我在OCaml中创建monad并需要编写它们,所以我创建了变换器。我使用Identity monad在变换器方面实现了常规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)
但是,我不能这样做:
open Option
let _ = (Some 1) >>= (fun x -> Some (x + 1))
错误是:
(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
如果我尝试使用module Identity : MONAD with type 'a m = 'a
修正错误,我会在module Option = OptionT(Identity)
收到错误消息
The type `m' is required but not provided
现在看来,'a
已将#em>替换为 'a m
。
否则
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
工作正常。
如何告诉编译器模块实现签名,以便签名中声明的类型与另一种类型相同,同时仍然保留签名的原始类型声明?
答案 0 :(得分:5)
现在看来,'a已取代'签名中的m。
当你写
时,这就是破坏性替换的效果module Identity : MONAD with type 'a m := 'a
您要求编译器将'a m
的所有实例替换为'a
。
相反,标准with
约束将类型相等添加到模块类型
module Identity : MONAD with type 'a m = 'a
看看你的各种例子,似乎你已经混淆了两者,并且在你想要添加类型约束时使用破坏性替换:
module OptionT(X:Monad) : MONAD with type 'a m = 'a = …
(* or *) module Option : MONAD with type 'a m = 'a option = …
而不是
module OptionT(X:Monad) : MONAD with type 'a m := 'a = …
(* nor *) module Option : MONAD with type 'a m := 'a option = …
答案 1 :(得分:0)
此表达式具有类型'一个选项,但表达式需要类型&b; b选项Identity.m
事实上,编译器对Identity
一无所知,只是它的签名是MONAD
。 (: MONAD
)不仅仅是帮助编译器的东西,它隐藏了有关Identity
的所有信息,除了它的签名是MONAD
。
因此,您可以为该
添加类型相等module Identity : MONAD with type 'a m = 'a = ...
它有效。