Clojure程序设计
上QQ阅读APP看书,第一时间看更新

前言

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)译注:本书中后续章节中还将出现另外一个含义的代理(Proxy),但绝大多数情况下,根据上下文即可分辨文中出现的“代理”应该是Agent还是Proxy,在可能会引起误解的地方,译者会加以注明。、原子(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资源及反馈

本书的英文官方主页http://www.pragprog.com/titles/shcloj2/programming-clojure。位于Pragmatic Bookshelf站点。在这里你可以订购本书的纸质版或是电子版,并且下载本书的示例代码。同样,你也可以将你的反馈提交至勘误表http://www.pragprog.com/titles/shcloj2/errata。或是直接发表至本书论坛http://forums.pragprog.com/forums/207。

下载示例源码

你可以在下列任意位置找到本书的示例源码。

● 本书主页http://www.pragprog.com/titles/shcloj2。上有链接指向官方发布的源码。同时,每次本书发布新版时,源码也将得到更新。

● 处于实时更新的本书git源码仓库http://github.com/stuarthalloway/programming-clojure。。这里有最新、最棒,且有时甚至强过书中所示的源码。

除非另行说明,示例文件都分别放在examples目录中。

贯穿于本书,示例源码的文件名列于源码清单起始位置,并采用灰色背景加以区别。例如,下面的源码清单来自于src/examples/preface.clj。

        src/examples/preface.clj
        (println "hello")

如果你正在阅读的是本书的PDF版本,你可以直接点击文件名下载对应的源码清单文件。

有示例源码在手,你就可以准备启航了。首先,我们将领略究竟是怎样的特性组合,使得Clojure如此的独一无二。