2013-02-26 128 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