0

我正在尝试在 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?

4

3 回答 3

3

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

module Any_monad : Monad

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

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

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

换句话说,一个有用的 monad 需要有不属于 monad 接口的函数。在Writermonad 的情况下,这将是一个 write 函数:

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)
于 2021-06-21T11:01:32.563 回答
3

如前所述,代码适用于:

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

这是因为描述 monad 的签名 hereMonad可能具有以下形式:

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)类型是抽象的并不是特别糟糕。在我看来,这是一对夫妇的事实是一个实现细节,一些不需要泄漏的管道。

于 2021-06-20T22:32:48.137 回答
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
于 2021-06-21T13:48:06.000 回答