Racket 友好入门

查看原文 HN 讨论

文章摘要

这是 geometridae 于 2026 年 8 月 11 日发在 Bear Blog 上的一篇 Racket 速成教程,开篇引用了 Eric S. Raymond 那句名言:「Lisp 值得一学,因为你终于领悟它时会获得的那种深刻的启蒙体验。」作者给全文定的目标很明确:读完之后,你将写出属于你自己的语法。

第一部分是历史。 Lisp 诞生于 1958 年,由 John McCarthy 在 MIT 发明,是目前仍在使用的第二古老的高级语言(只比 1957 年的 Fortran 晚一年),比 Python(1991)和 JavaScript(1995)早了三十多年。作者列举了一串「我们今天视为现代的东西其实生于 Lisp」:垃圾回收、一等函数、REPL(Python、Node、Julia 今天都有的交互式读取-求值-打印循环)、把条件语句当作有返回值的表达式,以及最关键的同像性(homoiconicity)——代码本身就是这门语言的一种数据结构。文章还提到 70、80 年代 Symbolics 和 LMI 制造过专门跑 Lisp 的物理机器(Lisp Machine),AI 寒冬后经费枯竭,Lisp 从明星沦为小众信仰。作者的评论是:「有趣的想法不会死,它们只是变异了——这也是 Lisp 系语言之美的一部分。」

第二部分是从 Lisp 到 Scheme 再到 Racket 的谱系。 1975 年 Gerald Sussman 与 Guy Steele 创造了极简、优雅、近乎数学化的 Scheme,成为学界最爱的教学语言(SICP 就是用 Scheme 写的)。1995 年 Matthias Felleisen 的团队做出面向教育与语言研究的 PLT Scheme,2010 年更名为 Racket。今天的 Racket 已经远不止是一个 Scheme,而是一门用来造语言的语言,其非官方口号是「面向语言的编程」(language-oriented programming):如果你的问题需要一门自己的语言,Racket 让你在一个下午之内造出来。作者附了一张时间线表格(1958 Lisp / 1975 Scheme / 1984 Common Lisp 标准化 / 1995 PLT Scheme / 2007 Clojure / 2010 更名 Racket / 今天:正准备写括号的你)。

第三部分回答「今天谁还在用 Lisp」: Clojure 跑在银行、航空公司和创业公司的生产环境(拉美最大数字银行 Nubank 就建在 Clojure 上);Common Lisp(SBCL)仍活在专家系统、航班规划(被 Google 收购的 ITA Software 驱动了 Google Flights)和科学计算里;Emacs Lisp 让数百万人每天都在跑 Lisp 而不自知;Guile/Guix 是一整个 100% 用 Scheme 配置的 Linux 发行版;Racket 自己有年度大会 RacketCon,被用于语言研究、形式化验证(Rosette)、排版与出版(Pollen)和教育。此外新的 Lisp 还在不断出现:编译到 Lua 的 Fennel、Janet、构建在 Python 之上的 Hy。作者还埋了个彩蛋:动画《The Amazing Digital Circus》第 8 集里 Kinger 打开终端想重置 Caine 时,可以看到这个 1996 年造出来的创意 AI 是用 Lisp 写的,文件名就叫 Caine-core.lisp

第四部分是实操。 安装只需五分钟:去 racket-lang.org 下载安装包,打开自带的 DrRacket(上半区写定义、下半区是 REPL),在第一行写 #lang racket 告诉 Racket 你用哪门语言——因为 Racket 是个语言工厂,你必须挑一门。命令行党用 racket 进 REPL、用 raco 做包管理。

作者随后用几组极短的例子铺完了核心:

> (+ 1 2)                      ; => 3
> (* 3 (+ 2 2))                ; => 12
> (string-append "hello " "world")

他把 Lisp 的规则浓缩成一行:永远是 (操作符 参数1 参数2 ...),没有例外。 没有运算符优先级要背,也没有任何东西的特殊语法。那些一开始吓人的括号,其实恰恰是「完全没有任意规则」的体现——一周之后你就看不见它们了。

接下来是定义与函数(define 带名字创建常量,带 (名字 参数...) 创建函数,; 开始注释,lambda 造匿名函数,作者顺手点出这就是 1930 年代 Church 的 lambda 演算),列表(list / '(1 2 3) / first / rest / cons / length,并特别提醒记住引号 ' 的含义:别求值,这是数据),高阶函数(map 变换、filter 筛选、foldl 累积——「有这三个函数,你不写一个 for 循环也能解决大部分列表问题」),以及递归。递归的思维方式被描述成「不是想『重复 N 次』,而是想『基线条件是什么,我怎么向它靠近』」。为了让它可视,作者给了个用 Racket 自带 2htdp/image 图形库画谢尔宾斯基三角形的例子,贴进 DrRacket 按 Run 就能看到图形出现。

大结局是「写代码的代码」。 引号把代码变成数据:'(+ 1 2) 得到的是列表而不是 3,(first '(+ 1 2)) 得到符号 +,而 (eval '(+ 1 2)) 又把数据当代码求值回 3。你的程序是一个列表,你能构造列表,因此你能用程序构造程序——这就是同像性,也是 Lisp 拥有真正宏(不是 C 那种文本宏,而是接收代码、返回代码、在一切运行之前执行的函数)的原因。作者用一个例子收尾:Racket 没有 while 循环?那就自己发明一个。

(define-syntax-rule (while condition body ...)
  (let loop ()
    (when condition
      body ...
      (loop))))

「你刚刚扩展了这门语言——在 Lisp 里,语法是你的。」文末引用 Alan Kay 把 Lisp 称为「软件的麦克斯韦方程组」:一个极小的内核,其余一切都能从中推导出来。推荐读物为 How to Design Programs、Racket Guide、Beautiful Racket 和 SICP。

HN 评论精华

这条帖子拿到 271 分、146 条评论。有意思的是,讨论几乎没有停留在教程内容上,而是迅速分裂成三个方向:Lisp 到底好在哪、Racket 的「无特殊语法」是不是谎话、以及为什么现实中没人用它