请评价一下Clojure语言的设计?

在尝试了一段时间的Scheme之后偶尔用了一下Clojure,发现Clojure有些设计实在是太好用了, 比如之前经常会去找Scheme上let的扩展实现,其实Clojure的let实现的destructuring binding正是我想要的功能, Clojure的实现更加完整统一。所以我想知道 1.Clojure能够脱颖而出最主要原因是什么, 2.评价一下Clojure中的一些设计, 比如STM等, 3.Clojure有哪些设计上的不足(如果有), 或者说这么问,Scheme中有哪些优秀的东西是Clojure没…
关注者
730
被浏览
94,161

13 个回答

个人观点,仅供参考:

1.Clojure能够吸引人的很重要一点是它是JVM之上的语言,这个决定非常关键
首先,因为根植于JVM之上,并且做到了跟Java语言的相互调用,它能吸引很多成熟的Java开发者。其次,它可以使用Java社区丰富的开源软件,不需要从头去构建一个社区,你可以看到很多Clojure开源代码都是简单地包装Java的开源包,但是通过Clojure高度抽象简单的语法提供更便利的使用的方式;
第三,由于JVM平台本身的高度成熟和优化,clojure的编译器生成的byte code跟Java编译器生成的byte code并无二致(不完全是),它的性能和稳定性也能马上得到保证,这比从头构建一个新平台成本低得多。

构建于JVM之上,Clojure就是一门“严肃”的语言,而非很多人眼中的Lisp“玩具”语言,你学习后可以马上使用并且实践。但是Clojure又是Lisp方言,Lisp的神奇能力它还都保留,这样兼具美学和实用的语言如何让人不爱?我相信很多熟悉Scheme之类方言的童鞋,并且有Java背景的,都会对Clojure有相见恨晚的感觉。

2.Clojure的设计原则可以概括成5个词汇:简单、专注、实用、一致和清晰。这不是我概括的,而是《The joy of 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抽象之上的各种高阶函数。

具体到STM,我个人认为这个特性在日常编程中,其实你用到的机会不多。在web编程里,你的并发模型Web Container已经帮你处理(tomcat,jetty),事务也是数据库帮你处理,几乎找不到场合去使用STM。这个特性在做一些中间件或者底层framework的时候才可能用到。这个特性的设计上面已经提到,跟clojure的设计目标是紧密相关的,跟immutable数据结构也是密不可分,同时它也不是没有代价,事务历史记录和慢事务频繁回滚的代价,有时候你还是需要退回去使用Java那套锁机制,庆幸的是Clojure不阻止你去使用,并且提供了类似locking这样的宏来方便你使用。

3.Scheme我对它的了解也就是做过SICP的习题,粗粗看过《Programming Scheme》,两者对比的优缺点似乎谈不上来。需要对Scheme更熟悉的专家来做个对比。

Clojure的设计缺陷不能说是缺陷,这是由于它设计的目标决定的,有得必有失

首先还是JVM,基于JVM有种种好处,但是JVM的启动速度实在悲剧,因此用Clojure写一些小的script处理日常事务,显得还是不够得心应手,这样的工作我还是用Ruby,Python的脚本语言来搞定更便捷。不过目前Clojure有一些其他语言之上的实现,比如rouge-lang/rouge · GitHubhalgari/clojure-py 路 GitHub 这些实现应该会比JVM的启动快很多(抱歉,我没测试过)。

不仅如此,因为Clojure跟JVM平台的绑定如此之深,并且为了真正发挥Clojure的威力,你还需要去熟悉Java平台的东西,熟悉Java语言、类库、内存模型、GC优化、多线程和网络编程、开源类库等等。可以这样认为:想成为一个好的Clojure程序员,首先需要是一名好的Java程序员。这也一定程度上阻碍了Clojure的推广,提高了学习成本。

其次,Clojure的API设计上,有时候不符合你的直觉,而是符合Clojure的哲学,比如contains?函数对vector等数组型集合的调用上。关于这一点,Rich的回答是“Elegance and familiarity are orthogonal.”,也就是优雅和熟悉是正交关系的。保持API内在的一致性,比直觉的“熟悉”更重要。Clojure不妥协于你的“直觉”。

第三,弱类型的好处足够多,灵活,减少声明代码,适合探索式编程;同样,坏处也不是没有,没有类型保障,错误可能要等到运行时才能发现,静态代码检查工具也没有办法帮你发现,这就需要你一定程度的测试代码来保证运行时行为。

第四,性能上,虽然clojure生成的字节码已经很高效,也有type hint这样的技术来帮助提升性能,但是会有不少的转型(checkcast)、装箱和拆箱(boxing and unboxing)以及类型判断分支跳转的多余指令,这在一些性能敏感的应用里可能会暴露出来。尽管我认为大多数网站型的应用瓶颈都会落在IO上。

以上是我对这个问题的一些看法,欢迎探讨。谢谢。

P.S. 我们的整个网站几乎都是基于 clojure 架构的,有兴趣可以看下 AVOS Cloud,每天承载的请求量也在亿次级别。
谢邀。@xiaodan zhuang 已经说的差不多了,想补充的不多。这里贴一下我在《Clojure编程》译者序里面有关我对Clojure总体看法的一段话:

在Clojure之前我也尝试过几种函数式语言,比如“最纯的函数式语言”Haskell、“天生擅长高并发的”Erlang,这些都是很棒的语言,但是学过之后一段时间就忘了,因为工作、生活中实在是用不到。而Clojure对我则有独特的吸引力,首先因为它是LISP —— 一门富有传奇色彩的语言,一直希望有机会可以学习一门LISP的方言;其次Clojure是一门接地气的语言,它运行在JVM这个最成功、应用最广泛平台之上,能够跟Java代码无缝互操作,JVM上所有资源都可以为Clojure所用。

Clojure是这样的有潜力、接地气,那么如果你要选择一门新语言来玩玩,不选它选什么?


而有关STM的评价,我在另外一个回答:多核和分布式的编程环境促使产生了并发编程语言,那么它和传统的编程语言有什么本质的不同?里面略有提到, 也贴一下:
不过在对实际问题建模过程中,我们还是需要一些可变的概念,比如经典的银行转账的例子,客户的银行余额显然应该是可变的,而且肯定会有多个线程会对这个余额进行读写,Clojure对于这种情况提供了软件事务内存(Software Transactional Memory -- STM),STM的作用简单点说就是我们无法直接对状态进行读写, STM代理了我们对于状态的所有读写,当我们说要一个状态进行修改的时候,STM会去监测在我们改的过程中,是否有别的线程改过这个状态,如果改过,那么自动重试,直到修改成功,或者失败一定次数之后不再重试告诉用户修改失败 -- 也就是说多线程之间的重试、协调由STM帮我们搞定了,自然不会有问题了。