9

次のコードを検討してください

module type Foo = sig 
  type t
  val do_with_t : t -> unit
end

let any_foo t (module F : Foo) = F.do_with_t t

次の素敵なタイプのエラーで拒否されます:

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

ただし、次の型注釈を追加すると受け入れられます。

let any_foo (type s) (t:s) (module F : Foo with type t = s) = F.do_with_t t

共有制約は私には理にかなっているので、私の質問は、OCamltが関数内での私の使用法から型シグネチャを推測できない理由です。

4

2 に答える 2

7

私は専門家ではありませんが、これが私の見解です。

本当のエラーは、メッセージの最後のビットです。

型コンストラクタ Ft はそのスコープをエスケープします

エラー メッセージを理解するために、最初any_fooに引数をパターン マッチングせずに書き直し、引数の名前を変更して説明を理解しやすくします。

let any_foo arg foo = 
  let (module F : Foo) = foo in
    F.do_with_t arg

ここではファースト クラス モジュールを使用しており、その let ステートメントのスコープ内で変数foo新しいmoduleFにアンパックしています。

argこの事実から推測できる引数の型を考えてみましょう。明らかに型は ですF.tが、重要なことに、これは現在のスコープでのみ認識されている型です。これは、 が現在のスコープでのみ認識されているためmodule Fです。

any_foo結果の関数の型を定義してみましょう。

val any_foo : F.t -> (module Foo) -> unit

そして、あなたの問題がありますF.t.関数スコープの奥深くから新しく作成された型を公開しようとしています. つまり、呼び出し元が関数内にのみ存在する型を知っていることを期待しています。F.tまたは、別の言い方をすれば、型がそのスコープをより広い対象者に「エスケープ」することを期待しています。

解決策、説明

問題がわかったので、この型が「外側」のスコープに存在し、引数argがその型であることをコンパイラに説明する必要があることを認識することができます。

言い換えれば、F引数の型が新しいモジュール内argの型と等しいことを示すために、新しく作成されたモジュールに制約を追加する必要があります。そのために、ローカル抽象型を使用できます。tF

同じ関数を続けて、ローカルに抽象型を追加し、それでaモジュールを制約できますF

let (type a) any_foo arg foo = 
  let (module F : Foo with type t = a) = foo in
    F.do_with_t arg

今の型を考えてみましょうany_foo

val any_foo : 'a -> (module Foo with type t = 'a) -> unit

問題ありません。

完全を期すために、パターン マッチングのバージョンに戻りましょう。

let (type a) any_foo arg (module F : Foo with type t = a) =
  F.do_with_t arg
于 2014-02-07T21:05:14.427 に答える
6

これは実際にはあなたの質問に答えるものではありませんが、あなたの場合、新しい型変数を導入するだけで済みますs:

let any_foo (type s) t (module F : Foo with type t = s) = F.do_with_t t

(t:s)つまり、ここでは型推論がうまく機能するので必要ありません。

于 2013-04-15T08:05:03.547 に答える