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.
Related
Here is a simple OCaml module type for a monad:
module type Monad = sig
type 'a t
val return : 'a -> 'a t
val bind : 'a t -> ('a -> 'b t) -> 'b t
end
I can instantiate this with any particular monad, such as the reader monad for some type r:
module Reader_monad : Monad = struct
type 'a t = r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
And I can parametrize it over the type r by using a functor:
module type Readable = sig type r end
module Reader (R : Readable) : Monad = struct
type 'a t = R.r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
However, the latter approach requires that I instantiate different instances of the functor for different types r. Is there any way to define a "parametrically polymorphic" module of type Monad that would give parametrically polymorphic functions like return : 'a -> ('r -> 'a)?
I think can get more or less what I want with a separate module type for "families of monads":
module type Monad_family = sig
type ('c, 'a) t
val return : 'a -> ('c, 'a) t
val bind : ('c, 'a) t -> ('a -> ('c, 'b) t) -> ('c, 'b) t
end
module Reader_family : Monad_family = struct
type ('c, 'a) t = 'c -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
But if I have a substantial library of general facts about monads, this would require modifying it everywhere manually to use families. And then some monads are parametrized by a pair of types (although I suppose that could be encoded by a product type), etc. So I would rather avoid having to do it this way.
If this isn't directly possible, is there at least a way to instantiate the module Reader locally inside a parametrically polymorphic function? I thought I might be able to do this with first-class modules, but my naive attempt
let module M = Reader(val (module struct type r = int end) : Readable) in M.return "hello";;
produces the error message
Error: This expression has type string M.t
but an expression was expected of type 'a
The type constructor M.t would escape its scope
which I don't understand. Isn't the type M.t equal to int -> string?
I think this is the same issue as The type constructor "..." would escape its scope when using first class modules, where the module M doesn't live long enough. If you instead wrote
# module M = Reader(struct type r = int end);;
# M.return "hello";;
- : string M.t = <fun>
then this would work fine.
Separately, the Reader functor loses some type equalities that you might want. You can restore them by defining it as such:
module Reader (R : Readable) : Monad with type 'a t = R.r -> 'a = struct
type 'a t = R.r -> 'a
let return a = fun _ -> a
let bind o f = fun x -> f (o x) x
end
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 meet an error about subtyping.
For this code, List.map (fun ((String goal_feat):> Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list).
I meet the following error in vscode:
This expression cannot be coerced to type
Yojson.Basic.t =
[ Assoc of (string * Yojson.Basic.t) list
| Bool of bool
| Float of float
| Int of int
| List of Yojson.Basic.t list
| Null
| String of string ];
it has type [< String of 'a ] -> 'b but is here used with type
[< Yojson.Basic.t ].
While compiling, I meet the following error.
Error: Syntax error: ')' expected.
If I change the code to List.map (fun ((String goal_feat): Basic.t) -> goal_feat) (goal_feats_json:> Basic.t list), which useq explicit type cast instead of subtyping, then the error disappeared. I can not understand what is the problem with my code when i use subtyping. Much appreciation to anyone who could give me some help.
First of all, most likely the answer that you're looking for is
let to_strings xs =
List.map (function `String x -> x | _ -> assert false) (xs :> t list)
The compiler is telling you that your function is handling only one case and you're passing it a list that may contain many other things, so there is a possibility for runtime error. So it is better to indicate to the compiler that you know that only the variants tagged with String are expected. This is what we did in the example above. Now our function has type [> Yojson.Basic.t].
Now back to your direct question. The syntax for coercion is (expr : typeexpr), however in the fun ((String goal_feat):> Basic.t) -> goal_feat snippet, String goal_feat is a pattern, and you cannot coerce a pattern, so we shall use parenthesized pattern here it to give it the right, more general, type1, e.g.,
let exp xs =
List.map (fun (`String x : t) -> x ) (xs :> t list)
This will tell the compiler that the parameter of your function shall belong to a wider type and immediately turn the error into warning 8,
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a case that is not matched:
(`Bool _|`Null|`Assoc _|`List _|`Float _|`Int _)
which says what I was saying in the first part of the post. It is usually a bad idea to leave warning 8 unattended, so I would suggest you to use the first solution, or, otherwise, find a way to prove to the compiler that your list doesn't have any other variants, e.g., you can use List.filter_map for that:
let collect_strings : t list -> [`String of string] list = fun xs ->
List.filter_map (function
| `String s -> Some (`String s)
| _ -> None) xs
And a more natural solution would be to return untagged strings (unless you really need the to be tagged, e.g., when you need to pass this list to a function that is polymorphic over [> t] (Besides, I am using t for Yojson.Basic.t to make the post shorter, but you should use the right name in your code). So here is the solution that will extract strings and make everyone happy (it will throw away values with other tags),
let collect_strings : t list -> string list = fun xs ->
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
Note, that there is no need for type annotations here, and we can easily remove them to get the most general polymoprhic type:
let collect_strings xs =
List.filter_map (function
| `String s -> Some s
| _ -> None) xs
It will get the type
[> `String a] list -> 'a list
which means, a list of polymorphic variants with any tags, returning a list of objects that were tagged with the String tag.
1)It is not a limitation that coercion doesn't work on patterns, moreover it wouldn't make any sense to coerce a pattern. The coercion takes an expression with an existing type and upcasts (weakens) it to a supertype. A function parameter is not an expression, so there is nothing here to coerce. You can just annotate it with the type, e.g., fun (x : #t) -> x will say that our function expects values of type [< t] which is less general than the unannotated type 'a. To summarize, coercion is needed when you have a function that accepts an value that have a object or polymorphic variant type, and in you would like at some expressions to use it with a weakened (upcasted type) for example
type a = [`A]
type b = [`B]
type t = [a | b]
let f : t -> unit = fun _ -> ()
let example : a -> unit = fun x -> f (x :> t)
Here we have type t with two subtypes a and b. Our function f is accepting the base type t, but example is specific to a. In order to be able to use f on an object of type a we need an explicit type coercion to weaken (we lose the type information here) its type to t. Notice that, we do not change the type of x per se, so the following example still type checks:
let rec example : a -> unit = fun x -> f (x :> t); example x
I.e., we weakened the type of the argument to f but the variable x is still having the stronger type a, so we can still use it as a value of type a.
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.
I have anonymous function:
fun x -> x;;
- : 'a -> 'a = <fun>
As you may see, this function accepts argument of any type. I want to specify concrete type, say int.
I know that I can annotate functions with type specs, but do not know syntax for it.
It would be helpful to get some reference to this syntax and extend this example with such annotation.
Thanks.
# fun (x: int) -> x;;
- : int -> int = <fun>
#
The reason this works is that
Function parameters are specified as patterns.
One alternative for a patttern is of the form:
( pattern : typexpr )
Syntax for patterns is given in Section 6.6 of the OCaml manual.
The most general form is:
(fun x -> x : int -> int)
Since fun x -> x is a value by itself, it can be annotated with a type, as any other expression. Indeed, in this type annotation you can omit one of the int's, since the other can be inferred by a compiler:
(fun x -> x : int -> 'a)
or
(fun x -> x : 'a -> int)
all will result in:
- : int -> int = <fun>
This also demonstrates that 'a in type annotations has different meaning from 'a in signatures. In type annotation it stands for "I don't care, you decide". Thats why the proper name for type annotations is type constraining, thus you're not annotating your expression with type, but you're giving extra constraint for type inference system. In this example, you're saying to it: I have this expression, and please infer its type, giving it is a function that returns int.
Also, you can use _ instead of type variables, the same way as you can do this for a normal variables:
(fun x -> x : _ -> int)
The result will be the same.