f# - 纯函数式语言中的 η 展开

标签 f# functional-programming ocaml lambda-calculus

在 OCaml 中,.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 函数,而计算定义映射到委托(delegate)对象。 (我不确定这种解释,如果非常熟悉 F# 的人可以指出描述这一点的引用文档,我将不胜感激。)

一个解决方案是系统地为 .mli 中的所有函数类型添加括号。 ,但我担心这会导致效率低下。另一种方法是检测计算函数并在 .mli 中添加相应类型的括号。 .第三种解决方案是将 η-展开明显的情况,并将其他情况用括号括起来。

我对 F#/CLR 内部结构不够熟悉,无法衡量哪些会导致显着的性能或接口(interface)损失。

最佳答案

理论上,F# 函数类型 'a -> 'b -> 'c'a -> ('b -> 'c) 的类型相同.也就是说,多个参数函数在 F# 中使用柯里化(Currying)形式表示。您可以在大多数情况下使用其中一个,例如调用高阶函数时。

但是,出于实际原因,F# 编译器实际上会区分这些类型 - 动机是它们在编译的 .NET 代码中以不同的方式表示。这会影响性能以及与 C# 的互操作性,因此进行区分很有用。

一个函数Foo : int -> int -> int将被编译为成员int Foo(int, int) - 编译器默认不使用柯里化(Currying)形式,因为这在调用 Foo 时效率更高使用两个参数(更常见的情况),它更适合互操作。一个函数Bar : int -> (int -> int)将编译为 FSharpFunc<int, int> Bar(int) - 实际上使用柯里化(Currying)形式(因此仅使用单个参数调用它会更有效,并且很难从 C# 中使用)。

这也是为什么 F# 在涉及签名时不将类型视为相等的原因 - 签名指定类型,但在这里它还指定如何编译函数。实现文件必须提供正确类型的函数,但在这种情况下,也必须提供正确的编译形式。

关于f# - 纯函数式语言中的 η 展开,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15829660/

相关文章:

haskell - Monads如何被认为是纯的?

Scala:为什么使用类型作为方法调用?

ocaml - 在 Ocaml 中缩写构造函数名称

functional-programming - 函数式编程函数混淆

f# - 如何将一系列可区分联合(其中所有项目都属于同一案例)映射到案例类型的项目序列上?

f# - 扑克手 - 在 F# 中确定满堂彩

f# - 如何为受歧视的联盟覆盖 .Equals() ?

haskell - func = elem [1..10] 在 GHCi 中工作但无法编译

ocaml - 多态性变体的打结

macos - Mac 上的 F# Interactive 在哪里