功能是如何计算的?

时间:2011-11-16 07:46:27

标签: pointers haskell functional-programming low-level currying

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

例如,当(+) 2 4被咖喱时,指向2的指针是否会被传递到4?甘道夫是否会缩短时空?这个魔法是什么?

2 个答案:

答案 0 :(得分:14)

简答:是的,在2传入之前,指针一直保持到4


比必要答案更长:

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

f x y = x + y

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

\x -> (\y -> (x + y))

如果你不熟悉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

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

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

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

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

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

答案 1 :(得分:8)

尝试使用GHC:

ghc -C Test.hs

这将在Test.hc

中生成C代码

我写了以下函数:

f = (+) 16777217

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);

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

这里的stg代表“Spineless Tagless G-Machine”。 There is a really good paper on it, by Simon Peyton-Jones。如果您对如何实现Haskell运行时感到好奇,请先阅读。