Anjaxs

Clojure – 扩展智慧的完美语言?

原文地址:Clojure – the perfect language to expand your brain?

作者:Eli Bendersky

有时候我会被问到接下来要学习什么编程语言,很好的是人们意识到掌握几种截然不同的语言是走向编程启蒙道路的重要一步。但事实是在有限的自由时间里,选择哪种语言的学习是很重要的,如著名的Alan Perlis所说:

A language that doesn’t affect the way you think about programming, is not worth knowing.

如果一门语言无法对你的编程思想造成影响,那么它不值得你去了解

在这篇文章中,我接下来会解释一下为什么我认为Clojure是此方面的了不起的语言。无论你的背景条件,它几乎保证能影响你对编程的思考方式。这是一种相当新的语言(从2007起),它很好地收集了许多现有语言的见解和范例,并把它们组织成一个连贯的整体。这里面充满了伟大的思想。换句话说, 这是一门扩展你智慧的完美语言。

这是Lisp

 首先,Clojure属于Lisp语言家族,像Common Lisp,Scheme和Racket。Lisp是最古老的,仍然是最重要的编程语言之一。Lisp意味着几件事情:
  • Clojure是一个动态的语言。无论你站在静态或动态类型的立场上,了解两个阵营的语言都很重要。Clojure有一种随意的编写方式,但在本质上是动态的。
  • 它促进了内置数据结构(lists、maps、vectors)和对象的组合,并有很好的理由这样做。
  • 它促进了编程的高阶函数,一些内置特性像 reducers and transducers 高度依赖于构造高阶函数来转换其他函数。
  • 因为受到全Lisp风格宏的支持,它拥有统一的的语法,对于非Lisp语言来说这种能力是难以达到,看一些真正运作的宏是有启发作用的。Clojure拥有类Go语言的CSP相等的东西,在core.async文件中通过使用宏来实现的。
  • 它鼓励基于PERL的开发,在一个交互终端逐渐自底向上的功能性构建和测试。

重要的是Clojure是一个非常现代的Lisp,有大量用于解决我们日常遇到任务的库。而且它在最近数据结构如efficient persistent vectors(出现在90年代或以后)的研究下建立。我一直尝试不同的Lisp将近15年,而Clojure是我想用在生产上的第一个Lisp。

我希望再次强调全Lisp风格宏的重要性。Lisp(Clojure)统一的语法和宏驱使我们思考Meta-circular abstraction  我们必须把我们的代码看成数据,这有深远的含义。 回到2005,当我还是一个编程新手, 我使用Perl语言时面临一个数据解码程序性能的挑战。在了解了一些Lisp后, 我知道了那程序成功的关键点 generating and evaluating new Perl code on the fly 。我相信如果我不知道“Lisp 方式”我不可能想出解决办法。

实用主义和来之不易的经验

Clojure的设计者是非常务实的,建立在几十年的行业经验。我强烈建议观看一些领先的Clojure开发者的 YouTube videos of talks。特别是观看Clojure的原设计师Rich Hickey的一些更受欢迎的会谈。上面提及的Alan Perlis的语录也许是最受欢迎的,但是我认为下面这句也很有见解:

Simplicity does not precede complexity, but follows it.

简单不先于复杂,而在复杂之后。

我相信我们都能认识到一类程序员有某一能力来自多年来构建系统的真实的、来之不易的经验和思考如何在下一次更有效地构建这些系统的经历。Rich Hickey当然属于高质量的这类,他的谈话都很有见地,哲学的设计和实现Clojure,以及它(友好的、充满活力)的社区。

序列和在语言数据处理功能强大的Laziness

从大型数据集中处理和提取有用信息位的应用程序在许多编程语言中都是相同的,至少在某种程度上是如此。在许多情况下,我们真的想类SQL原语纳入我们的语言里,但这往往是具有挑战性的(.NET的LINQ是一个成功方法例子)。

