前言
Clojure 是一种基于 Java 虚拟机(JVM,Java Virtual Machine)的动态编程语言(dynamic programming language)。它具有以下引人注目的特性。
● Clojure非常优雅。
摒弃了杂乱累赘的语法束缚,Clojure 干净、仔细的设计使你在编写代码时总能立刻切入问题的本质。
● Clojure是Lisp的再度崛起。
Clojure从Lisp继承了强大的力量,却未受到Lisp历史的束缚。
● Clojure是一种函数式语言(functional language)。
作为一门函数式语言,Clojure的数据结构具有不可变性(immutable),且大多数函数没有副作用(side effect)。因此,编写正确的程序更加容易,也能更轻松地将小程序组合成一个大家伙。
● Clojure简化了并发编程。
很多其他语言围绕同步锁(locking)建立的并发模型,难以驾驭。为此,Clojure提供了数个锁机制的替代方案:软事务内存(Software Transactional Memory,STM)、代理(agent)、原子(atom)和动态变量(dynamic variable)。
● Clojure与Java彼此亲密无间。
在Clojure中调用Java代码,无需任何中间的转换层,直接而且快速。
● 不同于许多其他流行的动态语言,Clojure运行飞快。
Clojure的实现利用了现代Java虚拟机上的众多优化技术。
尽管许多其他语言也包含了上述诸多特性中的一部分,但与它们相比,Clojure仍显得魅力非凡。上述任何一个特性,都极为强大和有趣。Clojure的迷人之处在于,将这些特性以非常干净的方式融合在了一起,且做到彼此协作无间。本书的第1章“启航”,将介绍以上这些特性及更多的内容。
谁应该阅读本书
Clojure 是一种强大的通用型(general-purpose)编程语言。如果你是一名经验老到的程序员,具备类似C#、Java、Python或者Ruby这样的现代编程语言的开发经验,并正在寻找更为强大、更加优雅的编程语言,那么本书是为你量身定做的。
Clojure构建于Java虚拟机之上,并且运行飞快。如果你是一名对表现力丰富的动态语言馋涎已久,但却因为对性能问题的担忧而裹足不前的Java程序员,那么本书将引起你特别的兴趣。
Clojure 有助于重新定义,一种通用型编程语言应该包含哪些特性。如果你使用Lisp,或使用一种诸如Haskell这样的函数式语言,又或者正编写明显存在并发的程序,那你一定会享受Clojure的一切。Clojure融合了来自Lisp、函数式编程和并发编程领域的理念,使得初次接触这些概念的程序员,一切触手可及。
Clojure是本轮编程语言形态大规模演化现象的一部分。诸如Erlang、F#、Haskell和 Scala 这样的语言,由于它们支持函数式编程,或是由于它们的并发模型,最近都得到了格外的关注。作为这些语言的忠实信徒,你也一定会从Clojure当中找到众多共通之处。
本书主要内容
第1章,启航。本章将展示作为一门通用型语言,Clojure的优雅特质及其函数式风格,以及独特的并发模型如何令其独一无二。阅读完本章,你还将能够轻松完成Clojure的安装,并学会如何使用REPL进行交互式开发。
第2章,探索Clojure。在这里,我们将对Clojure的核心构造进行一次广度优先的概览。完成本章的阅读后,你将能顺畅地阅读大多数常规的Clojure代码。
接下来的两章将讨论函数式编程。第3章,一切皆序列,将展示Clojure如何使用强大的序列隐喻,统一了所有的数据形态。
第4章,函数式编程。本章将向你展示如何编写与序列库代码风格相同的函数式程序。
第5章,状态。本章我们将深入Clojure的并发编程模型。探讨Clojure中用于处理并发问题的 4 种强大模型。此外还有来自 Java 并发库中的精华内容一并奉上。
第6章,协议和数据类型。本章将逐个介绍在Clojure中的记录(record)、类型(type)和协议(protocol)。这些概念自Clojure 1.2.0版本首次引入后,在Clojure 1.3.0版本中得到了进一步增强。
第 7 章,宏。本章将不加掩饰地炫耀这一来自 Lisp 中的标志性特性—宏(Macros)。你将看到它如何利用“Clojure代码本身就是数据”这一特质,提供了在其他非Lisp语系中极难甚至无法实现的非凡的元编程能力。
第8章,多重方法。本章将讨论Clojure解决多态问题的众多方法中的一种。多态,通常意味着“获取第一个参数的类型,并据此调度到相应的方法”。Clojure 的多重方法,使你可以更进一步,选择适用于所有参数的任意函数来进行调度。
第9章,极尽Java之所能。在本章中,你将看到如何从Clojure中调用Java,以及从Java中调用Clojure。你还将看到如何让Clojure疯狂运转,获得原生Java级别的性能。
最后,第10章,搭建应用。本章提供了一个可以让你完整了解Clojure应用开发全过程的视角。在这里,你将从头开始创建一个应用,并深入解决问题的方方面面,同时,还会考虑关于简单和质量的话题。你将借助一组有用的Clojure库,生产并发布一个Web应用。
附录,编辑器。这里列出了可供你选择的Clojure代码编辑器列表,并分别提供链接指向它们各自的安装说明。
如何阅读本书
所有读者都应该按顺序阅读最初的两章。请特别关注1.1节,这里提供了Clojure具备哪些优势的概述,里面的内容你一定会感兴趣。
持续的试验。Clojure 提供了一个可以让你立即获取反馈的交互式环境。请阅读1.2.1小节,以获得更多的信息。
读完最初的两章,你就可以随意翻阅了。但如果你打算开始阅读第5章,那么,确保你已经读过了第3章。顺序阅读这几章,将引导你从理解Clojure的不可变数据结构开始,一直到能够利用Clojure强大的并发模型,编写正确的并发程序。
当你开始接触后面各章中那些较长的代码示例时,请确保你使用的编辑器能为你提供Clojure代码自动缩进功能。附录“编辑器”列举了编写Clojure代码的通常选择。如果可能,请尝试使用支持括号匹配功能的编辑器,例如Emacs的paredit模式或者安装了CounterClockWise插件的Eclipse。这些编辑功能将为你顺利学习Clojure编程提供巨大的帮助。
致函数式程序员
● Clojure的函数式编程之道,在于将理论的纯粹之美,与Clojure需要运行在当前Java虚拟机之上的现实做出了完美的平衡。倘若仔细地阅读了第4章“函数式编程”,你将了解到Clojure与诸如Haskell这样的学院派语言之间存在的风格差异。
● Clojure的并发模型(第5章),提供了数个直截了当的途径,用于处理并发世界中副作用和状态的问题。这也使得广大读者可以深入地体验函数式编程之魅力。
致Java和C#程序员
● 请认真阅读第2章,Clojure只有很少的语法规则(相比Java或C#而言),所以我们能很快地熟悉它们。
● 请特别留意第7章,Java或C#背景的程序员将会发现,这部分是Clojure与他们所熟悉的语言之间的最大不同。
致Lisp程序员
● 第2章中的一些内容你可能已经很熟悉了,但无论如何,还是应该读一下这一章。Clojure从Lisp中承袭了众多关键特性,但它也在一些地方打破了Lisp传统,这里将会讨论这些内容。
● 请密切关注第4章中的惰性序列。
● 为你的Emacs装备一个“clojure-mode”吧,这将为你享受后面章节中的代码示例提供很大便利。
致Perl、Python和Ruby程序员
● 仔细阅读第5章,在Clojure中,进程内并行计算是一个非常重要的话题。
● 拥抱宏吧(第7章)。但请不要寄予太大的期望,能将你所用语言中的元编程风格轻松套用到 Clojure 宏中。请牢记,Clojure 的宏更为强大,并且,它是在代码读取期间被执行的,而非在运行期执行。
编写体例
以下编写体例将从始至终地贯穿于本书之中。
代码示例采用以下字体。
(+ 2 2)
为区别代码示例及其执行结果,我们会在执行结果前放置一个箭头(->)。
(+ 2 2) -> 4
同样,控制台的输出也不容易与示例代码和结果区别开来,因此,我们会在控制台的输出前放置一个管道(|)符。
(println "hello") | hello -> nil
当首次引入某个Clojure形式(form),我们需要说明其语法时,将采用下述表示法。
(example-fn required-arg) (example-fn optional-arg?) (example-fn zero-or-more-arg*) (example-fn one-or-more-arg+) (example-fn & collection-of-variable-args)
这是一种非正式的语法,采用?、*、+和&符号,用于说明不同的参数传递模式。
Clojure的代码是以程序库的形式进行组织的。如果本书某段示例代码所依赖的库没有包含在Clojure语言核心中,我们将用Clojure的use或require对此加以说明。
(use '[lib-name :only (var-names+)]) (require '[lib-name :as alias])
此处使用use引入仅出现在列表var-names中的名称。使用require则创建一个库别名,使得每个引入函数的来源更加明晰。例如,来自于clojure.java.io库中的常用函数file。
(use '[clojure.java.io :only (file)]) (file "hello.txt") -> #<File hello.txt>
或使用基于require的版本。
(require '[clojure.java.io :as io]) (io/file "hello.txt") -> #<File hello.txt>
事实上,如果成功调用了use,Clojure会返回nil。但为使本书更加简洁,这个输出在示例清单中省略了。
在阅读本书期间,你将在名为REPL的Clojure交互式环境中输入代码。REPL的控制台提示符形如下。
user=>
提示符中的user表明了你当前所在的Clojure名字空间。在本书大多数的例子中,当前位于哪个名字空间无足轻重。在这种情况下,我们将其省略,采用下述更简洁的语法表示在REPL中发生的一切。
(+ 2 2) ; 没有命名空间提示的输入行 -> 4 ; 返回值
少数情况下,当位于哪个名字空间非常重要时,我们将采用如下语法。
user=> (+ 2 2) ; 有命名空间提示的输入行
-> 4 ; 返回值
Web资源及反馈
本书的英文官方主页位于Pragmatic Bookshelf站点。在这里你可以订购本书的纸质版或是电子版,并且下载本书的示例代码。同样,你也可以将你的反馈提交至勘误表或是直接发表至本书论坛。
下载示例源码
你可以在下列任意位置找到本书的示例源码。
● 本书主页上有链接指向官方发布的源码。同时,每次本书发布新版时,源码也将得到更新。
● 处于实时更新的本书git源码仓库。这里有最新、最棒,且有时甚至强过书中所示的源码。
除非另行说明,示例文件都分别放在examples目录中。
贯穿于本书,示例源码的文件名列于源码清单起始位置,并采用灰色背景加以区别。例如,下面的源码清单来自于src/examples/preface.clj。
src/examples/preface.clj
(println "hello")
如果你正在阅读的是本书的PDF版本,你可以直接点击文件名下载对应的源码清单文件。
有示例源码在手,你就可以准备启航了。首先,我们将领略究竟是怎样的特性组合,使得Clojure如此的独一无二。