10 扩展阅读

这里的一些阅读材料教会,影响,或者启发了我们创作本书。希望你能像我们一样,至少喜 欢其中的一部分。

不熟悉递归编程和符号计算的读者可以看看 The Little Schemer (Friedman & Felleisen, 1996),或 The Little MLer (Felleisen & Friedman, 1996),或者有考据癖,看看 The Little LISPer (Friedman, 1974)。作为计算第一课, How to Design Programs (Felleisen et al., 2001) 深入探讨了如 何递归编程。

用归纳法定义集合和关系,是数理逻辑中久已存在的技术。我们的自底向上和推理规则式归 纳大致效仿 Plotkin (1975, 1981) 的工作。我们的“自顶向下 ”式归纳效仿另一种技术,名为余归纳 (coinduction) (参见Gordon, 1995; Jacobs & Rutten, 1997),Felleisen et al. (2001) 也使用了这种技术。

上下文无关语法是语言学和计算机科学的标准工具。大多数编译器书籍,比如 Aho et al. (2006),都对语法和解析算法进行了大篇幅的讨论。 将具体语法和抽象语法分开的思想通常归功于McCarthy (1962)。他强调用接 口抽象语法树。

我们的口号遵循语法基于结构化归纳法,由 Burstall (1969) 提出。即使过程没有遵循语法子目标归纳 (subgoal induction) (Morris & Wegbreit, 1977) 仍是证明递归过程正确性的有效 方法。过程的可能输入受不变式约束时,子目标归纳也有效。

泛化 (generalization) 是源自数学的标准技术,常用来证明某个特定陈述是某个 更通用陈述的特例。我们把额外参数描述为上下文的抽象,是受到属性语法 (Knuth, 1968)中的继承属性 启发。

我们的构造器 define-datatypecases 是受 ML 的 datatype 和模式 匹配工具启发,详见 Milner et al. (1989) 及其修订版 Milner et al. (1997)。

Lambda 演算由邱奇发明 (Church, 1941),用于研究数理逻辑,但已成为诸 多现代编程语言理论的灵感来源。Lambda 演算的介绍参见 Hankin (1994)、 Peyton Jones (1987) 或 Stoy (1977)。 Barendregt (1981, 1991) 提供了百科全书式的参考。

那样的等深线用来解释词法作用域,首先由 Johnston (1971) 提出。无名解释器和翻译器基于德布鲁金索引 (de Bruijn, 1972)。

Scheme 由 Sussman & Steele (1975) 发明。其开发过程记录 在 Steele & Sussman (1978); Clinger et al. (1985a); Rees et al. (1986); Clinger et al. (1991); Kelsey et al. (1998)。Scheme 标准由 IEEE standard (IEEE, 1991) 和 \textit{Revised}^6 Report on the Algorithmic Language Scheme (Sperber et al., 2007) 制定。

Dybvig (2003) 简短介绍了 Scheme,加入了许多富有洞见的例子。

解释器思想至少能追溯到图灵,他定义了能够模拟任何图灵机的“通用 ”机器。这种通用机器实际上是一个解释器,取一套描述图灵机的编码,模 拟解码机器 (Turing, 1936)。经典的冯诺依曼机 (von Neumann, 1945) 同 样是硬件实现的解释器,用来解释机器语言程序。

对解释器的现代应用可追溯到 McCarthy (1960),他提出了自循环解释器 (metacircular interpreter)(用被定语言本身写就的解释器),用来解释 Lisp 的能力。 当然,这样的解释器带来一大难题:如果被定语言由自身定义,我们要理解语言的定义,就 要先理解这种语言。确实,即使解释器不是自循环的,也会面临同样的问题。读者理解被定 义的事物之前,仍需理解书写定义用的语言。

这些年来,大量技术用来解决这一难题。我们把解释器视为方程式规范的转写 (Goguen et al., 1977) 或者 Plotkin (1975, 1981) 式的大 步操作语义。这只需要非常直观的数学。

指称语义是另一种用数学定义语言的技术。这种方法将解释器替换为一个函数,该函数把每 个被定语言的程序翻译为定义其行为的数学对象。Plotkin (1977) 对这种技 术做了无可替代的介绍,Winskel (1993) 做了更宽泛的探讨。 Milne & Strachey (1976) 是本百科全书,研究了如何用这种 技术建模大量语言特性,

