功能是如何计算的?

pro*_*nce 15 haskell pointers functional-programming currying low-level

我理解currying的概念是什么,并且知道如何使用它.这些不是我的问题,而是我很好奇这个实际上是如何在比Haskell代码更低的层次上实现的.

例如,当(+) 2 4curried时,是一个指向2维持的指针,直到4传入?甘道夫是否会缩短时空?这个魔法是什么?

Ben*_*Ben 14

简短的回答:是一个指针被保持到2直至4被通过.


超过必要答案:

从概念上讲,你应该考虑用lambda演算和术语重写来定义Haskell.假设您有以下定义:

f x y = x + y
Run Code Online (Sandbox Code Playgroud)

这个定义f在lambda演算中出现如下所示,其中我明确地将括号放在lambda体周围:

\x -> (\y -> (x + y))
Run Code Online (Sandbox Code Playgroud)

如果你不熟悉lambda演算,这基本上就是" x返回一个参数的函数(一个y返回(x + y)的参数的函数").在lambda演算中,当我们将这样的函数应用于某个值时,我们可以用函数体的副本替换函数的应用程序,并用值替换函数的参数.

那么表达式f 1 2将通过以下重写序列进行评估:

(\x -> (\y -> (x + y))) 1 2
(\y -> (1 + y)) 2                 # substituted 1 for x
(1 + 2)                           # substituted 2 for y
3
Run Code Online (Sandbox Code Playgroud)

所以你可以在这里看到,如果我们只提供一个参数f,我们就会停止\y -> (1 + y).所以我们有一个完整的术语,它只是一个函数,用于将1添加到某个东西,完全独立于我们的原始术语,它可能仍然在某处使用(对于其他参考f).

关键点在于,如果我们实现这样的函数,每个函数只有一个参数但有一些返回函数(以及一些返回返回函数的函数返回...).每次我们应用函数时,我们都会创建一个新术语,将第一个参数"硬编码"到函数体中(包括此函数返回的函数体).这就是你如何得到currying和closures.

现在,显然不是Haskell直接实现的方式.曾几何时,Haskell(或者可能是其前身之一;我对历史并不完全确定)是通过Graph缩减实现的.这是一种用于执行与上述术语缩减相当的操作的技术,它自动带来惰性评估和大量数据共享.

在图缩减中,一切都是对图中节点的引用.我不会详细介绍,但是当评估引擎将函数的应用程序减少到一个值时,它会复制与函数体相对应的子图,并为函数的参数值进行必要的替换.参数(但是共享对图形节点的引用,它们不受替换的影响).基本上,是的,部分应用函数会在内存中创建一个新结构,该结构具有对所提供参数的引用(即"指向该指针的指针2),并且您的程序可以传递对该结构的引用(甚至可以共享它并将其应用于多个时间),直到提供更多的参数并且它实际上可以减少.但是它不仅仅是记住函数并累积参数直到它获得所有这些;评估引擎实际上每次将它应用于新的时都会执行一些工作事实上,图形缩减引擎甚至无法区分返回函数但仍需要更多参数的应用程序与刚刚获得其最后一个参数的应用程序之间的区别.

我不能告诉你更多关于Haskell当前实现的信息.我相信这是一个遥远的图形缩减的变异后代,有大量聪明的捷径和更快的条纹.但我可能错了; 也许他们已经找到了一个完全不同的执行策略,而不再是图形缩减.但是我90%肯定它仍然会最终传递继续引用部分参数的数据结构,并且它可能仍然会做一些等同于部分参数的因素,因为它似乎对于懒惰的评估非常重要作品.我也相当肯定它会进行大量的优化和捷径,所以如果你直接调用5个参数的函数就像f 1 2 3 4 5它不会经历所有麻烦的复制f的身体5次连续更多"硬-编码".


Apo*_*isp 8

尝试使用GHC:

ghc -C Test.hs
Run Code Online (Sandbox Code Playgroud)

这将生成C代码 Test.hc

我写了以下函数:

f = (+) 16777217
Run Code Online (Sandbox Code Playgroud)

GHC产生了这个:

R1.p[1] = (W_)Hp-4;
*R1.p = (W_)&stg_IND_STATIC_info;
Sp[-2] = (W_)&stg_upd_frame_info;
Sp[-1] = (W_)Hp-4;
R1.w = (W_)&integerzmgmp_GHCziInteger_smallInteger_closure;
Sp[-3] = 0x1000001U;
Sp=Sp-3;
JMP_((W_)&stg_ap_n_fast);
Run Code Online (Sandbox Code Playgroud)

要记住的是,在Haskell中,部分应用并不是一个不常见的情况.从技术上讲,任何函数都没有"最后的论据".正如你在这里看到的那样,Haskell正在跳跃到stg_ap_n_fast期望有一个参数Sp.

stg这里代表"骨气无需生成代码G-机".Simon Peyton-Jones撰写了一篇非常好的论文.如果您对如何实现Haskell运行时感到好奇,请先阅读.