Dav*_*aux 13 f# ocaml functional-programming lambda-calculus
在OCaml中,拥有以下内容是合法的.mli:
val f : 'a -> 'a
val g : 'a -> 'a
Run Code Online (Sandbox Code Playgroud)
并且.ml:
let f x = x
let g = f
Run Code Online (Sandbox Code Playgroud)
然而在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.
Run Code Online (Sandbox Code Playgroud)
一个解决方法是η-扩展g的定义:
let g x = f x
Run Code Online (Sandbox Code Playgroud)
如果我的代码纯粹是功能性的(没有例外,没有副作用等),这应该是等价的(实际上,就多态性而言,它可能更好,这取决于语言如何概括类型:在OCaml中,部分应用程序不会产生多态功能,但他们的η扩展确实).
系统性η-扩展有任何缺点吗?
两个答案躲避关于η-扩展的问题:-)而是建议我在我的功能类型周围添加括号.这是因为,显然,F#在函数的"真实"定义之间区分打字级别(如λ表达式和计算定义,如在部分应用程序中); 大概这是因为λ表达式直接映射到CLR函数,而计算定义映射到委托对象.(我不确定这种解释,如果对F#非常熟悉的人可以指出描述这一点的参考文件,我将不胜感激.)
一个解决方案是系统地将括号添加到所有函数类型中.mli,但我担心这会导致效率低下.另一种方法是检测计算出的函数,并在其中添加括号大小的相应类型.mli.第三种解决方案是η-扩展明显的案例,并将其他案例加以括号.
我对F#/ CLR内部结构不够熟悉,无法衡量哪些会产生显着的性能或接口处罚.
理论上,F#函数'a -> 'b -> 'c类型与'a -> ('b -> 'c).也就是说,使用F#中的curried表示多个参数函数.在大多数情况下,例如在调用高阶函数时,您可以使用其中一个.
但是,出于实际原因,F#编译器实际上区分了类型 - 动机是它们在编译的.NET代码中表示不同.这会影响性能以及与C#的互操作性,因此进行区分非常有用.
函数Foo : int -> int -> int将被编译为成员int Foo(int, int)- 默认情况下编译器不使用curried形式,因为在Foo使用两个参数调用时更常见(更常见的情况)并且它对于interop更好.函数Bar : int -> (int -> int)将被编译为FSharpFunc<int, int> Bar(int)- 实际上使用curried形式(因此使用单个参数调用它更有效,并且很难从C#中使用它).
这也是为什么F#不治疗类型,等于当它涉及到的签名 - 签名指定的类型,但在这里还指定了要被编译要去功能.实现文件必须提供正确类型的函数,但是 - 在这种情况下 - 也是正确的编译形式.
有趣的是,我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.
Run Code Online (Sandbox Code Playgroud)
如果你添加括号来得到g :('a -> 'a)一切都很好
| 归档时间: |
|
| 查看次数: |
502 次 |
| 最近记录: |