signature MAPPABLE = sig
type 'a mappable
val fmap : ('a -> 'b) -> 'a mappable -> 'b mappable
end
structure Option : MAPPABLE = struct
type 'a mappable = 'a option
fun fmap f v =
case v of
(SOME x) => SOME (f x)
| NONE => NONE;
end
structure List : MAPPABLE = struct
type 'a mappable = 'a list
fun fmap f v = map f v
end
fun incByFive x = x + 5
Really just to have a function that does stuff with fmap
fun mapToPair f x =
let val b = List.fmap f x
in (b,b)
end
val lst = mapToPair incByFive [1,2,3];
Suppose you want to make a generic implementation, that works for
all instances of MAPPABLE. The following does not work
fun mapToPair f x =
let val b = MAPPABLE.fmap f x
in (b,b)
end
It seems, that SML people point to Functors, if that needs to be done.
I tried implementing one, for a generic implementation of mapToPair
functor FMAPFUNCTOR (structure Q : MAPPABLE)
= struct
fun mapToPair f x =
let val b = Q.fmap f x
in (b,b)
end
end;
However, to use it with what in Haskell I'd call a functor
instance, I need to instantiate the functor (this reminds me of C++
templates for some reason)
structure MAPPABLE_TO_PAIR = FMAPFUNCTOR (structure Q = List);
val lstByPoly = MAPPABLE_TO_PAIR.mapToPair incByFive [1,2,3]
I would have to repeat that instantiation for every MAPPABLE
instance I want to use. I guess Haskell performs something like this,
too. Just implicitly.
Now I wonder if there is any shortcut / sugar for a better "user
experience" in SML that I have missed. Because really, it seems kind
of a lot of boilerplate in the way in order to use this in a code
base.
I guess Haskell performs something like this, too. Just implicitly.
The Haskell standard library defines and imports a ton of type class instances. Given a sufficient set of ML functors, their applications and their implicit compile-time imports, you could achieve something quite convenient.
But Haskell does let you automate type class instance declarations in ways that SML doesn't.
For example, instance Foo t => Bar t where ... is comparable to SML's higher-order functors, but in SML you explicitly have to generate a module that corresponds to Bar t for each concrete Foo t. Haskell also lets you derive instances syntactically.
OCaml had modular implicits from 2014 (example), but they mainly give you syntax sugar to refer to defined functor instances, rather than generate them.
I suspect that the reason ML module systems are still more explicit than Haskell's is because of things like overlapping instances.
Andreas Rossberg contributed 1ML in 2014 in which modules are first-class citizens. That means a function could take a module as an argument, e.g. like this:
;; Higher-kinded polymorphism
type MONAD (m : type => type) =
{
return 'a : a -> m a;
bind 'a 'b : m a -> (a -> m b) -> m b;
};
map 'a 'b (m : type => type) (M : MONAD m) (f : a -> b) mx =
M.bind mx (fun x => M.return (f x));
do map :
'a => 'b => (m : type => type) => (M : MONAD m) => (a -> b) -> m a -> m b;
This is still research-y in the sense that the compiler has "TOY" in its name, but it'd be an example of an ML (although not Standard ML) that does something comparably generic with modules.
Related
I noticed that I cannot do the following in OCaml:
# let foo (f : 'a -> unit) = f 1; f "s";;
Error: This expression has type string but an expression was expected of type
int
In Haskell, this could be resolved by universally quantifying the input function f using Rank2Types:
{-# LANGUAGE Rank2Types #-}
foo :: (forall a. a -> ()) -> ()
foo f = let a = f 1 in f "2"
How can I get similar exposure to this in OCaml?
OCaml only supports semi-explicit higher-rank polymorphism: polymorphic functions arguments must be boxed either inside a record with polymorphic field:
type id = { id: 'a. 'a -> 'a }
let id = { id=(fun x -> x) }
let f {id} = id 1, id "one"
or inside an object
let id' = object method id: 'a. 'a -> 'a = fun x -> x end
let f (o: <id:'a. 'a -> 'a>) = o#id 1, o#id "one"
Beyond the syntactic heaviness, this explicit boxing of polymorphic functions has the advantage that it works well with type inference while still requiring only annotations at the definition of record types or the methods.
I'm trying to use the Writer monad in OCaml.
module Writer : Monad = struct
type 'a t = 'a * string
let return x = (x, "")
let (>>=) m f =
let (x, s1) = m in
let (y, s2) = f x in
(y, s1 ^ s2)
end
The statement below works.
Writer.(>>=) (Writer.return 2) (fun x -> Writer.return 1);;
But the statement below does not.
Writer.(>>=) (Writer.return 2) (fun x -> (x, "inc"));;
Error: This expression has type 'a * 'b but an expression was expected of type
'c Writer.t
I tried
Writer.(>>=) (Writer.return 2) (fun x -> ((x, "inc") : int Writer.t))
Error: This expression has type 'a * 'b but an expression was expected of type
int Writer.t
What am I doing wrong here? How can I lift a value into a monad in OCaml?
As mentioned, the code works with:
Writer.(>>=) (Writer.return 2) (fun x -> Writer.return 1);;
This is because the signature that describes the monad, here Monad, probably has this form:
module type Monad = sig
type 'a t
val return : 'a -> 'a t
val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
end
As the concrete implementation of 'a t is not yet known (the type module potentially serving several implementations), 'a t is "abstract".
One approach would therefore be to "de-abstract" it.
This is possible by reflecting in the signature of the Writer module that we want to implement the Monad module while not making the type 'a t abstract, in this way:
module Writer : Monad with type 'a t = 'a * string = struct
type 'a t = 'a * string
let return x = (x, "")
let (>>=) m f =
let (x, s1) = m in
let (y, s2) = f x in
(y, s1 ^ s2)
end
In this way, the type `'a t' will no longer be abstract.
As a side note, I personally think it's not particularly bad that the Writer (or Reader, or State) type is abstract. The fact that it's a couple seems to me to be an implementation detail, some piping that doesn't need to be leaked.
A more fundamental issue is that the signature constraint
module Any_monad : Monad
is almost always a mistake in OCaml.
Signature constraints remove information that stick out of the signature. Thus, after the constraint, Any_monad could be replaced by any other monads fulfilling the monad signature (if we temporarily exclude side-effectful monads). For instance, you could replace the Writer monad with this very useful monad:
module Nil = struct
type 'a t = unit
let bind x f = ()
let (>>=) = bind
let return x = ()
end
In other words, an useful monad needs to have functions that are not part of the monad interface. In the case of the Writer monad that would be a write function:
module Writer: sig
include Monad
val write: string -> unit t
val read: 'a t -> string
end = struct
...
end
Notice that we have extended the signature before using it as a constraint.
Once, those functions available, your original code can be rewritten as:
Writer.(return 2 >>= fun x -> write "inc" >>= fun () -> return x)
You're constraining your monad implementation to be a Monad but, in fact, you want its type to have the Writer signature. To put it in other words, you're upcasting your implementation and forgetting that it also have to implement operations specific to the Writer monad. Obviously, this renders your Writer monad useless as you can't write anything.
To get it fixed, we first need to define the Writer module type, a common definition might look something like this,
module type Writer = sig
type state
val write : state -> unit t
val read : 'a t -> state t
val listen : 'a t -> ('a * state) t
val exec : unit t -> state
include Monad with type 'a t := 'a t
end
Now you need to implement the newly added operations with the state type set to string and then you will be able to use your Writer monad without breaking its abstraction,
module Writer : Writer with type state = string = struct
type state = string
type 'a t = 'a * string
let return x = (x, "")
let (>>=) m f =
let (x, s1) = m in
let (y, s2) = f x in
(y, s1 ^ s2)
let write x = (),x
let read (_,x) = x
let listen (x,s) = ((x,s),s)
let exec (_,x) = x
end
There are few libraries that implement monads in OCaml, so you don't need to re-invent it by your own. You can try the monads library that we develop at CMU. It could be installed with
opam install monads
I'm learning SML and trying to make a datatype, called mySet, that can be any list of ints or reals, but with no duplicates and in sequential order. So far, I've made the datatype and some functions that do what I need to a list and then return it within that datatype which work fine. But I realized that the constructor for the datatype could also be used instead which completely bypasses the requirements. For what I need, I can just use the function, but I'd really like to know if there's any way I can patch up that problem? If a list doesn't follow the requirements, most of my functions for the datatype wouldn't work right.
datatype 'a set = Set of 'a list | Empty;
(* takes (item, list) and removes any copies of item from list *)
fun cleanList(a, []) = []
|cleanList(a, b::rest) =
if b = a then cleanList(a, rest)
else
b::cleanList(a, rest);
(*uses cleanList to make a list with all the items, no copies*)
fun removeDup([]) = []
| removeDup(a::rest) =
let
val cleanRest = cleanList(a, rest);
in
a::removeDup(cleanRest)
end;
(*uses above 2 functions, then puts the list in order *)
fun makeSet([]) = Empty
|makeSet(inputList) =
let
val cleanList = removeDup(inputList)
val sortedList = ListMergeSort.sort (fn(x,y) => x > y) cleanList;
in
Set(sortedList)
end;
val testList = [27, 81, 27, 3, 4, 5, 4, 27, 81, 3, 3, 7];
makeSet(testList); (* returns Set [3,4,5,7,27,81] *)
Set([1,1,1,1,1,1]); (*Set [1,1,1,1,1,1] which I don't want to allow *)
I realized that the constructor for the datatype could also be used instead which completely bypasses the requirements. For what I need, I can just use the function, but I'd really like to know if there's any way I can patch up that problem?
There is! Your basic constructor will break your data type's invariants, so you want to hide it and only expose a smart constructor that fails deliberately on certain input and doesn't allow invalid states.
As molbdnilo says, this is called an abstract type because you hide the way it is implemented and expose it through its smart constructor interface, which has whatever behavior you want it to. You can also call it an opaque type.
What each method of achieving this have in common is that you have a local scope in which the datatype is declared, but where only the external interface of the smart constructor leaves. In the interest of exploring how few language features you need, I tried to simply write:
val (fmap, pure) =
let
datatype 'a maybe = Just of 'a | Nothing
fun fmap f Nothing = Nothing
| fmap f (Just x) = Just (f x)
fun pure x = Just x
in (fmap, pure)
end
But my SML compiler actually rejected this program:
! in (fmap, pure)
! ^^^^
! Type clash: expression of type
! ('a -> 'b) -> 'a maybe -> 'b maybe
! cannot have type
! 'c
! because of a scope violation:
! the type constructor maybe is a parameter
! that is declared within the scope of 'c
So we need to whip out one of SML's language features designed specifically for this:
Update: #ruakh pointed out that I had forgotten about local.
Here is an example of the same thing using local-in-end:
local
datatype 'a maybe = Just of 'a | Nothing
in
fun fmap f Nothing = Nothing
| fmap f (Just x) = Just (f x)
fun pure x = Just x
end
The data type is shared between the two functions fmap and pure, but the definition is hidden from the external interface. You can have multiple local definitions including helper functions.
And the constructors are hidden:
> New type names: =maybe
val ('a, 'b) fmap = fn : ('a -> 'b) -> 'a maybe -> 'b maybe
val 'a pure = fn : 'a -> 'a maybe
let and local are discussed further in Difference between "local" and "let" in SML
Here is an example of the same thing using abstype:
abstype 'a maybe = Just of 'a | Nothing
with
fun fmap f Nothing = Nothing
| fmap f (Just x) = Just (f x)
fun pure x = Just x
end
And you can see how Just and Nothing are hidden:
> New type names: maybe
type 'a maybe = 'a maybe
val ('a, 'b) fmap = fn : ('a -> 'b) -> 'a maybe -> 'b maybe
val 'a pure = fn : 'a -> 'a maybe
But you can also use an opaque module. This StackOverflow answer covers precisely how the skeleton for a set functor works. Chapter 7 of ML for the Working Programmer covers how to define a module that takes a module as argument (a functor). This is an alternative to parametric polymorphism.
I am trying to understand difference between these two;
val my_fun = length o List.filter (fn (item) => item = #"a") o String.explode
This one can be invoked (my_fun "name" will return 1) and works fine. I am trying to understand why following is not working
length o (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
Definition of function composition in sml
f o g = f(g(x))
In the second form what we do is ( I think)
length ([#"a"])
You seem to be confusing function composition with function application.
Composition is a higher-order function which takes two functions, f and g, of compatible types and returns another function -- the function which is computed by first applying the g to a value and then applying f to the result. o is a built-in operator, but if you wanted to define composition yourself it would be something like
fun compose (f,g) x = f(g(x))
this has type fn : ('a -> 'b) * ('c -> 'a) -> 'c -> 'b (which is exactly the type you get when you type (op o); in the REPL). Note that the return value of compose is 'c -> 'b, which is a function type.
length o List.filter (fn (item) => item = #"a") o String.explode
makes perfect sense since the types are compatible and composition is right-associative.
On the other hand, as you already note,
length o (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
would be equivalent to
length o [#"a"]
That really doesn't make sense. What would it even mean to compose a function with a list. A list isn't a function. It does make sense to apply length to that list, which is what you seem to expect.
Application is simply juxtaposition, so all you would need to do is write
length (List.filter (fn (item) => item = #"a" ) (String.explode "name"))
Which reduces to length [#"a"] and from thence to 1.
If you wanted to write your own apply function you would write:
def apply f x = f x
This might strike you as superficially similar to compose but its type is radically different: fn : ('a -> 'b) -> 'a -> 'b. Composition involves application, but it isn't the same thing.
Let's say I have a list of options:
let opts = [Some 1; None; Some 4]
I'd like to convert these into an option of list, such that:
If the list contains None, the result is None
Otherwise, the various ints are collected.
It's relatively straightforward to write this for this specific case (using Core and the Monad module):
let sequence foo =
let open Option in
let open Monad_infix in
List.fold ~init:(return []) ~f:(fun acc x ->
acc >>= fun acc' ->
x >>= fun x' ->
return (x' :: acc')
) foo;;
However, as the question title suggests, I'd really like to abstract over the type constructor rather than specialising to Option. Core seems to use a functor to give the effect of a higher kinded type, but I'm not clear how I can write the function to be abstracted over the module. In Scala, I'd use an implicit context bound to require the availability of some Monad[M[_]]. I'm expecting that there's no way of implicitly passing in the module, but how would I do it explicitly? In other words, can I write something approximating this:
let sequence (module M : Monad.S) foo =
let open M in
let open M.Monad_infix in
List.fold ~init:(return []) ~f:(fun acc x ->
acc >>= fun acc' ->
x >>= fun x' ->
return (x' :: acc')
) foo;;
Is this something that can be done with first class modules?
Edit: Okay, so it didn't actually occur to me to try using that specific code, and it appears it's closer to working than I'd anticipated! Seems the syntax is in fact valid, but I get this result:
Error: This expression has type 'a M.t but an expression was expected of type 'a M.t
The type constructor M.t would escape its scope
The first part of the error seems confusing, since they match, so I'm guessing the problem is with the second - Is the problem here that the return type doesn't seem to be determined? I suppose it's dependent on the module which is passed in - is this a problem? Is there a way to fix this implementation?
First, here is a self-contained version of your code (using the legacy
List.fold_left of the standard library) for people that don't have
Core under hand and still want to try to compile your example.
module type MonadSig = sig
type 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
val return : 'a -> 'a t
end
let sequence (module M : MonadSig) foo =
let open M in
let (>>=) = bind in
List.fold_left (fun acc x ->
acc >>= fun acc' ->
x >>= fun x' ->
return (x' :: acc')
) (return []) foo;;
The error message that you get means (the confusing first line can
be ignored) that the M.t definition is local to the M module, and
must not escape its scope, which it would do with what you're trying
to write.
This is because you are using first-class modules, that allow to
abstract on modules, but not to have dependent-looking types such as
the return type depends on the argument's module value, or at least
path (here M).
Consider this example:
module type Type = sig
type t
end
let identity (module T : Type) (x : T.t) = x
This is wrong. The error messages points on (x : T.t) and says:
Error: This pattern matches values of type T.t
but a pattern was expected which matches values of type T.t
The type constructor T.t would escape its scope
What you can do is abstract on the desired type before you abstract on the first-class module T, so that there is no escape anymore.
let identity (type a) (module T : Type with type t = a) (x : a) = x
This relies on the ability to explicitly abstract over the type variable a. Unfortunately, this feature has not been extended to abstraction over higher-kinded variables. You currently cannot write:
let sequence (type 'a m) (module M : MonadSig with 'a t = 'a m) (foo : 'a m list) =
...
The solution is to use a functor: instead of working at value level, you work at the module level, which has a richer kind language.
module MonadOps (M : MonadSig) = struct
open M
let (>>=) = bind
let sequence foo =
List.fold_left (fun acc x ->
acc >>= fun acc' ->
x >>= fun x' ->
return (x' :: acc')
) (return []) foo;;
end
Instead of having each monadic operation (sequence, map, etc.) abstract over the monad, you do a module-wide abstraction.