通过普适序列协议的持久数据结构和laziness的结合,Clojure只使用内置的工具能轻松完成这一任务。这是Clojure Applied书里的一个函数:

(defn revenue-by-department [carts]
  (->> (filter :settled? carts)
       (mapcat :line-items)
       (map line-summary)
       (group-by :dept)
       (reduce-kv dept-total)))

所有在上面使用的函数都是内置的, 包括宏 ->> 。 这样的代码看起像SQL并且同样高效。实际上,它可以更有效的,甚至可以使用Reducers and Transducers无缝地并行。

clojure.spec 库提供了一定程度的嵌套数据结构类型安全。如果你曾经写过这样的评论:“this maps strings to lists, where each list element is a map of …”,clojure.spec使它更正式更可核查。这样的数据是对工作非常有用的。另一个例子,Clojure的实用主义显著,语言在其核心是动态的,但它也有静态检查(core.typed是静态类型的一种选择)。

面向对象的正确方法

 从历史上看,Lisp程序员不是面向对象的最大支持者。这并不意味着OOP在lisp-y语言毫无价值。然而,Common Lisp拥有CLOS已经接近了几十年,而Clojure一样有类OO功能的数组。

据说,Clojure的OOP特点是特别强大的,往往能阻止不良行为。Clojure使用“协议”,这是一种接口,并鼓励在协议而不是在继承层次结构的类和像Go语言排序的角度思考。再加上一个真正的 multiple dispatch 的能力和一个强大的建模工具。

内置并发性和并行性的支持

在2000年,如果没有并发和并行强大正确支持的核心就不会形成一种真正的语言。在许多方面Clojure支持并发和并行。

第一,这是一个持久化数据结构的功能性语言。持久数据结构实际上是不可变的,这使得它们在多线程环境下非常有吸引力。Clojure很好的实现了持久化数据结构如vectors(以一种有效的方式实现是很有挑战性的)。这可能是一个比最初出现的较大进步。纯函数总是在理论上可行但在实践中由于过多的复制复杂数据结构而失败, Clojure优雅的持久性数据结构用法解决了这个问题,使得纯函数高效,因此更适用于广泛的数组问题。

第二,Clojure并没有把自己锁在纯粹主义的衣柜里,而是支持低等的需要的并发和异变原语。Atoms是内置的可变状态的原子的更新单元。 Refs and transactions 里进一步了解,实现STM。

第三,Clojure配备了一堆内置的并发工具,Promises,futures和threads利用底层的JVM的线程工具。

最后,在core.async模块,Clojure有相当好的Go’s CSP实现,它提供了两个有用的东西:

  1. Channels 和 Go 里类select的功能 ,这使得它很容易安全并行队列的工作和“沟通数据共享”。
  2. 非阻塞原语go-blocks,这使得可能很容易写出合作的“绿色线程”。

总结-尝试用下它

最后,Clojure并非只停留在学术上,它已经被几家公司在生产中应用,具有很强的根在巨大的java生态系统中。Clojure的互操作性与java是无缝的—java代码被调用无任何限制,所有核心Clojure实体是基于java对象的。

在客户端,作为另一种语言编译JS的选项,Clojurescript已获得拉力。它的客户端编程带来了Clojure的风采,其中具有一些独特的功能如core.async的无回调并发。

这并不意味着你应该只考虑Clojure作为一个适合你的下一个项目。相反,我只推荐学习,即使在短期内很少有机会在生产中使用它。事实上,这正是我的处境,Clojure是一个来扩展你的编程视野的伟大的语言。谁知道呢?也许在未来,你会发现它很有用,对于一些真正的工作。如果你这样做,好极了!如果你不这样做,学习它将对你如何使用其他编程语言产生积极的影响。

 

附注:

Alan Perlis的编程警句

码字很辛苦,转载请注明来自Anjaxs博客《Clojure – 扩展智慧的完美语言?》

评论