为什么用功能语言编写编译器更容易?[关闭]


88

我考虑这个问题已经很长时间了,但实际上在Google上找不到答案,在Stackoverflow上也找不到类似的问题。如果有重复,对不起。

许多人似乎都说,用命令式语言编写使用OCaml和Haskell等功能语言编写的编译器和其他语言工具要高效得多,并且更容易。

这是真的?如果是的话,为什么用功能性语言而不是像C这样的命令性语言编写它们是如此高效和容易?而且-功能语言中的语言工具难道不比C等低级语言慢吗?


5
我不会说这更容易。但是,诸如解析之类的编译任务的功能性质可能很自然地适合于函数式编程。像OCaml这样的功能语言可以非常快,可以与C的速度相媲美。
罗伯特·哈维

17
乡亲,这真的有争议吗?肯定有人有见识。我想认识自己。
罗伯特·哈维

我认为应该至少有一些充分的理由说明为什么使用功能性语言而不是命令性语言。我发现有些文章基本上是关于功能语言没有副作用之类的。但这还不是很清楚。但是,如果这是争论的话,那么最好将其关闭或重新表述该问题。
wvd 2010年

12
承认某些壁ni更适合特定的语言风格真的有争议吗?“我认为,“为什么C比编写设备驱动程序的Java要好于Java脚本”将不会引起争议……
CA McCann,2010年

1
我认为那是相反的。我正在阅读“超级微型编译器”,它到处都使用了变量突变。
罗尔夫(Rolf)

Answers:


101

通常,编译器对树的工作量很大。源代码被解析为语法树。然后可以将该树转换为带有类型注释的另一棵树,以执行类型检查。现在,您可以将该树转换为仅包含核心语言元素的树(将句法式的类似糖的符号转换为未修饰的形式)。现在您可以执行各种优化,这些优化基本上是对树的转换。之后,您可能会以某种常规形式创建一棵树,然后遍历该树以创建目标(程序集)代码。

函数式语言具有模式匹配和对有效递归的良好支持等功能,这使得使用树很容易,因此这就是为什么它们通常被认为是编写编译器的良好语言。


到目前为止最完整的答案,我将其标记为可接受的答案,但是我认为Pete Kirkham的答案也不错。
wvd 2010年

1
关于“证明正确性”呢,由于编译器的正确性是重要的属性,所以我经常听到函数语言的支持者以某种方式将正确性的“证明”纳入其工作流程中。我不知道这实际上意味着什么,但是由于编译器的可靠性很重要,所以这似乎值得。
沃伦·P

3
@WarrenP:“带有证明的代码”概念来自静态类型的功能语言。想法是使用这种类型系统,以便函数只能对类型是否正确进行类型检查,因此代码编译的事实就是正确性的证明。当然,在保持语言的图腾完整和可判定类型检查的同时,这是不可能的。但是类型系统越强大,您就越可以实现该目标。
sepp2k 2010年

3
这个概念主要在功能社区中流行的原因是,在具有可变状态的语言中,您还必须对有关类型何时何地发生状态更改的信息进行编码。在您知道函数结果仅取决于其参数的语言中,在类型中对证明进行编码要容易得多(手动验证代码的正确性也要容易得多,因为您不必考虑哪个全局状态是可能以及它将如何影响功能的行为)。但是,这些都与编译器无关。
sepp2k 2010年

3
我认为最重要的功能是模式匹配。使用模式匹配来优化抽象语法树非常容易。不进行模式匹配就很难做到这一点。
鲍勃·阿曼

38

许多编译器任务都是在树结构上进行模式匹配。

OCaml和Haskell都具有强大而简洁的模式匹配功能。

很难将模式匹配添加到命令式语言中,因为要评估或提取与该模式匹配的任何值都必须是无副作用的。


听起来像是一个合理的答案,但这是唯一的吗?例如,诸如尾递归之类的事情也会起作用吗?
wvd 2010年

这似乎表明,类型系统的问题比实际执行模型的问题更多。基于命令式编程的某些东西在结构类型上具有不变的值可能会很好。
Donal Fellows,2010年

