Haskell 哈斯凯尔是不是;“理解”;咖喱函数定义?

Haskell 哈斯凯尔是不是;“理解”;咖喱函数定义?,haskell,Haskell,在Haskell中,函数始终采用一个参数。多个参数通过以下方式实现:。在这种情况下,我可以看到下面如何将两个参数的函数定义为“func1”。它是一个返回函数(闭包)的函数,该函数将外部函数的单个参数添加到返回函数的单个参数中 然而,尽管curried函数就是这样工作的,但这不是定义双参数函数的常规Haskell语法。相反,我们被教导定义类似“func2”的函数 我想知道Haskell是如何理解func2的行为应该与func1相同的。func2的定义并没有告诉我它是一个返回函数的函数。相反,它实际

在Haskell中,函数始终采用一个参数。多个参数通过以下方式实现:。在这种情况下,我可以看到下面如何将两个参数的函数定义为“func1”。它是一个返回函数(闭包)的函数,该函数将外部函数的单个参数添加到返回函数的单个参数中

然而,尽管curried函数就是这样工作的,但这不是定义双参数函数的常规Haskell语法。相反,我们被教导定义类似“func2”的函数

我想知道Haskell是如何理解func2的行为应该与func1相同的。func2的定义并没有告诉我它是一个返回函数的函数。相反,它实际上看起来像一个双参数函数,我们被告知它不存在

这里有什么诀窍?Haskell是不是生来就知道我们可以用教科书上的方法定义多参数函数,而且它们的工作方式与我们期望的一样?也就是说,这是一个似乎没有明确记录的语法约定(Haskell知道您的意思,并将为您提供缺少的函数return),还是有其他一些魔力在起作用,或者我缺少什么

func1 :: Int -> (Int -> Int)
func1 x = (\y -> x + y)

func2 :: Int -> Int -> Int
func2 x y = x + y

main = do
    print (func1 7 9)
    print (func2 7 9)

当谈到类型签名时,没有“多参数函数”这样的东西。所有函数都是单参数、周期。Haskell不需要以某种方式将多参数函数“转换”为单参数函数,因为前者根本不存在

所有函数类型签名看起来像
a->b
,其中
a
是参数类型,
b
是返回类型。有时
b
可能恰好包含更多的箭头
->
,在这种情况下,我们人类(而不是编译器)可能会说函数有多个参数

当谈到实现的语法时,即
fxy=z
——这仅仅是语法上的糖分,在编译过程中会被分解(即机械地转换)成
f=\x->\y->z

在语言本身中,编写形式为
f x y z=\ucode>的函数定义相当于
f=\x y z->\ucode>,这相当于
f=\x->\y->\z->\ucode>。这没有理论上的原因;只是那些嵌套的lambda抽象是一个可怕的眼痛/指痛,每个人都认为牺牲一点学究风度来为它添加一些语法糖分是好的。这就是表面上的一切,也许是你现在需要知道的全部

然而,在语言的实现中,事情变得更加棘手。在GHC中,这是最常见的实现,实际上
fxy=\ucode>和
f=\x->\y->\ucode>之间存在差异。当GHC编译Haskell时,它为声明分配arity。
f
的前一个定义是arity
2
,后者是arity
0
。从
GHC.Base

(.) f g = \x -> f (g x)
()
具有arity
2
,即使其类型(
(b->c)->(a->b)->a->c
)表示最多可以应用三次。这会影响优化:GHC只会内联一个饱和的函数,或者至少应用了与其arity相同数量的参数。在调用
(最大值)
中,
()
不会内联,因为它只有一个参数(它是不饱和的)。在调用
(maximum.f)
中,它将内联到
\x->maximum(fx)
,在
(maximum.f)1
中,
()
将首先内联到lambda抽象(生成
(\x->maximum(fx))1
,该抽象将beta降低到
最大值(f1)
。如果实施了
(.)

(.) f g x = f (g x)
()
将具有arity
3
,这意味着它将更少地内联(特别是
f.g
情况,这是高阶函数的一个非常常见的参数),可能会降低性能,这正是对它的评论所说的:

确保它只有两个参数在左边,这样它就可以内联了 应用于两个函数时,即使没有最终参数


最终答案:根据语言的语义,这两种形式应该是等价的,但在GHC中,这两种形式在优化方面有不同的特点,即使它们总是给出相同的结果。

Haskell将
func2
转换为
func1
,多参数函数肯定是作为一个概念存在于实施上述脱糖的人的头脑中。您是否在文档中引用了对脱糖的解释?在我读的所有书籍和维基页面中,我都错过了它,尽管很明显,类似的事情必须发生(因此我的问题)。顺便说一句,当我使用你说的“翻译”这个词时,我实际上是指你说的事情正在发生,你称之为“去糖化”。意识到不是我使用了“翻译”这个词,尽管我很容易做到。Desugaring只是句法翻译的一种形式,不是吗?你在问题中引用了两种不同的类型签名和两种不同的身体定义,并问Haskell如何“知道”它们是等价的。我回答了两个问题:第一部分是关于类型签名,第二部分是关于正文定义。谢谢,这很有帮助。fwiw我想我明白了为什么这在文献中没有清楚地出现:函数通常在lambda表达式之前引入,但如果不先理解后者,就无法解释这种语法。我最终在“Haskell编程”第4.5节“Lambda表达式”中找到了解释,第3.6节讨论了Curried函数之后的页面。尽管教程页面介绍了lambdas,并讨论了一个有两个参数的函数,但它对这个主题没有任何说明。Haskell定义文档擅长回答