# 括号中的革命:当两个程序员决定把函数变成“公民”
1975年,麻省理工学院人工智能实验室的走廊里弥漫着咖啡和打印纸的气味。Guy Steele和Gerald Sussman正面临一个几乎不可能的任务:如何让Lisp——这个已经存在了15年的编程语言——既能写出优雅的编译器,又能清晰地表达计算机科学最深刻的思想?他们手中握着的,不仅是一篇论文的草稿,更是一枚可能炸毁整个编程范式围墙的炸弹。当时的Lisp依赖动态作用域,函数只是二等公民,而他们偏要逆流而上,用极简的内核和词法作用域重新定义“计算”的本质。这场始于黑板上的争论,最终催生了Scheme——一门语言,一个宣言,一场持续至今的思想风暴。
## 黑与白的战争:动态作用域的诅咒
1970年代初的Lisp世界,是一个被动态作用域统治的丛林。想象一下:你写了一个函数`(lambda (x) (+ x y))`,当这个函数被调用时,变量`y`的值取决于调用时的环境,而非定义时的环境。这就像你写了一封信,收件人地址却随着信件到达的邮局而改变——混乱、不可预测、充满陷阱。当时的MacLisp和Interlisp两大阵营,正深陷这种“动态地狱”中无法自拔。
Guy Steele,一个刚从哈佛毕业的数学天才,在MIT的AI实验室里被分配去写一个Lisp编译器。他遇到了一个棘手的问题:如何优化函数调用?动态作用域使得编译器几乎无法进行任何静态分析——因为一个变量的绑定可能在运行时被任意改变。与此同时,Gerald Sussman,一位正在用Lisp做符号推理的教授,发现他的学生经常被动态作用域的诡异行为折磨:“为什么这个函数在顶层运行正常,在另一个函数内部就崩溃了?”
两人的相遇源于一个共同的愤怒:Lisp本该是“可计算的λ演算”,但动态作用域彻底背叛了这一理想。阿隆佐·邱奇在1930年代提出的λ演算,核心就是词法作用域——函数定义时的环境决定了变量的意义。而Lisp的实践却背道而驰。Steele在回忆中写道:“我们意识到,为了让Lisp真正成为λ演算的实用化身,我们必须砍掉动态作用域这颗毒瘤。”
于是,一个激进的计划浮现:创造一个“最小化”的Lisp,内核小到可以用几十行代码实现,但拥有完整的词法作用域、一流的函数和尾递归优化。Sussman在办公室的黑板上画了一个圆圈:“这个圆圈里只放最核心的东西——lambda、define、if、cons、car、cdr。其他一切都是语法糖。”Steele则在一旁补充:“以及,函数必须是一等公民——它们能被创建、传递、返回,就像数字和字符串一样。”
## 纸上的革命:《Lambda: The Ultimate Imperative》
1975年深秋,MIT的AI实验室里,Steele和Sussman正在进行一场持续数周的“白板马拉松”。他们面前是一个更深刻的挑战:如何用这个最小内核证明,编程语言的几乎所有高级特性——循环、异常、甚至面向对象——都可以通过lambda表达式“模拟”出来?
一天晚上,Steele在白板上写下了一段代码:
```scheme
(define (factorial n)
(if (= n 0)
1
(* n (factorial (- n 1)))))
```
Sussman指着递归调用说:“这里的问题是,每次递归调用都会消耗栈空间。如果我们能证明所有递归都可以转化为尾递归,并且由编译器自动优化为迭代,那么Scheme就能支持无限深的递归。”
Steele兴奋地敲着桌子:“没错!用lambda实现continuation,把控制流变成数据流。这样,我们不需要任何特殊的循环语句,只需要lambda和递归。”
这场争论的最高潮,发生在他们撰写那篇著名论文《Lambda: The Ultimate Imperative》时。论文的标题本身就是一种挑衅:“终极命令”——lambda表达式才是真正的命令,而不是什么goto或for循环。他们用Scheme演示了如何用lambda实现赋值、顺序执行、条件分支——所有传统的“命令式”特性,都可以被λ演算优雅地吞噬。
然而,真正的技术突破并非仅仅是理论上的。Steele和Sussman设计了一个极其简洁的“核心Scheme”,只有7种基本形式。他们证明了,只要有了lambda、define、if、set!、begin、quote和宏,就可以构建出整个语言的其余部分。宏系统尤其关键——它允许程序员在不修改语言内核的情况下,创造出自己的控制结构。这就像给了程序员一把“语言乐高”,他们可以拼出任何想要的语法。
1975年12月,当论文最终发表时,整个Lisp社区震动了。有人欢呼这是“Lisp的净化”,也有人怒斥这是“背叛传统”。Interlisp阵营的一位资深开发者甚至写信给Steele:“你们把Lisp变成了一个学术玩具,它失去了实用性。”但Steele和Sussman毫不动摇。他们知道,自己创造的不仅仅是一门语言,更是一种思考计算的方式——一种将复杂性压缩到极致的哲学。
## 从象牙塔到教育圣殿
Scheme的发布并没有立刻引发商业革命。相反,它像一颗种子,在学术土壤中静静地生长。1980年代,MIT的计算机科学课程(著名的6.001)开始使用Scheme作为教学语言。哈罗德·阿贝尔森和杰拉尔德·萨斯曼(Sussman本人)合著的《计算机程序的构造与解释》(SICP)成了无数程序员的“圣经”。这本书用Scheme教会了一代人如何将编程视为“抽象的艺术”:如何用高阶函数封装模式,如何用流处理模拟无限序列,如何用元循环求值器理解语言本身。
Scheme的极简主义产生了深远的技术影响。它的词法作用域和闭包机制,直接启发了Common Lisp、JavaScript、Python、Ruby等语言。JavaScript的创始人布兰登·艾克曾承认:“Scheme的闭包和一等函数,是JavaScript设计的核心灵感。”更讽刺的是,JavaScript最初被设计为“Java的脚本语言”,但它的灵魂却来自Scheme——那个只有几十个原语的极小语言。
Scheme的宏系统也开创了“语言导向编程”的先河。Racket(Scheme的一个方言)甚至允许用户创建自己的语言——你可以在Racket中定义一个“Python语法”或“C语法”,然后编写代码。这种“语言即库”的思想,后来被Rust和Julia等现代语言继承。
但Scheme最伟大的遗产,可能是它对“简单性”的信仰。在软件复杂度爆炸的今天,Scheme用事实告诉我们:一个足够强大的核心,比一堆功能堆砌的庞然大物更有力量。正如Sussman在SICP中写的:“计算机科学不是关于计算机,而是关于如何设计——如何设计复杂系统,使其在保持优雅的同时,能够应对变化。”
## 评论
Scheme的故事,本质上是一场关于“权力下放”的革命。它没有试图提供所有功能,而是给了程序员制造功能的能力。这种哲学在商业史上极为罕见——大多数语言追求“开箱即用”的便利,Scheme却坚持“最小内核+可扩展性”。这导致它在商业上几乎失败,却在教育、研究和语言设计中取得了不朽的胜利。Scheme提醒我们:技术的真正突破,有时不是发明更多的东西,而是学会如何减少。在今天这个“框架满天飞、依赖项堆积如山”的时代,Scheme的极简主义像一剂清醒药——它问我们:你的代码里,有多少是真正必要的?又有多少只是因为你没有勇气用lambda去解决问题?这种追问,比任何性能优化都更接近软件的本质。
## 参考资料
- [Guy Steele和Gerald Sussman的原始论文《Lambda: The Ultimate Imperative》](https://dspace.mit.edu/handle/1721.1/5790) — MIT图书馆存档,Scheme诞生宣言
- [Scheme编程语言官方网站](https://www.scheme.org/) — 包含历史文档、实现列表和社区资源
- [《计算机程序的构造与解释》(SICP)](https://mitpress.mit.edu/sites/default/files/sicp/index.html) — MIT出版社官方页面,Scheme教学圣经
- [Guy Steele关于Scheme起源的回忆](https://www.youtube.com/watch?v=6cA-N7k3GXU) — 2015年Strange Loop大会演讲视频
- [Wikipedia: Scheme (programming language)](https://en.wikipedia.org/wiki/Scheme_(programming_language)) — 详细历史和技术概述
1975年,麻省理工学院人工智能实验室的走廊里弥漫着咖啡和打印纸的气味。Guy Steele和Gerald Sussman正面临一个几乎不可能的任务:如何让Lisp——这个已经存在了15年的编程语言——既能写出优雅的编译器,又能清晰地表达计算机科学最深刻的思想?他们手中握着的,不仅是一篇论文的草稿,
发布于 2026/7/4