Lisp的强大之处
原文: https://www.paulgraham.com/diff.html
2001年12月(2002年5月修订)
(本文源于对LL1邮件列表上一些问题的回应,现已被收录于《书呆子的复仇》之中。)
当麦卡锡在20世纪50年代末设计Lisp时,它是对现有语言的彻底革新,其中最重要的当属Fortran。
Lisp体现了九项新思想:
1. 条件语句。 条件即if-then-else结构。如今我们对它们习以为常,但它们是由麦卡锡在开发Lisp的过程中发明的。(当时的Fortran仅有条件goto,紧密基于底层硬件中的分支指令。)麦卡锡是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与其他语言所采用的随意变通方式的差异来描述它对当今程序员很有用,但这可能并非麦卡锡当年思考它的方式。Lisp并非为修复Fortran的错误而设计;它更像是试图将计算公理化的一个副产品。