Reputation: 1319
I am creating monads in OCaml and need to compose them, so I created transformers. I implemented the regular monad in terms of the transformer with the Identity monad:
module type MONAD = sig
type 'a m
val (>>=) : 'a m -> ('a -> 'b m) -> 'b m
val return : 'a -> 'a m
end
module Identity : MONAD = struct
type 'a m = 'a
let (>>=) m f = f m
let return x = x
end
module OptionT (M : MONAD) : MONAD with type 'a m := ('a option) M.m = struct
type 'a m = ('a option) M.m
let (>>=) m f = M.(>>=) m (fun option ->
match option with
| Some x -> f x
| None -> M.return None)
let return x = M.return @@ Some x
end
module Option = OptionT(Identity)
However, I can't do this:
open Option
let _ = (Some 1) >>= (fun x -> Some (x + 1))
The errors are:
(Some 1)
This expression has type 'a option
but an expression was expected of type 'b option Identity.m
Some (x + 1)
This expression has type 'a option
but an expression was expected of type 'b option Identity.m
If I try to fix the error with module Identity : MONAD with type 'a m = 'a
I get an error at module Option = OptionT(Identity)
that states that
The type `m' is required but not provided
It seems that now, 'a
has replaced 'a m
in the signature.
Doing
module Option : MONAD with type 'a m := 'a option = struct
type 'a m = 'a option
let (>>=) m f =
match m with
| Some x -> f x
| None -> None
let return x = Some x
end
works just fine.
How do I tell the compiler that a module implements a signature so that a type declared in the signature is the same as another type, while still keeping the signature's original type declaration?
Upvotes: 1
Views: 110
Reputation: 18912
It seems that now, 'a has replaced 'a m in the signature.
This the effect of destructive substitution, when you write
module Identity : MONAD with type 'a m := 'a
you are asking the compiler to substitute all instance of 'a m
by 'a
.
Contrarily, standard with
constraint adds a type equality to the module type
module Identity : MONAD with type 'a m = 'a
Looking at your various examples, it seems that you have confused the two, and are using destructive substitution when you meant to add a type constraint:
module OptionT(X:Monad) : MONAD with type 'a m = 'a = …
(* or *) module Option : MONAD with type 'a m = 'a option = …
and not
module OptionT(X:Monad) : MONAD with type 'a m := 'a = …
(* nor *) module Option : MONAD with type 'a m := 'a option = …
Upvotes: 5
Reputation: 3028
This expression has type 'a option but an expression was expected of type 'b option Identity.m
And indeed, the compiler does not know anything about Identity
except that its signature is MONAD
. (: MONAD
) is not something that merely helps the compiler, it hides all information about Identity
except that its signature is MONAD
.
So, you can add a type equality for that
module Identity : MONAD with type 'a m = 'a = ...
and it works.
Upvotes: 0