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 の内部構造に十分に精通しておらず、どれが重大なパフォーマンスやインターフェイスのペナルティをもたらすかを測定できません。