2013-02-26 69 views
12

比方说,我有一个选项列表:OCaml的:更高kinded多态性(抽象化了的模块?)

let opts = [Some 1; None; Some 4] 

我想这些转换成列表的选项,使得:

  • 如果列表包含None,结果为None
  • 否则,将收集各种整数。

这是比较简单的写这对于这种特殊情况下(使用CoreMonad模块):

let sequence foo = 
let open Option in 
let open Monad_infix in 
    List.fold ~init:(return []) ~f:(fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
    ) foo;; 

然而,作为问题标题所暗示的,我真的很想抽象的过度型构造函数而不是专门用于Option。 Core似乎使用一个仿函数来给出更高kinded类型的效果,但我不清楚我如何编写要在模块中抽象的函数。在Scala中,我会使用隐式上下文绑定来要求某些Monad[M[_]]的可用性。我期待没有办法隐式地传入模块,但我该如何明确地做到这一点?换句话说,我可以写的东西近似的:

let sequence (module M : Monad.S) foo = 
let open M in 
let open M.Monad_infix in 
    List.fold ~init:(return []) ~f:(fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
    ) foo;; 

这是不是可以用第一类模块来完成?

编辑:好吧,所以它实际上并没有发生,我尝试使用特定的代码,它似乎比我预期的更接近工作!似乎语法事实上是有效的,但我得到这样的结果:

Error: This expression has type 'a M.t but an expression was expected of type 'a M.t 
The type constructor M.t would escape its scope  

错误的第一部分还是很迷惑,因为它们匹配,所以我猜这个问题是第二个 - 是这里的问题返回类型似乎没有确定?我想它是依赖于传入的模块 - 这是一个问题吗?有没有办法解决这个实现?

+0

这个老问题可能对你有用:http://stackoverflow.com/questions/1986374/higher-order-type-constructors-and-functors-in-ocaml – rgrinberg 2013-02-26 15:10:48

回答

18

首先,这里是你的代码的一个独立的版本(使用传统的标准库的 List.fold_left)对于没有在手 核心,仍然想尝试编译你的榜样的人。

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

let sequence (module M : MonadSig) foo = 
    let open M in 
    let (>>=) = bind in 
    List.fold_left (fun acc x -> 
    acc >>= fun acc' -> 
    x >>= fun x' -> 
    return (x' :: acc') 
) (return []) foo;; 

,你得到的错误信息手段(混乱的第一线可 被忽略),该MT定义是本地M模块,并 不能逃脱它的范围,它会与你做什么'试着 写。

这是因为使用的是一流的模块,允许 抽象的模块,但不要有依赖前瞻性类型,如 返回类型取决于参数的模块的值,或至少 路径(这里是M)。

考虑这个例子:

module type Type = sig 
    type t 
end 

let identity (module T : Type) (x : T.t) = x 

这是不对的。在(x : T.t)错误信息点,并说:

Error: This pattern matches values of type T.t 
     but a pattern was expected which matches values of type T.t 
     The type constructor T.t would escape its scope 

什么你可以做的是在你之前所期望的类型上一流的模块牛逼抽象抽象的,所以不存在逃跑了。

let identity (type a) (module T : Type with type t = a) (x : a) = x 

这依赖于能够显式抽象类型变量a。不幸的是,这个特性还没有扩展到对更高主因变量的抽象。您目前不能写:

let sequence (type 'a m) (module M : MonadSig with 'a t = 'a m) (foo : 'a m list) = 
    ... 

的解决方案是使用一个函子:而不是在价值层面的工作,你在模块级,它具有更丰富的一种语言工作。

module MonadOps (M : MonadSig) = struct 
    open M 
    let (>>=) = bind 

    let sequence foo = 
    List.fold_left (fun acc x -> 
     acc >>= fun acc' -> 
     x >>= fun x' -> 
     return (x' :: acc') 
    ) (return []) foo;; 
end 

代替具有各一元操作(sequencemap等)抽象在单子的,你做一个模块宽的抽象。

+0

是的,我已经到了假设它是引起问题的依赖(-ish)类型!感谢您提供非常详细的答案。 – Impredicative 2013-02-26 16:16:46