"Lisp 不是一门语言,它是一种构建素材。" (艾伦·凯)
"任何C或Fortran程序复杂到一定程度之后,都会包含一个临时开发的、只有一半功能的、不完全符合规格的、到处都是bug的、运行速度很慢的Common Lisp实现。"(格林斯潘第十定律(Greenspun's Tenth Rule))
Clojure是一门Lisp方言(Lisp dialect).
Lisp 是一种编程语言,以表达性和功能强大著称,但人们通常认为它不太适合应用于一般情况。Clojure 是一种运行在 Java™ 平台上的 Lisp 方言,它的出现彻底改变了这一现状。如今,在任何具备 Java 虚拟机的地方,都可以利用 Lisp 的强大功能。
Clojure 是完全的,真正意义上的神圣的lisp语言的一个方言.
lisp语言因为其无以伦比强大能力和几乎无穷的表达力而获得了盛誉,Clojure自然也不例外. 它的功能和元编程的能力是建立在这样的基础之上的:异常驯服的C语言的"石头" 或 具有 延展性的java语言的"木头" . 你可以用几百行甚至几十行Clojure代码取替代几千行静态语言 的代码,伴随着这而来的是bug数量的减少和开发时间的缩短.
样板代码(Boilerplate code )被完全删去. 域指定语言(Domain Specific Languages ) 不仅 简单,而且更一般化--lisp程序往往是按照 "自下而上" 的开发方式写成的. 展开式(演进式)的 结构和语法更适合特定的问题领域. 你在程序运行的时候取修改程序,而不需要重新编译或重启 程序.
但是,历史上也有对lisp进行诋毁的人,或许称为抱怨更为合适. lisp发展过程中,没有完整的规范, 各种不兼容的实现,陈旧落伍的限制.cruft accumulate 在其存在的四五十年里一直存在. 对于 大多数人来说,它的语法过于诡异了.
Clojure 修正上面的大多数这些问题. 它保留了 lisp 的思想和哲学,并同时清除了过去的很多限制. Clojure 高速、干净、具有优先能力和优雅的特征. 但是没有改变lisp中 "代码也是数据" 的哲学. Clojure 语言在直觉和观感上比历史上的lisp更易于阅读. 在后面开始学习的初级阶段,你就发现虽然 仍有各种括号,但是代码是难以置信的容易读和写.
对于那些熟悉lisp语言的人来说,他们很快就会发现他们非常适应Clojure.
20世纪50年代中期,在大多数计算机处理的都是数值数据等,包括语言学、心理学和数学领域上一些人们开始对人工智能产生了兴趣。觉得必须实现共同需要的一个方法,使计算机能够处理链表中的符号数据,允许语言的处理、信息存入和检索、定理证明的过程机器化。IBM是首先对人工智能开发有兴趣的商业机构之一。
1958年夏天,来自麻省理工学院的人工智能研究先驱约翰·麦卡锡(John McCarthy)参与IBM资讯研究部的工作,研究符号运算及应用需求。可是,IBM旗下的Fortran表处理语言却未能支援符号运算的递归、条件表达式、动态存储分配及隐式回收等功能。约翰·麦卡锡于1958年秋季回到麻省理工学院后,和Marvin Minsky组成了人工智能项目。开展一个表处理软件系统来实现McCarthy提出建议采纳者程序的工作,尔后推动了表处理语言LISP的诞生。
1960年4月,麦卡锡在ACM杂志发表了一片文章《递回函数的符号表达式以及由机器运算的方式,第一部》.
自1960代末年至1980年初年,各种更新LISP版本涌现,有源自加利福尼亚大学伯克利分校的Franz Lisp、在AutoCAD运行的AutoLISP前身XLISP、犹他大学开展的Standard Lisp及Portable Standard Lisp、专属于Lisp机器上运行的ZetaLisp、源自法国国家信息与自动化研究所的LeLisp、以及MIT人工智能实验室的Gerald Sussman与Guy Steele所开发的Scheme等。 1984年,改良自MacLisp、集各版本大成、跨平台、且被目为事实标准的Common Lisp诞生。至1994年,美国国家标准学会(ANSI)对Common Lisp语言进行了标准化。
自稳定运行的Common Lisp出现起,再有各机构按各自所需而开展后续Lisp,包括1990年来自欧洲用户的EuLisp、运行于Java虚拟机的Clojure、受到Maclisp影响而创的Emacs Lisp、以及自由开源来自卡内基·梅隆大学的CMUCL、还有IsLisp,Racket,ACL2等蓬勃涌现。
自2000年起,LISP共享者合力支援的自由开源社区逐渐形成,致力于LISP后续发展。
当前最新潮的编程语言,只是实现了他在1958年的设想而已。
这怎么可能呢?计算机技术的发展,不是日新月异吗?1958年的技术,怎么可能超过今天的水平呢?
这是因为John McCarthy本来没打算把Lisp设计成编程语言,至少不是我们现在意义上的编程语言。他的原意只是想做一种理论演算,用更简洁的方式定义图灵机。
所以,为什么上个世纪50年代的编程语言,到现在还没有过时?简单说,因为这种语言本质上不是一种技术,而是数学。数学是不会过时的。
Lisp语言就好比是快速排序(Quicksort)算法,这种算法是1960年提出的,至今仍然是最快的通用排序方法。
Lisp语言诞生的时候,就包含了9种新思想。其中一些我们今天已经习以为常,另一些则刚刚在其他高级语言中出现,至今还有2种是Lisp独有的。按照被大众接受的程度,这9种思想依次是:
现在大家都觉得这是理所当然的,但是Fortran I就没有这个结构,它只有基于底层机器指令的goto结构。
在Lisp语言中,函数与整数或字符串一样,也属于数据类型的一种。它有自己的字面表示形式(literal representation),能够储存在变量中,也能当作参数传递。一种数据类型应该有的功能,它都有。
Lisp是第一种支持递归函数的高级语言。
在Lisp语言中,所有变量实际上都是指针,所指向的值有类型之分,而变量本身没有。复制变量就相当于复制指针,而不是复制它们指向的数据。
Lisp程序是一些表达式区块的集合,每个表达式都返回一个值。
符号实际上是一种指针,指向储存在哈希表中的字符串。
Lisp并不真正区分读取期、编译期和运行期。你可以在读取期编译或运行代码;也可以在编译期读取或运行代码;还可以在运行期读取或者编译代码。
在读取期运行代码,使得用户可以重新调整(reprogram)Lisp的语法;
在编译期运行代码,则是Lisp宏的工作基础;
在运行期编译代码,使得Lisp可以在Emacs这样的程序中,充当扩展语言(extension language);
在运行期读取代码,使得程序之间可以用S-表达式(S-expression)通信,近来XML格式的出现使得这个概念被重新"发明"出来了。
Lisp语言刚出现的时候,它的思想与其他编程语言大相径庭。后者的设计思想主要由50年代后期的硬件决定。随着时间流逝,流行的编程语言不断更新换代,语言设计思想逐渐向Lisp靠拢。
思想1到思想5已经被广泛接受,思想6开始在主流编程语言中出现,思想7在Python语言中有所实现,不过似乎没有专用的语法。
思想8可能是最有意思的一点。它与思想9只是由于偶然原因,才成为Lisp语言的一部分,因为它们不属于John McCarthy的原始构想,是由他的学生Steve Russell自行添加的。它们从此使得Lisp看上去很古怪,但也成为了这种语言最独一无二的特点。
Lisp古怪的形式,倒不是因为它的语法很古怪,而是因为它根本没有语法,程序直接以解析树(parse tree)的形式表达出来。在其他语言中,这种形式只是经过解析在后台产生,但是Lisp直接采用它作为表达形式。它由列表构成,而列表则是Lisp的基本数据结构。
用一门语言自己的数据结构来表达该语言,这被证明是非常强大的功能。思想8和思想9,意味着你可以写出一种能够自己编程的程序。这可能听起来很怪异,但是对于Lisp语言却是再普通不过。最常用的做法就是使用宏。
术语"宏"在Lisp语言中,与其他语言中的意思不一样。Lisp宏无所不包,它既可能是某样表达式的缩略形式,也可能是一种新语言的编译器。如果你想真正地理解Lisp语言,或者想拓宽你的编程视野,那么你必须学习宏。
如果你创造了一种新语言,其中有car、cdr、cons、quote、cond、atom、eq这样的功能,还有一种把函数写成列表的表示方法,那么在它们的基础上,你完全可以推导出Lisp语言的所有其他部分。事实上,Lisp语言就是这样定义的,John McCarthy把语言设计成这个样子,就是为了让这种推导成为可能。
运行于Java虚拟机的List方言Clojure.
Lisp是一种以表达性和功能强大著称的编程语言,但人们通常认为它不太适合应用于一般情况,而Clojure的出现彻底改变了这一现状。如今,在任何具备 Java 虚拟机的地方,都可以使用 Lisp 的强大功能。
它囊括了函数式编程的所有精华:
避免了不稳定状态、递归、更高阶的函数等。
我们可以选择添加类型信息来提高代码中的关键路径的性能。
Clojure 不仅可在 JVM 上运行,而且可以与Java无缝融合(JVM平台的语言家族原则上都支持)的互操作性。最后,Clojure 在设计上也考虑了并发性,并具有并发编程的一些独特特性。
(1)简单: 鼓励纯函数,极简的语法(少数special form),个人也认为clojure不能算是多范式的语言(有部分OO特性),为了支持多范式引入的复杂度,我们在C++和Scala身上都看到了。
(2)专注:前缀运算符不需要去考虑优先级,也没有什么菱形继承的问题,动态类型系统(有利有弊),REPL提供的探索式编程方法(告别修改/编译/运行的死循环,所见即所得)。
(3)实用:前面提到,构建在JVM之上,跟Java语言的互操作非常容易。直接调用Java方法,不去发明一套新的调用语法,努力规避Java语言中繁琐的地方(doto,箭头宏等等)。
(4)清晰:纯函数(前面提到),immutable var,immutable数据结构,STM避免锁问题。不可变减少了心智的负担,降低了多线程编程的难度,纯函数也更利于测试和调试。
(5)一致:语法的一致性:例如doseq和for宏类似,都支持destructring,支持相同的guard语句(when,while)。数据结构的一致性:sequence抽象之上的各种高阶函数。
Clojure有着独特的吸引力,首先因为它是LISP —— 一门富有传奇色彩的语言,一直希望有机会可以学习一门LISP的方言;
其次Clojure是一门接地气的语言,它运行在JVM这个最成功、应用最广泛平台之上,能够跟Java代码无缝互操作,JVM上所有资源都可以为Clojure所用。
Clojure是这样的有潜力、接地气,那么如果你要选择一门新语言来玩玩,不选它选什么?