15

我试图在 OCaml 中实现 state monad(作为练习)。我的实现如下所示:

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

module MonadBuilder = functor (M: MONAD_BUILDER) ->
struct
  let ( >>= ) = M.bind
  let return = M.return
end;;

module StateM = 
struct
  type 'a state = { state: 's . 's -> ('a * 's) }
  type 'a t = 'a state
  let return x = { state = fun s -> (x, s) }
  let bind m f = 
    let aux s = 
      let (x, s') = m.state s in
      (f x).state s'
    in { state = aux }
  let run m x = fst (m.state x)
end;;

我为记录字段选择了存在类型,因为我不喜欢使用仿函数并将状态类型包装在模块中的想法。getState上面的实现是有效的,但是我在实现and时遇到了问题setState。我试图像这样实现它们:

let getState = { state = fun s -> (s, s) }

let setState s = { state = fun _ -> ((), s) }

这不起作用,因为推断的字段类型,例如'a -> ('a * 'a)'a -> (unit * 'a),不如声明的类型通用's . 's -> ('a * 's)。我理解为什么会发生这种情况,但我想知道是否有另一种使用记录方法使其工作的方法?

谢谢。

干杯,亚历克斯

4

1 回答 1

13

's. 's -> ('a * 's)是一种通用类型。您将很难实现具有通用类型的状态...

在不使用模块的情况下,没有一种简洁的方式来封装存在类型(因为存在类型是抽象类型的用途)。

当然,您可以改为发布状态类型:

type ('a,'s) state = { state : 's -> ('a * 's) } 

甚至更短,

type ('a,'s) state = 's -> 'a * 's

使用附加参数,您的代码将运行。但是,您会遇到这样一个事实,即您的参数必须由 monad 处理。因此,您可以在构建 monad 时将其隐藏:

module Monad = MonadBuilder(struct
  include StateM
  type 'a t = ('a,myStateType) state
end)

或者更改您的 monad 设计以包含用于存在类型的附加类型参数:

module type MONAD_BUILDER =
sig
  type ('a,'param) t
  val return : 'a -> ('a,'param) t
  val bind : ('a,'param) t -> ('a -> ('b,'param) t) -> ('b,'param) t
end;;
于 2011-05-01T00:17:24.953 回答