另一种方式是写出被定语言子集的解释器。例如,状态的几个解释器依靠 Scheme 的存储器来解释存储器的概念,但它们只用了一个全局可变对象,而不是 Scheme 可变变量的所有能力。

将计算视为操作存储器的思想可追溯到现代计算(参见 von Neumann, 1945) 的开端。EXPLICIT-REFS 的设计基于 ML (Milner et al., 1989) 的存储器 模型,而后者与 Bliss (Wulf, 1971) 类似。IMPLICIT-REFS 的设计类似于 大多数具有可变局部值的标准编程语言,诸如 Pascal、Scheme 和 Java。

术语“左值”和“右值”,以及 内存的环境-存储器模型源自 Strachey (1967)。

Fortran (Backus et al., 1957) 是第一种使用按指调用的语言,Algol 60 (Naur et al., 1963) 是第一种使用按名调用的语言。 Friedman & Wise (1976) 较早介绍了全面使用懒求值的威力。 Haskell (Hudak et al., 1990) 是第一种使用按需调用的实际语言。为了建 模按名调用,Ingerman (1961) 发明了值箱 (thunk)。我们用它们 和效果建模按需调用。这与助记法 (memoization) (Michie, 1968) 类似。

MonadsMoggi (1991) 提出,因 Wadler (1992) 流行。它提供了编程语言效果的通用模型。在函数式语言 Haskell (Peyton Jones, 2001) 中,monads 提供了非函数式行为的组织原则。

续文由多人独立发现,Reynolds (1993) 介绍了这一迷人历史。 Strachey & Wadsworth (1974) 或许是其中影响最大的。 Reynolds (1972) 将一个自循环解释器做了 CPS 变换,并展示了这样做如何 避免自循环的某些问题。将尾式程序翻译为命令式可追溯到 McCarthy (1962),Abelson & Sussman (1985, 1996) 强调了将其作为 一种编程技术的重要性。

Plotkin (1975) 给出了相当清晰的 CPS 变换,发现了它的理论性质。 Fischer (1972) 提出了非常类似的变换。Wand (1980b) 率先 探讨了续文和累加器之间的联系,像写出续文传递风格的程序结尾的例子 fact 那样。

在程序中直接使用续文的思想源自于 Landin (1965a) (另见 Landin 1965b),在 Lisp 和早期版本的 Scheme (Steele & Sussman, 1978) 中广泛使用。我们的 letcc 基于 Scheme 的 call-with-current-continuation,始见于 Clinger et al. (1985b)。

Wand (1980a) 展示了如何用续文建模轻量级进程或线程。续文用途广泛,远 超本书讨论范围,如协程 (coroutine) (Haynes et al., 1986)。

我们对线程的讨论模拟了 POSIX 线程 (例如,参见 Lewis & Berg, 1998)。 基于 Erlang 的消息传递并发模型 (Armstrong, 2007)。

Steele 的 RABBIT 编译器 (Steele, 1978) 以 CPS 变换为基 础。这一编译器首先对源程序做 CPS 变换,然后以数据结构表示续文。得出的程序像我们 的寄存程序一样,很容易编译。这条线发展出了 ORBIT 编译器 (Kranz et al., 1986) 和 Standard ML of New Jersey 编译器 (Appel & Jim, 1989)。

续文传递风格的 CPS 算法基于 Danvy & Nielsen (2003) 提 出的一阶组合式算法。CPS 翻译历史悠久,包括 Sabry & Wadler (1997),他们改进了 Sabry & Felleisen (1993),而后者又是受本书初版第 8 章的 CPS 算法启发。 基于 Danvy & Filinski (1992) 提 出的高阶组合式 CPS 算法。CPS 之外还有 A-normal form(),由 Sabry & Felleisen (1992); Flanagan et al. (1993) 提出。

当前大多数关于有类型编程语言的工作都能追溯到 Milner (1978),他在 ML 中引入了类型,作为保证计算机生成证明可靠性的工具。Ullman (1998) 对 此做了精辟的介绍。更多讨论参见 Felleisen & Friedman (1996),另见 Paulson (1996); Smith (2006)。

