Racket 编程入门

第 6 部分 · Language Design

语言设计与计算模型

回答"语言本身是怎么造出来的"。宏、延续、generator、模块机制、惰性求值、DSL,最后亲手造一门 #lang 语言。

01

宏——让 Racket 语言自己长出来的方式

Racket 的宏不是黑魔法——它是一段在编译期运行的程序,吃进语法树,吐出新的语法树。理解了“树替换”这一件事,你就能让语言长出它本来没有的构造。

02

延续——Racket 里的时间旅行

延续(continuation)是“计算剩下要做的事”。`call/cc` 把它打包成一个可调用的值,你可以存起来、传出去、以后再跳回去。`return` 只能回一层,延续能回到任意一个被保存的未来——但这力量该怎么用,才不会反噬?

03

generator——用延续造出来的迭代器

Racket 的 generator 是一个能按暂停键的函数:你每叫它一次,它吐一个值,下次从上次停下的地方接着跑。它不是新机制,底下是用延续切开的一次函数调用。

04

语言声明与 require——Racket 的模块机制

`#lang` 决定一个文件用什么语言写,`require` 在这门语言里加载别的模块——新手最常把这两件事搞混。分清它们,Racket 的模块机制就不再是一团乱麻。

05

include——源码级引入,不是模块

`include` 把另一个文件的代码原样塞进当前文件,像粘贴,不像导入。它和 `require` 长得像,却活在完全不同的层面——一个走模块系统,一个只是文本拼接。

06

惰性求值——按需才算

Racket 默认急着把每个参数都算好;可你想表示“所有自然数”、或跳过那段永远用不上的计算时,急就急不来了。`delay`/`force` 把计算推迟到催账那一刻,`lazy` 和流让你装下一整条无穷的链。

07

造一个 DSL——Racket 让你设计自己的语言

SQL、CSS、正则、HTML,都是为某个领域量身定做的小语言。如果有一天你想要的那个小语言没人替你做,你能不能自己造一个?Racket 说能——而且不用从零写解析器。

08

写你自己的 lang——从内部 DSL 到一门独立语言

上一章你用“数据 + 解释函数 + 宏”造了一门住在 Racket 里的内部 DSL。这一章再往前走一步:造一门有自己 `#lang`、连源代码长什么样都由你定的独立语言。