在 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.
一种解决方法是对 g 的定义进行 η 扩展:
let g x = f x
如果我的代码是纯功能性的(没有例外,没有副作用等),这应该是等效的(实际上,多态性可能会更好,这取决于语言如何概括类型:在 OCaml 中,部分应用程序不会产生多态性函数,但它们的 η-展开确实)。
系统的 η 展开有什么缺点吗?
两个答案避开了关于 η-expansion 的问题 :-) 而是建议我在我的函数类型周围添加括号。这是因为,显然,F# 在类型级别区分函数的“真实”定义(作为 λ 表达式和计算定义,如在部分应用程序中);这大概是因为 λ 表达式直接映射到 CLR 函数,而计算定义映射到委托对象。(我不确定这种解释,如果非常熟悉 F# 的人可以指出描述这一点的参考文档,我将不胜感激。)
一个解决方案是系统地为 中的所有函数类型添加括号.mli
,但我担心这会导致效率低下。另一种方法是检测计算函数并在.mli
. 第三种解决方案是将 η-展开明显的情况,并将其他情况用括号括起来。
我对 F#/CLR 内部结构不够熟悉,无法衡量哪些会导致显着的性能或接口损失。