人们多次发现了类型推导。标准参考书是 Hindley (1969),但 Hindley 提到,Curry 在 1950 年代已经知道 了这些结论。Morris (1968) 也提出了类型推导,但在Milner 1978 年的论文发表之前,类型推导从未广泛应用。

Wand (1987) 率先阐明了如何将类型推导分为方程构建和求解。名为 Hindley-Milner 多态的 Milner (1978) 系统与 中 的系统基本相同。Pierce (2002, 2004) 的两卷著作对类型做了百科全书式 的讨论。

广为论述的数据抽象思想是 1970 年代的一大创举。这里我们仅仅提及 Parnas (1972),他强调了以接口作为信息隐藏边界的重要性。数据类型的实 现是满足该类型定义的任意值和操作的集合。Goguen et al. (1977) 证明, 任意数据类型都能以树的集合实现,树中记录了值如何构建,且从一个树的集合到该数据类 型另一实现的集合具有唯一映射。相应地,任意数据类型都能以过程表示法实现,且从该数 据类型的任何其他实现到过程表示法都有唯一映射 (Giarratana et al., 1976; Wand, 1979; Kamin, 1980)。

用类型强化数据抽象始见于 Reynolds (1975),类型应用于 CLU (Liskov et al., 1977)。这发展为 Standard ML (Milner et al., 1989) (另见 Paulson, 1996; Ullman, 1998)的模块 系统。我们的模块系统基于 Leroy (1994),它应用于 CAML(参见 Smith, 2006),而这是 ML 的另一变体。

通常视 Simula 67 (Birtwistle et al., 1973) 为第一种面向对象语言。面 向对象的类比由 Smalltalk (Goldberg & Robson, 1983) 和 Actors (Hewitt, 1977) 扩充。二者都使用人类互动以及收发消息的类比来 解释他们的思想。Sussman 和 Steele 原打算用 Scheme 理解 Hewitt 的 actor 模型,但 Scheme 超越其原意。Abelson & Sussman (1985, 1996) 和 Springer & Friedman (1989) 给出了更多用 Scheme 进行面向对象编程的例子,讨论了 函数式编程和命令式编程在哪些时候最适合。Steele (1990) 和 Kiczales et al. (1991) 描述了 Common Lisp 中强大的面向对象编程组件 CLOS。

对象和类的语言基于 Java 的对象模型。Java 的标准参考书是 Arnold & Gosling (1998),有意精研的读者可以阅读其规范 Gosling et al. (1996)。

Ruby (参见 Thomas et al., 2005)、Python (van Rossum & Drake, 2006)和 Perl(Wall et al., 2000; Dominus, 2005)是具有对象和过程的无类型语言,大致类似我们的 CLASSES。 C# 是一种带类型的语言,相较 Java 添加了很多特性,最著名的是与过程类似 的委托 (delegates),它还允许程序员指定某些调用应该是尾调用。

Abadi & Cardelli (1996) 定义了一种简单的对象演算,为面 向对象系统中的类型研究奠定了基础。Flatt et al. (1998) 形式化了 Java 的一个子集。另一有用的子集是 Featherweight Java (Igarashi et al., 1999)。

Gamma et al. (1995) 编写了一本备受关注的手册,专论编写面向对象程序 的有效组织原则。

ACM 于 1978 年 (Wexelblatt, 1978)、1996 年 (Bergin & Gibson, 1996) 和 2007 年(Hailpern, 2007) 三次举办会议, 专门讨论编程语言的历史。这些会议囊括了讨论各种编程语言历史的论文。IEEE Annals of the History of Computing 囊括了介绍方方面面计算历史的学术性文章,也包括编程语言。 Knuth & Pardo (1977) 介绍了初期编程语言的迷人历史。

不计其数的会议在汇报编程语言的新进展。至少就本书讨论的话题来说,三个顶级会议是 ACM Symposium on Principles of Programming Languages (POPL)、ACM SIGPLAN International Conference on Functional Programming (ICFP) 和 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI)。编 程语言的主要学术期刊包括 ACM Transactions on Programming Languages and SystemsJournal of Functional ProgrammingHigher-Order and Symbolic Computation。除此之外,还有些网站专注于编程语言的方方面面。