繁体   English   中英

Haskell 是否“理解”柯里化函数定义?

[英]Does Haskell “understand” curried function definitions?

在 Haskell 中,函数总是采用一个参数。 多个参数通过Currying实现。 既然如此,我可以在下面看到如何将具有两个参数的函数定义为“func1”。 它是一个返回函数(闭包)的函数,该函数将外部函数的单个参数添加到返回函数的单个参数中。

然而,尽管这就是柯里化函数的工作方式,但这不是用于定义双参数函数的常规 Haskell 语法。 相反,我们被教导定义像“func2”这样的函数。

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

这里有什么诀窍? Haskell 是不是刚出生就知道我们可以用这种教科书的方式定义多参数函数,并且它们无论如何都以我们期望的方式工作? 也就是说,这是一个似乎没有明确记录的语法约定(Haskell 知道你的意思并且会为你提供丢失的函数返回),或者是否有其他一些魔法在起作用或我遗漏了什么?

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)

语言本身中,编写形式为fxyz = _的函数定义相当于f = \\xyz -> _ ,相当于f = \\x -> \\y -> \\z -> _ 这没有理论上的原因。 只是那些嵌套的 lambda 抽象是一个可怕的眼睛/手指疼痛,每个人都认为牺牲一点迂腐来为它制作一些语法糖是可以的。 这就是表面上的全部内容,现在可能就是您需要知道的全部内容。

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

(.) f g = \x -> f (g x)

(.)具有 arity 2 ,即使它的类型 ( (b -> c) -> (a -> b) -> a -> c ) 表示它最多可以应用三次。 这会影响优化:GHC 只会内联一个已饱和的函数,或者应用的参数至少与其元数一样多。 在调用(maximum .)(.)不会内联,因为它只有一个参数(它是不饱和的)。 在呼叫(maximum . f)内联到\\x -> maximum (fx)和在(maximum . f) 1中, (.)将直列第一到的λ抽象(产生(\\x -> maximum (fx)) 1 ),这将 beta 减少到maximum (f 1) 如果(.)被执行

(.) f g x = f (g x)

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

确保它只在左侧有两个参数,以便在应用于两个函数时内联,即使没有最终参数

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

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

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

在谈论实现的语法时,即fxy = z - 这只是语法糖,它在编译期间被脱糖(即机械转换)为f = \\x -> \\y -> z

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM