是什么让Lisp与众不同

2001年12月 (修订于2002年5月)

(本文源于LL1邮件列表上的一些问题。现在已并入《书呆子的复仇》。)

当McCarthy在20世纪50年代末设计Lisp时,它与当时的现有语言(其中最重要的是Fortran)截然不同。

Lisp体现了九个新思想:


1. 条件语句。 条件语句是if-then-else结构。我们现在认为这些是理所当然的。它们是由McCarthy在开发Lisp的过程中发明的。(当时的Fortran只有条件goto,它与底层硬件中的分支指令密切相关。)McCarthy当时在Algol委员会任职,他将条件语句引入了Algol,然后它们传播到大多数其他语言。

2. 函数类型。 在Lisp中,函数是一等公民——它们是一种数据类型,就像整数、字符串等一样,并且具有字面表示形式,可以存储在变量中,可以作为参数传递,等等。

3. 递归。 递归当然在Lisp之前就作为一种数学概念存在,但Lisp是第一个支持它的编程语言。(可以认为它隐含在将函数作为一等公民的过程中。)

4. 变量的新概念。 在Lisp中,所有变量实际上都是指针。值才具有类型,而不是变量,并且赋值或绑定变量意味着复制指针,而不是它们指向的内容。

5. 垃圾回收。

6. 由表达式组成的程序。 Lisp程序是表达式树,每个表达式都返回一个值。(在某些Lisp中,表达式可以返回多个值。)这与Fortran和大多数后续语言形成对比,后者区分表达式和语句。

在Fortran中自然会有这种区别,因为(毫不奇怪,在一种输入格式是穿孔卡的语言中)该语言是面向行的。您不能嵌套语句。因此,虽然您需要表达式来进行数学运算,但让其他任何东西返回值是没有意义的,因为不会有任何东西等待它。

这种限制随着块结构化语言的出现而消失,但那时已经太晚了。表达式和语句之间的区别已经根深蒂固。它从Fortran传播到Algol,然后传播到它们的后代。

当一种语言完全由表达式组成时,您可以随意组合表达式。您可以使用Arc语法说

(if foo (= x 1) (= x 2))

或者

(= x (if foo 1 2))

7. 符号类型。 符号与字符串的不同之处在于,您可以通过比较指针来测试相等性。

8. 使用符号树的代码表示法。

9. 整个语言始终可用。 读取时、编译时和运行时之间没有真正的区别。您可以在读取时编译或运行代码,在编译时读取或运行代码,以及在运行时读取或编译代码。

在读取时运行代码允许用户重新编程Lisp的语法;在编译时运行代码是宏的基础;在运行时编译是Lisp用作Emacs等程序中的扩展语言的基础;在运行时读取使程序能够使用s-表达式进行通信,这个想法最近被重新发明为XML。


当Lisp最初被发明时,所有这些想法都与普通的编程实践相去甚远,后者主要由20世纪50年代末可用的硬件决定。

随着时间的推移,默认语言(体现在一系列流行的语言中)已逐渐演变为Lisp。1-5现在已经很普遍。6开始出现在主流中。Python具有7的一种形式,尽管似乎没有任何语法可以实现它。8(与9一起)是使Lisp宏成为可能的原因,到目前为止,它仍然是Lisp独有的,也许是因为(a)它需要那些括号,或者同样糟糕的东西,并且(b)如果您添加了最终的权力增量,您将不再能够声称发明了一种新语言,而只能声称设计了一种新的Lisp方言; -)

虽然对当今的程序员有用,但用它与其他语言采用的随机权宜之计的差异来描述Lisp是很奇怪的。这可能不是McCarthy的想法。Lisp并非旨在修复Fortran中的错误;它更多地是试图公理化计算的副产品。