Chapter 1. 概述
作为读者,你也许已经听说过“编译器”“解释器”这些名词,但它们常常给人一种“高深莫测”的感觉:好像只有专业的语言学家、编译器工程师,才有资格碰这一类系统。而这本书想做的事情,恰恰相反——我们希望把这类看上去神秘的系统,拆解成一块一块可以动手实践的小零件,让你能真正看到:一门语言是怎样从源代码的字符,一步步变成可以在硬件上运行的“张量”和“指令”的。
在接下来的章节中,我们会用 MoonBit 这门语言,构造出一个 MiniMoonBit 编译器。MiniMoonBit 是 MoonBit 的一个语法子集,但足够表达有趣又不失实用的程序。本章作为全书的开篇,会先回答三个看起来朴素、但非常关键的问题:
- 为什么要研究编译器?
- 这本书具体要讲些什么?
- 要读懂、跟上这些内容,需要具备什么样的前置知识?
带着这三个问题,我们开始第一章的旅程。
为什么要研究编译器
自从 MoonBit 诞生之日,不少社交媒体(例如知乎等)上,就对 MoonBit 语言有各种质疑。
有的人说:现代世界已经有很丰富的编程语言了,我们不再需要新的编程语言了。
也有的人说,编程语言的研究意义已经不大,因为 AI 就能写代码。由于数据集的问题,AI 写已有的语言更轻松,而新的语言会因为缺少训练数据,进而由于马太效应,新的语言就会发展不起来。
甚至还有人说,AI 的时代已经到来了,人们做事只需要跟机器提要求就好了,编程语言什么的以后就不需要了。
真的是这样吗?
根据我自己对编程语言、编译器的长期研究经验,以及与 AI 技术日常打交道的体会,我不仅不认为 AI 时代会让新的语言“没有必要”,恰恰相反:越是 AI 时代,对编程语言与编译器的研究反而会变得越来越重要。 要理解这一点,我们先从“底层现实”出发——看看硬件和运行环境发生了什么变化,再看看软件世界里的使用者有什么样的需求,最后再回到 AI 本身。
越来越丰富的硬件和运行环境
过去二十多年里,计算机硬件的世界发生了翻天覆地的变化。
如果把时间倒回二十年前,在个人电脑和服务器市场上,占据绝对统治地位的是 x86 架构。绝大多数编译器,只要能把代码高效地翻译成 x86 指令,就已经能覆盖绝大部分应用场景了。
而今天的世界完全不同了。即便只考虑 CPU,我们也已经有了多种指令集架构:x86、ARM、RISC-V、LoongArch 等等。其中,尤其值得一提的是 RISC-V——它是一个开源指令集架构(ISA),任何芯片厂商都可以自由实现和扩展它。这带来的直接后果是:不同厂商生产的 RISC-V 芯片,往往在细节上都不完全相同,有的扩展了向量指令,有的增加了自定义指令,有的面向高性能服务器,有的则针对极度低功耗的嵌入式设备。而这带来的问题就是,如果编译器对RISC-V架构的芯片不加以区分,使用同一套策略生成代码,轻则导致性能下降,重则引发严重的兼容性问题。
再看 GPU。最初,GPU 只是为了图像渲染而生,但在近十多年里,GPU 在高性能计算和 AI 推理中扮演的角色越来越重要。为了进一步加速 AI 训练与推理,人们又发明了 TPU(Tensor Processing Unit)以及各种专用的 AI 加速 ASIC。不同厂商的芯片有不同的内存层次结构、不同的并行执行模型、不同的指令集扩展。要真正“榨干”这些芯片的性能,往往需要高度定制化的代码。
现实中,一个广为人知的事实是:很多高性能 AI 算子到今天仍然依赖手写汇编或手工高度优化的低级代码。 这不仅难以维护,而且严重限制了硬件创新的速度----每出现一代新的硬件,软件栈都要重新补课。
除此之外,还有另一个正在快速演进的“运行环境”:浏览器。WebAssembly(Wasm)的出现,让我们有机会在浏览器中高效地执行接近本地速度的代码。但 WebAssembly 本身只是一个“虚拟硬件”的抽象,它仍然在内存管理、安全模型、并发模型等方面,保留了大量需要编程语言和编译器研究者深入探索的问题。
把这些现象放在一起看,你会发现一个重要事实:
- 硬件世界正变得高度多样化和碎片化。
- 想要用一门“统一的”高级语言高效驾驭这些硬件,离不开强大的编译器和恰当的语言设计。
如果没有合适的语言和编译器,硬件创新就只能依赖“专家团队 + 手写汇编”的方式,难以被更多开发者利用。编译器,正是连接“抽象的语言世界”和“纷繁复杂的硬件世界”的那座桥梁。
编程语言的使用者的要求不一样
除了越来越丰富的硬件,还有一个常常被忽略但同样重要的维度:编程语言的使用者本身非常多元。 在不同的应用场景下,对编程语言的要求往往天差地别。
例如:
- 追求极致性能的程序:比如高频量化、数值计算、高性能后端服务等场景,对延迟和吞吐量极为敏感。这类使用者往往青睐 C、C++ 等能细粒度控制内存布局和指令的语言。
- 重视能耗和电池寿命的程序:在手机、可穿戴设备或电池供电的嵌入式设备上运行的应用,能耗太高不仅会让设备发烫,还会直接影响续航。它们需要编译产物既高效又节能。
- 受制于体积和存储空间的程序:在微控制器、工业控制设备等嵌入式环境中,程序的体积往往受到严格限制。一个看似“微小”的二进制尺寸差异,可能就是“能不能烧进芯片”的分水岭。
- 追求开发效率的团队:有些企业的业务变化非常快,验证想法的速度往往比运行时的性能更重要。他们希望使用像 Python 这类开发速度极快、生态丰富的语言,否则,开发节奏过慢,会让投资人和市场失去耐心。
- 把可靠性放在首位的系统:另一些团队则反过来,他们往往从事和金融交易、工业控制、操作系统内核等强安全和高可靠性的领域打交道。对于这些场景,减少生产事故比提升一点点开发效率重要得多。于是他们会选择 Rust 等在编译期尽可能消除 Bug 的语言。
- 极度在意编译速度和反馈循环的开发者:对于大型代码库和频繁迭代的项目,编译速度直接影响到开发体验。长时间的编译会让开发者不断被“打断思路”。这类用户往往非常青睐像 Go 这样编译速度极快的语言。
- 看重可读性和优雅性的程序员:有些人则特别在意程序的抽象结构和数学意义,希望语言更接近数学推理和逻辑表达,他们会被 Haskell、OCaml 等函数式语言所吸引。
- 已经习惯 Vibe Coding 的新一代程序员:对于他们而言,性能固然重要,但更重要的是:语言是否易读?报错信息是否友好?是否有便利的测试和文档工具?是否能很好地与 AI 编程助手协作?诸如此类的“整体开发体验”,会促使他们选择像 MoonBit 这样从一开始就考虑 AI 协同和现代工程实践的语言。
你看,每家公司、每个团队、每一类程序员,对编程语言的期待都不一样。而且,随着软件系统的复杂度和社会分工的提升,未来的使用者只会提出更多、更细致的要求:有的人希望语言更安全,有的人希望语言更易懂,有的人期待语言能天然支持分布式和并发,还有的人甚至希望机器学习模型可以直接融入语言工具链本身。

