如何将值提升为 monad

时间:2021-06-20 22:05:23

标签: ocaml writer-monad

我正在尝试在 OCaml 中使用 Writer monad。

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

以下语句有效。


Writer.(>>=) (Writer.return 2) (fun x -> Writer.return 1);;

但是下面的语句没有。

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

我试过了

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

我在这里做错了什么?如何将值提升到 OCaml 中的 monad 中?

3 个答案:

答案 0 :(得分:3)

如前所述,代码适用于:

Writer.(>>=) (Writer.return 2) (fun x -> Writer.return 1);;

这是因为描述 monad 的签名,此处为 Monad,可能具有以下形式:

module type Monad = sig
  type 'a t 
  val return : 'a -> 'a t 
  val ( >>= ) : 'a t -> ('a -> 'b t) -> 'b t
end

由于 'a t 的具体实现尚不清楚(类型模块可能服务于多个实现),'a t 是“抽象的”。

因此,一种方法是“去抽象化”它。

这可以通过在 Writer 模块的签名中反映出我们想要实现 Monad 模块而不是使类型 'a t 抽象,如下所示:

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

这样,类型“a t”将不再是抽象的。

作为旁注,我个人认为 Writer(或 Reader 或 State)类型是抽象的并不是特别糟糕。在我看来,这是一对夫妇的事实是一个实现细节,一些不需要泄漏的管道。

答案 1 :(得分:3)

一个更基本的问题是签名约束

module Any_monad : Monad

在 OCaml 中几乎总是一个错误。

签名约束删除了签名之外的信息。因此,在约束之后,Any_monad 可以被满足 monad 签名的任何其他 monad 替换(如果我们暂时排除有副作用的 monad)。例如,您可以用这个非常有用的 monad 替换 Writer monad:

module Nil = struct
  type 'a t = unit
  let bind x f = ()
  let (>>=) = bind
  let return x = ()
end

换句话说,一个有用的 monad 需要具有不属于 monad 接口的函数。如果 Writer monad 是一个写函数:

module Writer: sig
  include Monad
  val write: string -> unit t
  val read: 'a t -> string
end = struct
  ...
end

请注意,我们在将签名用作约束之前对其进行了扩展。 一旦这些功能可用,您的原始代码就可以重写为:

Writer.(return 2 >>= fun x -> write "inc" >>= fun () -> return x)

答案 2 :(得分:2)

您将 monad 实现限制为 Monad,但实际上,您希望其类型具有 Writer 签名。换句话说,您正在升级您的实现并忘记它也必须实现特定于 Writer monad 的操作。显然,这会使你的 Writer monad 变得无用,因为你不能写任何东西。

为了修复它,我们首先需要定义 Writer 模块类型,一个通用的定义可能看起来像这样,

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

现在您需要实现新添加的操作,并将 state 类型设置为 string,然后您将能够在不破坏其抽象的情况下使用您的 Writer monad,

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

在 OCaml 中实现 monad 的库很少,因此您无需自己重新发明它。您可以尝试我们在 CMU 开发的 monads 库。它可以安装

opam install monads
相关问题