η-以纯函数式语言扩展

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内部结构不够熟悉,无法衡量哪些会产生显着的性能或接口处罚.

Tom*_*cek 9

理论上,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#不治疗类型,等于当它涉及到的签名 - 签名指定的类型,但在这里还指定了要被编译要去功能.实现文件必须提供正确类型的函数,但是 - 在这种情况下 - 也是正确的编译形式.


Joh*_*mer 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.
Run Code Online (Sandbox Code Playgroud)

如果你添加括号来得到g :('a -> 'a)一切都很好