13

オプションのリストがあるとしましょう:

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

これらを次のようにリストのオプションに変換したいと思います。

  • リストに が含まれている場合None、結果は次のようになります。None
  • それ以外の場合は、さまざまな int が収集されます。

この特定のケースでこれを書くのは比較的簡単です ( 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。コアはより高い種類の型の効果を与えるためにファンクターを使用しているようですが、モジュールで抽象化される関数をどのように記述できるかが明確ではありません。Scala では、some の可用性を要求するようにバインドされた暗黙のコンテキストを使用します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    

エラーの最初の部分は一致するため、混乱しているように見えるので、問題は2番目にあると推測しています-戻り値の型が決定されていないように見えるという問題はここにありますか? 渡されたモジュールに依存していると思います-これは問題ですか? この実装を修正する方法はありますか?

4

1 に答える 1

19

List.fold_leftまず、 Coreを手元に持っておらず、サンプルのコンパイルを試みたい人のために、(標準ライブラリのレガシーを使用して)コードの自己完結型バージョンを 示します。

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

解決策はファンクターを使用することです。値レベルで作業する代わりに、より豊富な種類の言語を持つモジュールレベルで作業します。

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

sequence各モナド操作( 、mapなど)をモナド上で抽象化する代わりに、モジュール全体の抽象化を実行します。

于 2013-02-26T16:05:18.757 に答える