编程与软件工程flint.cs.yale.edu
为什么 ML/OCaml 适合写编译器
Why ML/OCaml are good for writing compilers (1998)
2011 — 2017,它一次次回到首页
黑点是上首页(≥ 50 分)的那次投稿,空心点是没冲上去的投稿,点越大分数越高。
导读
这是耶鲁大学 1998 年的一篇经典文章,论证 ML/OCaml 语言为何特别适合编写编译器:垃圾回收、尾递归、代数数据类型、模式匹配和类型推断等特性都能显著简化编译器开发。文章多年来多次登上 Hacker News 首页,至今仍是讨论函数式语言与编译器工程时的常被引用的文献。
为什么经久不衰:它对 ML 语言适合编译器开发的论证经受住了近三十年时间检验,代数数据类型等观点至今仍是语言设计讨论的核心,对理解现代语言(Rust、Swift 等)的设计渊源也很有帮助。
HN 评论精选
逐条指出文章论点在实际使用 OCaml 时的局限与例外
文中一些观点在实践中并不完全成立:1) GC 的说法成立,但写作时 GC 在主流语言中还不常见;2) 尾递归对遍历树帮助不大,因为树递归大多不是尾递归;3) OCaml 因实现细节使用 63/31 位整数,不适合 64/32 位整数场景;4) 代数数据类型描述 AST 有好有坏,一旦 AST 需要携带所有变体共享的语义信息(如源码坐标),继承反而更合适;8) 类型推断不适用于模块签名,必须显式写出,而且显式标注函数类型也能减少难以理解的类型错误。
rbehrends查看英文原文
I'll note that some of the aspects don't necessarily work out like that in practice: 1. The GC part is true, but one has to remember that this was written at a time when GC was still a bit of an unusual feature in mainstream languages. 2. Tail recursion doesn't really make much of a difference for walking trees, which is recursive, but (mostly) not tail recursive. 3. OCaml in particular uses 63/31-bit ints due to implementation details, which isn't a good fit for 64/32-bit integers. The strings and bignum part is mostly right, though. 4. ADTs can be good or bad for describing ASTs. Once you enrich ASTs with semantics shared by all variants (such as source coordinates), inheritance can become a better fit than ADTs. 8. Type inference doesn't really extend to module signatures, which you have to write out explicitly (though tooling such as `ocamlc -i` allows you to let the compiler help you write them). I also generally find it better to explicitly annotate functions with types. Not only does it make the code more readable later in its life, but you get fewer truly impenetrable type error messages because you forgot parentheses or a semicolon somewhere. That said, there are several good points still.
解释为何在编译器领域选 OCaml 而非功能更强的 Haskell
这篇文章缺少对'为什么选 ML/OCaml 而不是 Haskell'的回答。Haskell 特性更多、类型系统更先进、库支持更好,但我认为 OCaml/SML 往往是更好的选择:大量编译器教材(如 TAPL、ML 版现代编译器实现)都用 ML 作示例语言;更重要的是 ML 有多种实现,都比 GHC 平易近人得多——OCaml 编译器代码库规模适中,个人几周就能摸清原理,SMLNJ、MLton、CakeML 等也都开源且易读,而 Haskell 编译器往往需要深入理解大量类型类和语言扩展。
nv-vn查看英文原文
The problem with this article is that it's missing an answer to why one would choose ML/OCaml over Haskell. Haskell has many more features, a more advanced type system, arguably superior syntax, and much better library support. However, I believe that OCaml/SML are often a better choice for a number of reasons. First of all, OCaml/SML are the best choice in terms of example code for compilers. They're historically the choice of many compiler/interpreter/type theory texts (Types and Programming Languages, Modern Compiler Implementation in ML, and an ML is even used as a language to interpret in Essentials of Programming Languages). Andrej Bauer's PLZOO is also written in OCaml. Equally important is the fact that there are a variety of ML implementations, all of which are much more approachable than GHC. The OCaml compiler's codebase is a reasonable size that an individual could get a good idea of how it works in a few weeks or so. SMLNJ, MLKit, MLton, CakeML are all open source and on Github, and all seem to be fairly approachable in comparison to the monolith that is GHC. And that's not even mentioning other compiler in ML (Haxe, Rust's bootstrap compiler, Facebook's Hack compiler, etc.). The fact that there are real-world compilers with perfectly approachable code bases (even without great familiarity with the language; compilers in Haskell might require an in-depth understanding of many of the core type classes and language extensions available) that are open source is highly attractive to novice compiler writers. Additionally, the feature set in MLs is a good choice for compilers. While they lack some of the cooler features of Haskell, MLs make up for it in simplicity; lots of the features in GHC's type system (especially with language extensions) mean very little for 90% of compiler writers, and getting rid of them from the get-go helps keep the code small and easy to reason about (even if you won't have as much type safety in the compiler itself). This also means that there are a lot less ways to do a single thing, which can be nice when you're not sure exactly how you're going to implement a certain feature. However, one thing I really find incredibly useful is OCaml's polymorphic variants. These are pretty much perfect for statically enforcing a nanopass-like system in your compiler and are a great way of designing your AST/data types in your compiler. I feel like this gets passed up a ton (as far as I know I'm the first person who's used them to create nanopasses), but it's quite convenient and makes OCaml a good competitor for Scheme in this regard.
一线教学经验印证和类型是 OCaml 写编译器的最大优势
我喜欢用 OCaml 写编译器:我担任编译器课程助教,自己的课程项目实现(一个类 Go 子集的编译器)就是用 OCaml 写的。相比用 Java 写的论文项目,OCaml 让人耳目一新。我认为 OCaml 最重要的特性是类型系统,尤其是和类型(sum types)。一旦学会用和类型来设计方案,就很难再回到那些不支持它、只能费劲模拟的语言。
gnuvince查看英文原文
I love writing compilers in OCaml: I T.A. a compiler class and my own implementation of the project (a compiler for a not-quite subset of Go) is written in OCaml. Compared to my thesis project which is written in Java, OCaml is a breath of fresh air and I feel that the single, most important feature of OCaml is its type system, especially having sum types available. Once you learn to use sum types to design your solutions, it's very hard to go back to other languages that don't support them and where you need to figure out the least painful way to emulate them.
指出 ML 写编译器的样板代码痛点,提出 Nanopass 式改进方向
ML 系语言写编译器远好于普通主流语言,但仍有问题:最糟的是即使对 AST 做最简单的一遍处理也需要大量样板代码——比如只想访问并改写所有标识符节点,却要为所有节点类型写递归函数。Haskell 的 Scrap Your Boilerplate 能缓解一点,但仍远不如 Nanopass 便捷。相关问题还有:稍作修改的 AST 类型很难定义,无法从现有类型简单推导;ML 也没有透明处理元数据的简便方式。我真正想要的是把 ML 类型系统和 Nanopass 的简洁性结合在一个语言里(我正在做,还没完成)。
sklogic查看英文原文
MLs are way much better than the average blubs for writing compilers, but yet they still have some issues. The worst is the amount of a boilerplate code needed to implement even the smallest pass over an AST. Say, you want to visit and rewrite all the identifier nodes, but yet you must implement recursive functions for all the node types. In Haskell a bit of the Scap Your Boilerplate magic relieves this problem a bit, but still a long way to go to reach a level of convenience of Nanopass. Next issue is related: slightly amended AST types are hard to define, they cannot be derived fromtypesxisting one by a simple rewrite. Also, MLs do not allow an easy way to handle metadata transparently. What I really want to have is ML type system and Nanopass density combined in one language (working on it, not done yet).
历次讨论
同类还值得读
开源应用架构
The Architecture of Open Source Applications
《开源软件架构》(AOSA)是一本在线免费书,邀请知名开源项目的作者亲自剖析其内部设计,涵盖 SQLAlchemy、LLVM、Audacity、Apache、Bash 等系统。
编程与软件工程上首页 9 次 · 2011–2024aosabook.org编辑精选编程语言简史:简短、不完整且多半错误
A Brief, Incomplete, and Mostly Wrong History of Programming Languages
一篇以戏谑、故意不准确的方式回顾编程语言史的博客文章,用夸张轶事和讽刺定义串起 Lisp、C、C++、Java、JavaScript、PHP 等语言。
编程与软件工程上首页 8 次 · 2009–2021james-iry.blogspot.comSQLite 是如何测试的
How SQLite is tested
SQLite 官方文档页,系统介绍 SQLite 的测试体系:多种测试方法(TCL 测试、模糊测试、回归测试、静态分析等)和海量测试用例,展示了这个小型开源项目如何靠极致测试获得…
编程与软件工程上首页 8 次 · 2009–2025sqlite.org编辑精选高级编程语言(2009)
Advanced programming languages (2009)
Matt Might 的经典文章,按用途推荐“高级编程语言”:如果追求就业就选主流语言,如果追求表达力、学术研究或创业,则推荐 Haskell、OCaml、Scala 等函数式语…
编程与软件工程上首页 8 次 · 2009–2022matt.might.net我的 Forth 与栈机器经历
Yosefk - My history with Forth & stack machines
作者以亲身经历回顾了使用 Forth 语言和栈式机器的历程:既赞叹其简洁优雅、代码量小、可自举的优点,也坦率指出栈操作在实际编程中的痛苦,以及 Forth 社区文化中的问题。
编程与软件工程上首页 8 次 · 2010–2024yosefk.com编辑精选