2013-02-26 10 views
12

はのは、私はオプションのリストを持っているとしましょう:のOCaml:高いkinded多型(?モジュールの上に抽象化)

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

私は、リストのオプションの中にこれらを変換したい、そのようなこと:

  • リストにNoneが含まれている場合、結果はNone
  • です。それ以外の場合は、さまざまなintが収集されます。私は本当に種類以上の抽象化したいのですが、質問のタイトルが示すとおり、

    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;; 
    

    しかし:

それは(Coreを使用してMonadモジュール)は、この特定のケースのためにこれを書くことは比較的簡単ですコンストラクタではなく、Optionに特化しています。コアは、より親切な型の効果を与えるためにファンクタを使用しているようですが、モジュール上で抽象化される関数をどのように書くことができるかはわかりません。 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

答えて

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 

でき脱出はもう存在しないように、ファーストクラスのモジュールTの抽象あなたの前に希望のタイプの抽象あるん。

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) = 
    ... 

ソリューションはファンクタを使用することです:代わりに値レベルでの作業のため、あなたは豊かな種類の言語を持つモジュールレベルで動作します。代わりに、モナドの上に各単項演算(sequencemapなど)抽象を持つの

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 

、あなたはモジュール全体の抽象化を行います。

+0

ええ、私はそれが問題を引き起こしていた依存型(-ish)型であると仮定して!非常に詳細な答えをありがとう。 – Impredicative