@wvd:尾递归优化是实现细节,而不是语言特性,它使线性递归函数等效于迭代循环。与在Scheme中对列表进行递归一样,在C中遍历链表的递归函数也会从中受益。
CA McCann 2010年

@wvd gcc C和其他可变状态语言一样,都有尾音消除功能
Pete Kirkham 2010年

3
@camccann:如果语言标准保证了tco(或至少保证某种形式的递归函数将永远不会导致堆栈溢出或内存消耗的线性增长),那么我会考虑一种语言功能。如果标准不能保证它,但是编译器仍然可以保证,那就是编译器的功能。
sepp2k 2010年

15

要考虑的一个重要因素是,在任何编译器项目中,很大一部分就是您可以自行托管编译器并“吃掉自己的狗粮”。因此,当您查看像OCaml这样用于语言研究的语言时,它们往往具有针对编译器类型问题的强大功能。

正是出于这个原因,在我上一个像编译器一样的工作中,正是出于这个原因,在处理C代码时使用了OCaml,它只是完成任务的最佳工具。如果INRIA的人员以不同的优先级构建了OCaml,则可能不是一个很好的选择。

也就是说,功能语言是解决任何问题的最佳工具,因此从逻辑上讲,它们是解决特定问题的最佳工具。QED。

/ me:少高兴一些地爬回我的Java任务...


4
-1表示“功能语言是解决任何问题的最佳工具”。如果这是真的,我们都会在各处使用它们。;)
Andrei Krotkov 2010年

15
@Andrei Krotkov:今天的日常用语是fa·ce·tious发音:\ fə-ˈsē-shəs \功能:形容词源:中古法语facetieux,来自facetie jest,来自拉丁文facetia日期:1599 1:经常开玩笑或开玩笑经常:摇摇晃晃的<just be facetious> 2:表示幽默或有趣:不认真的<a facetious remark>同义词see witty除了开个玩笑,您的逻辑仍然有缺陷。您假设所有人都是理性行为者,而且恐怕这不是一个公平的假设。
Ukko,2010年

5
我想我错过了这个玩笑,因为我知道现实生活中的人们会说出确切的话,除了非常认真地讲。我猜是坡定律。tvtropes.org/pmwiki/pmwiki.php/Main/PoesLaw
Andrei Krotkov 2010年

13
@Andrei:使用您的论点:“如果理性比情感上的无知更好,那么我们都会在各处使用它。”
蒂姆·谢弗

9

基本上,编译器是从一组代码到另一组代码的转换-从源代码到IR,从IR到优化的IR,从IR到汇编,等等。这正是功能语言设计的目的-纯函数是只是从一件事到另一件事的转变。命令式函数没有这种品质。尽管您可以用命令式语言编写此类代码,但是功能性语言专用于此。


6

也可以看看

F#设计模式

FP将事物按“操作”分组,而OO将事物按“类型”分组,对于编译器/解释器,“按操作”更为自然。


3
这与某些编程语言理论界所说的“表达问题”有关。例如,请参见此问题,其中我演示了一些真正可怕的Haskell代码,它们以“可扩展类型”的方式执行操作。相反,将OOP语言强制为“可扩展的操作”样式往往会激发访问者模式。
CA McCann 2010年

6

一种可能性是,编译器往往不得不非常仔细地处理大量的极端情况。通过使用设计模式来构造正确的代码通常会更容易,该设计模式以与实现规则并行的方式来构造实现。通常,最终的结果是声明式(模式匹配,“ where”)而不是命令式(排序,“ when”)设计,因此更容易以声明性语言实现(并且大多数都是功能性的)。


4

似乎每个人都错过了另一个重要原因。为解析器编写嵌入式领域专用语言(EDSL)非常容易,该语言看起来很像普通代码中的(E)BNF。解析器组合器像Parsec这样的器很容易使用高阶函数和函数组合用函数语言编写。不仅容易,而且非常优雅。

基本上,你代表了最简单通用的解析器像刚才的功能,你有特殊的操作(通常高阶函数),它让你撰写这些原始解析器到更加复杂,更加具体的解析器你的语法。

这不是建立课程框架的唯一方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.