我对Haskell了解不多,但是从我读过的有关计算可变性的内容(例如:函数返回函数,复杂的monad和函数等)看起来你可以做很多元编程,即使在运行时也是如此。
答案 0 :(得分:8)
我不确定你的“可变性”是什么意思,但是将这种抽象语言编译成高效的机器代码肯定是一件复杂的事情。作为一个起点,你可以阅读The Implementation of Functional Programming Languages,这是Haskell背后的主要人物Simon Peyton-Jones的一本书。
对于更新的东西,有some commentary on the internals of GHC,旗舰Haskell编译器。
在一个更实际的说明中,如果它只是你想知道的“作为价值观的功能”的想法,那就是旧帽子--Lisp方言一直在编译高阶函数的时间比我长。我们还活着,甚至连C都指向周围的功能。
答案 1 :(得分:8)
Haskell如何编译成机器代码......?
通过此编译策略:Implementing Lazy Functional Languages on Stock Hardware
答案 2 :(得分:5)
你以非标准的方式使用“不可变”这个词令人困惑。如果某些数据是不可变的,我们的意思是它不会在程序的整个生命周期中发生变化。我认为你要问的是:Haskell允许你将函数视为第一类对象,这样你就可以动态地构建旧函数。您想知道如果编译器在运行时之前不知道哪些函数会存在,那么如何将其编译为机器代码。这是一个很好的问题。
我将描述可以使用的方法的一部分。不一定是真正的编译器所做的。这只是一个理论上的想法,让您了解如何编译动态显示的功能。 (实际上,我在玩具编译器中使用过它。)
这是一个Haskell函数
f x = let w = 3*x
g y = w+2*y in g
f
就像你描述的那样。它构建了一个名为g
的新函数,它将其参数加倍并添加3*x
。然后它返回新函数。如果在将x
传递到f
之前甚至不知道函数是什么,那么如何编译该函数?
编译器可以执行一个名为Lambda lifting的阶段。它看到你在本地创建了一个名为g
的函数,并将其作为一个全局函数拉出来。有一个问题。我们无法使g
成为全局函数,因为它取决于本地值w
。因此,我们将g
作为一个全局函数,将w
作为参数:
g' w y = w+2*y
现在编译器可以重写f
以返回g'
,为其提供所需的参数:
f' x = let w = 3*x in g' w
您现在可能会问,“如果g'
需要2个参数,我们如何才能返回g' x
只提供g'
一个参数?”。在内部,编译器可以生成所谓的closure。它将g' w
表示为包含一对对象,全局函数g'
的指针(或其他)和值w
的结构。编译器现在可以继续编译g'
作为普通函数。我希望你看到我们已经消除了可以构建新功能的任何地方。每当我们将g' w
应用于第二个参数时,编译器都可以发现它已经存储了第一个参数,并将其和第二个参数传递给g'
。
同样,我会强调有很多方法可以编译功能代码,我刚才描述了一件你可以做的事情。但只是展示一种方式应该带走一些关于它可能的事实的神秘感。真正的编译器可能会以各种方式优化您的代码,甚至无需构建闭包。我认为最新的C ++标准可能正如我所描述的那样实现新的lambda
。
我想我明白你的来源。当我开始学习Haskell时,我非常关注“如何编译汇编语言?”人。因此,在我最初理解Haskell之后,我只是停下来,编写了我自己的基于this论文的函数式语言编译器,然后以更深入的理解返回Haskell。 SASL根本不像Haskell那样编译,但它足以让我更容易理解Haskell 是如何编译的。
答案 3 :(得分:1)
Haskell实际上是强不可变的并且非常静态。您可以获得动态行为和可变性,但默认值是不可变数据结构和严格的引用透明性。