有一句古话,叫做“国王已死,国王万岁!”它的意思是,老国王已经死去,国王的儿子现在继位。这句话的幽默,就在于这两个“国王”其实指的不是同一个人,而你咋一看还以为它自相矛盾。今天我的话题仿效了这句话,叫做“Lisp 已死,Lisp 万岁!”希望到最后你会明白这是什么意思。
首先,我想总结一下 Lisp 的优点。你也许已经知道,Lisp 身上最重要的一些优点,其实已经“遗传”到了几乎每种流行的语言身上(Java,C#,JavaScript,Python, Ruby,Haskell,……)。由于我已经在其他博文里详细的叙述过其中一些,所以现在只把这些 Lisp 的优点简单列出来(关键部分加了链接):
Lisp 的语法是世界上最精炼,最美观,也是语法分析起来最高效的语法。这是 Lisp 独一无二的,其他语言都没有的优点。有些人喜欢设计看起来很炫的语法,其实都是自找麻烦。为什么这么说呢,请参考这篇《谈语法》。
Lisp 是第一个可以在程序的任何位置定义函数,并且可以把函数作为值传递的语言。这样的设计使得它的表达能力非常强大。这种理念被 Python,JavaScript,Ruby 等语言所借鉴。
Lisp 有世界上最强大的宏系统(macro system)。这种宏系统的表达力几乎达到了理论所允许的极限。如果你只见过 C 语言的“宏”,那我可以告诉你它是完全没法跟 Lisp 的宏系统相提并论的。
Lisp 是世界上第一个使用垃圾回收(garbage collection)的语言。这种超前的理念,后来被 Java,C# 等语言借鉴。
想不到吧,现代语言的很多优点,其实都是来自于 Lisp — 世界上第二古老的程序语言。所以有人才会说,每一种现代语言都在朝着 Lisp 的方向“进化”。如果你相信了这话,也许就会疑惑,为什么 Lisp 今天没有成为主流,为什么 Lisp Machine 会被 Unix 打败。其实除了商业原因之外,还有技术上的问题。
早期的 Lisp 其实普遍存在一个非常严重的问题:它使用 dynamic scoping。所谓 dynamic scoping 就是说,如果你的函数定义里面有“自由变量”,那么这个自由变量的值,会随着函数的“调用位置”的不同而发生变化。
比如下面我定义一个函数 f,它接受一个参数 y,然后返回 x 和 y 的积。
(setq f
(let ((x 1))
(lambda (y) (* x y))))