13

OCaml では、 in を持つことは合法です.mli:

val f : 'a -> 'a
val g : 'a -> 'a

.ml:

let f x = x
let g = f

しかし、F# では、これは拒否されます。

eta_expand.ml(2,5): error FS0034: Module 'Eta_expand' contains
    val g : ('a -> 'a)    
but its signature specifies
    val g : 'a -> 'a    

The arities in the signature and implementation differ. The signature specifies that 'g' is function definition or lambda expression accepting at least 1 argument(s), but the implementation is a computed function value. To declare that a computed function value is a permitted implementation simply parenthesize its type in the signature, e.g.
    val g: int -> (int -> int)
instead of
    val g: int -> int -> int.

1 つの回避策は、g の定義を η 展開することです。

let g x = f x

私のコードが純粋に機能的である場合 (例外や副作用がないなど)、これは同等であるはずです (実際、言語がどのように型を一般化するかによっては、ポリモーフィズムに関してはさらに良いかもしれません: OCaml では、部分的なアプリケーションはポリモーフィックを生成しません関数ですが、それらの η-展開はそうです)。

体系的な η 展開に欠点はありますか?

2つの答えは、η-expansionに関する質問を回避します:-)代わりに、機能型の周りに括弧を追加することを提案します。これは明らかに、F# が型付けレベルで関数の「真の」定義 (λ 式として) と計算された定義 (部分適用の場合) を区別するためです。おそらくこれは、λ 式が CLR 関数に直接マップされ、計算された定義がデリゲート オブジェクトにマップされるためです。(私はこの解釈がよくわからないので、F# に精通している誰かがこれについて説明している参照ドキュメントを指摘してくれるとありがたいです。)

解決策は、 のすべての関数型に体系的に括弧を追加することです.mliが、これは非効率につながるのではないかと心配しています。もう 1 つは、計算された関数を検出し、対応する型に括弧を追加すること.mliです。3 番目の解決策は、明らかなケースを η 展開し、その他を括弧で囲むことです。

私は、F# / CLR の内部構造に十分に精通しておらず、どれが重大なパフォーマンスやインターフェイスのペナルティをもたらすかを測定できません。

4

2 に答える 2

10

理論的には、F# 関数の型'a -> 'b -> 'cは と同じ型'a -> ('b -> 'c)です。つまり、複数の引数関数は、F# ではカリー化された形式を使用して表されます。高階関数を呼び出す場合など、ほとんどの場合、もう一方が期待される場合に一方を使用できます。

ただし、実用的な理由から、F# コンパイラは実際には型を区別します。その動機は、コンパイルされた .NET コードで型が異なって表現されることです。これはパフォーマンスに影響を与えるだけでなく、C# との相互運用性にも影響を与えるため、区別することは有用です。

関数Foo : int -> int -> intはメンバーとしてコンパイルされますint Foo(int, int)。コンパイラはデフォルトではカリー化された形式を使用しません。これはFoo、両方の引数を指定して呼び出した場合 (より一般的なケース) に効率的であり、相互運用性に優れているためです。関数Bar : int -> (int -> int)は - 実際にはカリー化された形式を使用してコンパイルされますFSharpFunc<int, int> Bar(int)(そのため、単一のパラメーターで呼び出す方が効率的であり、C# から使用するのは困難です)。

これが、F# がシグネチャに関して型を同等に扱わない理由でもあります。シグネチャは型を指定しますが、ここでは関数のコンパイル方法も指定します。実装ファイルは正しいタイプの関数を提供する必要がありますが、この場合は正しいコンパイル形式も提供する必要があります。

于 2013-04-05T11:19:18.997 に答える
4

興味深いことに、fsiより役立つエラー メッセージが表示されます。

/test.fs(2,5): error FS0034: Module 'Test' contains
    val g : ('a -> 'a) but its signature specifies
    val g : 'a -> 'a The arities in the signature and implementation differ. 
          The signature specifies that 'g' is function definition or lambda expression 
          accepting at least 1 argument(s), but the implementation is a computed 
          function value. To declare that a computed function value is a permitted 
          implementation simply parenthesize its type in the signature, e.g.
        val g: int -> (int -> int) instead of
        val g: int -> int -> int.

ブラケットを追加してg :('a -> 'a)すべてを取得すると問題ありません

于 2013-04-05T09:04:06.297 に答える