要回应这些不断涌现的新需求,仅仅依赖现有的几门主流语言是远远不够的。编程语言和编译器的研究与演进,只会变得更加丰富,而不会“失去意义”。
AI 时代下的编程语言
当我们看清了现实世界中:一方面硬件和运行环境急剧丰富,另一方面使用者的需求不断分化,这时再把视角转向 AI,就会看到一个有趣的事实:
AI 既不会让编译器和编程语言“过时”,反而会成为推动新语言和新编译器诞生的强大工具。
在没有 AI 的时代,从零开始实现一门编程语言,往往是一件耗时耗力的工程。即便是有扎实基础的学生,系统学习了多年编译原理和类型系统的理论之后,才有可能勉强动手实现一个“勉强能用”的语言原型。
而在 AI 时代,这个过程发生了质变。只要你掌握了必要的理论基础,懂得如何与 AI 工具有效协作,你就可以把很多机械性的工作(例如样板代码的书写、重复性的测试用例构造、某些模式化的代码转换等)交给 AI 来完成。这样一来:
- 你可以把更多精力放在“语言应该长什么样”“抽象应该如何设计”这些真正关键的问题上;
- 在面对编译错误、运行时崩溃、边界条件时,AI 可以帮助你更快地定位和修复问题;
- 甚至在探索新的 IR(中间表示)、新的优化策略时,AI 也可以提供有价值的实验和参考。
于是,原本需要几年时间才能“勉强试一试”的自制语言项目,如今对于一个有基础的开发者来说,变成了可以在数月甚至数周内完成的实践项目。
从这个角度看,AI 的到来,并不是让人类程序员“失业”,而是让更多程序员有机会站到更高的抽象层次上,去思考: “我真正需要怎样的一门语言?” ,以及 “我可以为自己的芯片、自己的业务场景,定制怎样的一套语言和工具链?”
在我看来,MoonBit 恰好具备了一系列非常有趣的特性,使得它非常适合作为“实现新编程语言的语言”,也非常容易与 AI 协作。在本书的后续章节中,当我们用 MoonBit 构造出 MiniMoonBit 编译器、并用它实现光线追踪、Lisp 解释器和神经网络训练时,你会逐渐体会到 MoonBit 在语法、类型系统、模块化和工具链上的优势,以及这些优势是如何帮助我们更轻松地理解和实现语言特性的。
这本书要讲些什么
在这本书中,我们会为读者展示一种 MiniMoonBit 语言的完整构造过程。它不是一个“玩具”级别的示例,而是一个足以支撑中等规模项目的语言子集。
我们将使用 MoonBit 语言,从最基础的 词法分析 开始,一路构建到:
- 语法分析:把一串个字符和记号,组织成带有结构的抽象语法树(AST);
- 类型检查与解糖:在保证程序类型安全的同时,把语法糖形式转换成更核心的语言内核;
- K Normal Form(K 正规形式) :把表达式规范化,为后续优化和代码生成奠定基础;
- LLVM IR:把程序翻译成 LLVM 的中间表示,以便利用成熟的优化和后端;
- CIR:设计一种类似于 C 语言的中间表示,从 KNF 形式转换而来;
- Machine IR(机器中间表示) :一种架构无关的机器级 IR,用来桥接高级 IR 和具体硬件指令;
- RISC-V64 汇编生成:最终把程序翻译成可以在 RISC-V64 架构上运行的汇编代码。
在这个过程中,我们会逐步实现并讲解如下关键主题:
- 类型系统与双向类型检查:通过示例理解什么是“从上往下推断”“从下往上检查”,以及它们如何结合。
- 泛型与多态:如何在 MiniMoonBit 中表达类型参数,并在编译阶段正确处理它们。
- 模式匹配:用更声明式的方式处理数据结构,代替繁琐的分支语句。
- 闭包与闭包转换:把带有捕获环境的函数,转换成在底层可以高效实现的数据结构和调用约定。
- 寄存器分配:在有限数量的硬件寄存器中,如何尽量减少溢出到栈上的次数。
- 中间表示上的优化:通过简单但实用的优化例子,理解编译器“聪明”起来的大致路径。
当我们的 MiniMoonBit 编译器基本成型之后,我们不会就此打住,而是会进入更有趣的一部分:用自己实现的语言去“做点真正的事情”。
我们会用 MiniMoonBit 实现三个中等规模的项目:
- 光线追踪(Ray Tracing) :从几何建模到光线与物体的相交计算,体会高性能数值运算与语言设计的关系。
- Lisp 解释器:用一门语言实现另一门语言,理解解释器、抽象语法以及求值策略。
- 神经网络训练与推理(MNIST 手写数字识别) :实现一个简单但完整的神经网络训练与推理流程,在 MiniMoonBit 中感受“从 Token 到 Tensor”的全链路。
通过这三个项目,你不仅会看到前面章节中构建的编译器组件在真实场景中的用法,也会对“语言设计如何影响工程实践”有更加直观的认识。
阅读这本书需要的前置知识
读这本书,你不需要有任何编译器实现的经验,也不需要提前掌握类型论、范畴论等抽象数学理论。我们会在需要的时候,尽量用直观的例子和图示来解释概念,而不是堆砌公式。
如果你具备以下这些背景,会读得更加轻松:
- 有一定的编程经验:熟悉至少一门主流语言(如 C/C++、Rust、Go、Java、Python 或 MoonBit 等),知道什么是变量、函数、循环、条件、数组、结构体等基本概念。
- 了解基础的数据结构和算法:例如链表、树、图、栈、队列,以及基本的排序和查找算法。这些知识会帮助你更自然地理解语法树、符号表等结构。
- 不害怕阅读和运行代码:本书会配套完整的代码仓库,你可以边读边运行、边修改。把这本书当成一本“带着你做项目的教科书”,而不仅仅是理论讲义。
当然,即便你暂时对其中某些前置知识不够熟悉,也不必担心。我们会尽可能在关键的地方回顾所需的基础,并给出进一步阅读的建议。你可以把这本书当成一次循序渐进的旅程:一开始也许会有点陌生,但随着你一步步亲手构造出自己的 MiniMoonBit 编译器,你会发现,原来“编译器”并不是遥不可及的黑魔法,而是由许多可以理解、可以推导、可以实践的小部件有机组合而成的系统。