1.1 Scheme ¶
Scheme 是 Lisp 编程语言的一种方言,由 Guy
Lewis Steele Jr. 和 Gerald Jay Sussman 于 20 世纪 70 年代中期发明。第一个
Lisp 语言是 LISP,由 John McCarthy 于
20 世纪 50 年代后半期创建。
Lisp 的语法使用 S-表达式,它们由 McCarthy 创建,
用于在他的 eval 函数中将 LISP 函数表示为数据
(发表于 1960 年)。Eval 是一个只有一页的通用 LISP 函数,
能够运行任何其他 LISP 函数。第一个 LISP 解释器
是 Steve Russell 手工编译 eval 函数时
创建的。
今天,许多语言提供了 Lisp 的大量子集特性,
有些甚至试图提供 Scheme 的特性。S-表达式没有被加入
是因为那会使这些语言变成 Lisp。
Lisp 语言的显著特性包括动态类型、垃圾回收
和 S-表达式("符号表达式")。Scheme 在
这些基础上增加了一些额外的显著特性:静态作用域、
正确的尾递归、卫生宏、完整续延和
dynamic-wind。
- 动态类型意味着程序员不需要向编译器证明
程序是类型良好的。这是有益的,因为
存在一些关于程序的真命题,除非运行程序,
否则无法证明。
- 垃圾回收是自动回收未使用的内存。这是有益的,
因为没有它,有些程序就无法编写。
- S-表达式提供了一种将代码表示为数据的便捷方式,
采用 Lisp 程序员习惯使用的格式,这使得
可以通过宏进行自动变换和通过 eval 进行动态求值。
这是有益的,因为没有动态代码求值,
有些程序就无法编写。它也使程序员
不必猜测解析器将如何分解代码。
- 静态作用域意味着变量的使用与变量在
程序文本中出现的绑定相关联。这在
今天的大多数语言中都是标准做法,但 Scheme 是少数
始终正确做到这一点的语言之一。(如果需要动态作用域,
可以通过 dynamic-wind 获得,因为有些
程序没有它就無法编写)。
- 正确的尾递归提供了一个保证:当过程调用
出现在 尾上下文 中时(例如在过程的
最后一个表达式中),不会使用额外的栈帧。这是有益的,
因为没有正确的尾递归,有些程序
就无法编写。
- 卫生宏意味着一个库可以提供对
Scheme 语法的扩展,这些扩展与内置的 Scheme 语法
无法区分,而卫生性意味着这些宏不会意外地破坏
静态作用域。功能较弱的宏系统容易出现插入
与程序中已使用的变量冲突的变量。
- (续延通常以极其抽象的术语解释,所以
我在这里会非常具体)。续延是栈的副本,
以及与 dynamic-wind 相关的状态。许多
语言提供了在栈中向上逃逸的方法(例如 longjmp
或异常),但续延还允许程序将栈恢复到
捕获续延时的状态。想象一下抛出异常
从而向上移动栈,然后异常处理程序修复问题
再回到栈的下方继续执行。Scheme 还不同寻常地允许
续延被多次恢复。续延是有用的,因为
没有它们,有些程序就无法编写。任何控制结构
都可以用它们表达,甚至是那些没有内置于语言中的结构。
- Dynamic-wind 提供了一种在程序的某个部分被进入
然后退出时运行代码的方法。续延意味着
这可以发生多次。这个特性是有用的,因为
没有它,有些程序就无法编写。
上述特性可以在缺乏它们的语言中被模拟。
尾递归可以通过用列表模拟栈来手动实现,
动态类型可以通过抽象数据类型和类型分派来实现,
垃圾回收可以为某个数据结构实现,
而续延可以通过续延传递风格来手动实现。
从任何图灵完备的语言都可以实现任何其他图灵完备的
语言的意义上来说,这样的模拟总是可能的。
但这些模拟不会存在于与宿主语言相同的
层次上,因此本质上更低劣。