为什么在 ML 系列语言中自动套用
Why automatic currying in ML family languages
ML 系列语言具有自动柯里化功能 - 其中带有两个参数的函数调用被认为一次应用一个,f x y
= (f x) y
- 而其他语言系列则没有。
这只是历史事故,还是有技术原因?
请注意,我 不是 询问自动柯里化是好是坏(这是主观判断)。我在问它是否与 ML 家族语言的其他特性协同作用,以便更自然地包含在这些语言中,如果是,这些特性是什么,协同作用的本质是什么?或者相反,是否有其他语言家族的特性会与任何提供自动柯里化的尝试发生冲突?
编辑:除了给定的答案之外,我想到可能还有另一个因素:如果你正在用一种具有模式匹配的语言编写编译器,那没关系让 AST 变得更复杂,这意味着传统的 'call with a tuple' 结构可以成为一个复合术语。因此,考虑到编译器通常是用自己的语言编写的,自动柯里化和模式匹配可以很好地结合在一起。
由于标准 ML 支持元组(和其他记录)和模式匹配,它还支持 非-currying 模型:
fun add (x, y) = x + y
所以柯里化函数只是一种选择,定义它们的特殊支持只是语法糖(尽管对于编写惯用的 ML 代码来说非常重要的语法糖)。没有理由像 JavaScript 这样的语言不能 提供相同的语法糖:
// Analogous to fun (f o g) x = f (g x) -- not actually valid JS:
function compose(f)(g)(arg) {
return f(g(arg));
}
但是由于 JavaScript 函数立即 return 一个函数并不常见,所以这个语法糖在那里不会那么有用。 (当然,这有点循环:一种语言中的共同点是由该语言使什么变得容易而形成的,而一种语言使事情变得简单是因为它们在该语言中是共同的。但这对所有语言特性都是如此,而不是特定于这个。)
有趣的是,即使在标准 ML 中,特殊的柯里化函数语法也仅适用于函数 声明 (fun ...
),不适用于函数 表达式 (fn ...)
。所以我可以写
val op o = fn f => fn g => fn x => f (g x)
但不是
val op o = fn f g x => f (g x) (* illegal *)
在Haskell中,相比之下,即使是函数表达式也支持自动柯里化;例如,(\ x y z -> x + y + z) 3 4 5
的计算结果为 12
。 (Haskell 还在另一个方面将柯里化视为更重要的部分,即中缀运算符是柯里化函数,这与标准 ML 不同,在标准 ML 中,中缀运算符是采用一对的函数。)
这绝对不是偶然的。
一方面,它简化了语言:不需要将具有多个参数的函数作为一个单独的概念引入;每个函数只有一个参数。多个参数可以通过元组或柯里化来表达。
其次,它在实践中非常方便,因为依赖于部分应用的各种模式(例如,高阶函数、组合器库等)证明了..
ML 系列语言具有自动柯里化功能 - 其中带有两个参数的函数调用被认为一次应用一个,f x y
= (f x) y
- 而其他语言系列则没有。
这只是历史事故,还是有技术原因?
请注意,我 不是 询问自动柯里化是好是坏(这是主观判断)。我在问它是否与 ML 家族语言的其他特性协同作用,以便更自然地包含在这些语言中,如果是,这些特性是什么,协同作用的本质是什么?或者相反,是否有其他语言家族的特性会与任何提供自动柯里化的尝试发生冲突?
编辑:除了给定的答案之外,我想到可能还有另一个因素:如果你正在用一种具有模式匹配的语言编写编译器,那没关系让 AST 变得更复杂,这意味着传统的 'call with a tuple' 结构可以成为一个复合术语。因此,考虑到编译器通常是用自己的语言编写的,自动柯里化和模式匹配可以很好地结合在一起。
由于标准 ML 支持元组(和其他记录)和模式匹配,它还支持 非-currying 模型:
fun add (x, y) = x + y
所以柯里化函数只是一种选择,定义它们的特殊支持只是语法糖(尽管对于编写惯用的 ML 代码来说非常重要的语法糖)。没有理由像 JavaScript 这样的语言不能 提供相同的语法糖:
// Analogous to fun (f o g) x = f (g x) -- not actually valid JS:
function compose(f)(g)(arg) {
return f(g(arg));
}
但是由于 JavaScript 函数立即 return 一个函数并不常见,所以这个语法糖在那里不会那么有用。 (当然,这有点循环:一种语言中的共同点是由该语言使什么变得容易而形成的,而一种语言使事情变得简单是因为它们在该语言中是共同的。但这对所有语言特性都是如此,而不是特定于这个。)
有趣的是,即使在标准 ML 中,特殊的柯里化函数语法也仅适用于函数 声明 (fun ...
),不适用于函数 表达式 (fn ...)
。所以我可以写
val op o = fn f => fn g => fn x => f (g x)
但不是
val op o = fn f g x => f (g x) (* illegal *)
在Haskell中,相比之下,即使是函数表达式也支持自动柯里化;例如,(\ x y z -> x + y + z) 3 4 5
的计算结果为 12
。 (Haskell 还在另一个方面将柯里化视为更重要的部分,即中缀运算符是柯里化函数,这与标准 ML 不同,在标准 ML 中,中缀运算符是采用一对的函数。)
这绝对不是偶然的。
一方面,它简化了语言:不需要将具有多个参数的函数作为一个单独的概念引入;每个函数只有一个参数。多个参数可以通过元组或柯里化来表达。
其次,它在实践中非常方便,因为依赖于部分应用的各种模式(例如,高阶函数、组合器库等)证明了..