Clojure

历史与开发过程

里奇·希基（Rich Hickey）是Clojure语言的创造者。 在Clojure之前，他开发过类似但基于.NET平台的项目——dotLisp。 在那之前，他还有过三次旨在在Lisp与Java之间提供互操作（interoperability）的尝试：“Common Lisp的Java外语接口”［Java foreign language interface for Common Lisp (jfli)］、“Lisp的外语对象接口”［Foreign Object Interface for Lisp (FOIL)］以及“Lisp友好的Java Servlet接口”［Lisp-friendly interface to Java Servlets (Lisplets)］。
在公开发布之前，里奇·希基在Clojure的工作上花了大约两年半的时间。期间，在没有外部资金的情况下，他将其大部分时间都专门投入到了Clojure的工作上。在这段时间结束时，里奇·希基以电子邮件的形式向他在Common Lisp社区里的一些朋友宣布了该语言。
Clojure的开发过程由社区驱动并且在Clojure JIRA项目网页上进行管理。 该网站可用于提交问题报告（issue）。一般的开发讨论是在“Clojure谷歌网上论坛”（Clojure Google Group）上进行的。 任何人都可以提交错误报告和想法，而贡献补丁（patches）则需要先签署“Clojure贡献者协议”（Clojure Contributor Agreement）。 JIRA错误报告（JIRA ticket）由一个筛选者（screener）团队进行处理并最终由里奇·希基批准更改。

设计理念

里奇·希基开发Clojure的原因是因为他想要一款适合函数式编程的现代Lisp。该语言既需要与已创建的Java平台共生又需要有适合并发性的设计。
Clojure对待变化（change）的方式以标识（identity）的概念为特征。 标识是指随着时间的推移而产生的一系列状态（state）。而状态则是指标识在某一特定时间点上的值（value）。需要强调的是，这里的值是不可变的（immutable）。由此引申，由于状态是不可变的值，任意数量的工作单位（workers）都可以在其上以并行（parallel）的方式实施操作。因此，并发性（concurrency）就成为一道管理状态间变化的问题［注意，这里的“变化”是指从一个状态到另外一个状态的跃迁（transition）而不是状态本身的变化（mutation）。］为此，Clojure提供了几个可变的（mutable）引用类型（reference type）。每个引用类型都有其明确定义的语义用于控制状态之间的跃迁。

平台

Clojure的主要平台是Java但也存在其他目标平台上的实现。其中，最值得关注的是ClojureScript（可被编译成ECMAScript 3）和ClojureCLR（.NET平台上的完整移植版，可与其生态系统互操作。）2013年对1,060名受访者进行的Clojure社区调查发现，47％的受访者在使用Clojure的同时也使用ClojureScript。2014年，这一数字增长到了55％，而到了2015年，则达到了66％（根据2,445名受访者）。 人气较高的ClojureScript项目包括React实现，如Reagent、re-frame、Rum及Om。

功能示例

以下示例均可在Clojure REPL中运行（如，使用Clojure命令行界面工具启动的REPL或在REPL.it上提供的在线REPL。）

简单性

由于强调简单性，典型的Clojure程序主要包括函数和简单的数据结构（即列表，向量，映射和集合）：
;; 一个典型的Clojure程序的入口(defn -main                   ; 函数名
  [& args]                    ; 参数向量(`-main`是个可变参数函数)
  (println "Hello, World!"))  ; 函数体

REPL编程

与其他Lisp一样，Clojure的标志性特征之一是基于REPL的交互式编程。在以下示例中，;;表示一行注释的开始，而;; =>则表示输出：
;; 定义一个var(def a 42);; => #'user/a;; 调用名为`+`的函数(+ a 8);; => 50;; 调用名为`even?`的函数(even? a);; => true;; 定义一个函数以返回n除10之余(defn foo [n] (rem n 10));; => #'user/foo;; 调用该函数(foo a);; => 2;; 打印`rem`的文档字符串（docstring）(doc rem);; =>-------------------------clojure.core/rem([num div])
 remainder of dividing numerator by denominator.;; 打印`rem`的源代码(source rem);; =>(defn rem
  "remainder of dividing numerator by denominator."
  {:added "1.0"
   :static true
   :inline (fn [x y] `(. clojure.lang.Numbers (remainder ~x ~y)))}
  [num div]
    (. clojure.lang.Numbers (remainder num div)))

运行时可用的名字

与Clojure不同，其他语言的编译器会将程序中的名字编译掉使得它们在运行时不可用。而在Clojure中，可以用普通的数据结构对其运行时进行观察：
;; 定义一个var(def a 42);; => #'user/a;; 以映射（map）的形式获取在`user`名字空间中捕获的（interned）所有var(ns-publics 'user);; => {a #'user/a};; 用`#'`（读取器宏）及其关联的、名字空间限定的符号`user/a`引用该var#'user/a;; => #'user/a;; 解引用该var（获取其值）(deref #'user/a);; => 42;; 定义（并附加文档字符串）一个函数以返回n除10之余(defn foo "返回`(rem n 10)`" [n] (rem n 10));; => #'user/foo;; 获取var `#'user/foo`的元数据(meta #'user/foo);; =>{:arglists ([n]),
 :doc "返回`(rem n 10)`",
 :line 1,
 :column 1,
 :file "user.clj",
 :name foo,
 :ns #namespace[user]}

代码即数据（同像性）

与其他Lisp类似，Clojure也具有同像性［又被称为“代码即数据”（code as data）］。从下面的示例中可以看到，用Clojure编写代码从而修改代码本身是非常容易的：
;; 调用一个函数（代码）(+ 1 1);; => 2;; 引用该函数调用;;（将代码转换成数据，此处为含一组符号的列表）(quote (+ 1 1));; => (+ 1 1);; 获取该列表上的首个元素;; （视代码为数据并对其进行操作）(first (quote (+ 1 1)));; => +;; 获取该列表上的最后一个元素;; （视代码为数据并对其进行操作）(last (quote (+ 1 1)));; => 1;; 替换原列表上的符号从而获取一个新列表;; （视代码为数据并对其进行操作）(map (fn [form]
       (case form
         1 'one
         + 'plus))
     (quote (+ 1 1)));; => (plus one one)

富有表现力的数据变换操作符

穿梭宏（threading macro，如，->、->>等）可以在语法上表达一个数据集在一系列变换间穿梭的抽象：
(->> (range 10)
     (map inc)
     (filter even?));; => (2 4 6 8 10)
利用变换归纳器（transducer）也可以更有效地实现该过程：
(sequence (comp (map inc)
                (filter even?))
          (range 10));; => (2 4 6 8 10)

标识及其状态的线程安全管理

线程安全的唯一序列号生成器（然而，和许多其他Lisp方言一样，Clojure内部使用其内置的gensym函数）：
(def i (atom 0))(defn generate-unique-id
  "每次调用会返回一个唯一数字ID。"
  []
  (swap! i inc))

宏

java.io.Writer的一个匿名子类（不写任何内容）和一个宏（使用该类来静音其中打印的所有内容）:
(def bit-bucket-writer
  (proxy [java.io.Writer] []
    (write [buf] nil)
    (close []    nil)
    (flush []    nil)))(defmacro noprint
  "在对给定的`forms`求值的同时静音所有向`*out*`打印的内容。"
  [& forms]
  `(binding [*out* bit-bucket-writer]
     ~@forms))(noprint
  (println "Hello, nobody!"));; => nil

Java互操作

作为其主要设计目标之一，Clojure从一开始就将其宿主平台（host platforms）视为其不可分割的一部分。Clojure与Java之间出色的互操作即得益于此：
;; 调用一个实例方法(.toUpperCase "apple");; => "APPLE";; 调用一个静态方法(System/getProperty "java.vm.version");; => "12+33";; 创建`java.util.HashMap`的一个实例;; 并加入一些键值对（key-value pairs）(doto (java.util.HashMap.)
  (.put "apple" 1)
  (.put "banana" 2));; => {"banana" 2, "apple" 1};; 创建`java.util.ArrayList`的一个实例;; 并用`clojure.core/map`递增（increment）其元素(def al (doto (java.util.ArrayList.)
          (.add 1)
          (.add 2)
          (.add 3)))(map inc al);; => (2 3 4);; 利用Java Swing显示一个消息对话框(javax.swing.JOptionPane/showMessageDialog
  nil
  "Hello, World!");; => nil

软件事务内存

10个线程操纵一个共享数据结构，该结构由100个向量组成，而每个向量包含10个（最初是连续的）唯一数字。每个线程随后在两个随机向量中重复选择两个随机位置并交换它们。通过使用Clojure的软件事务内存系统，对向量的所有更改都发生在事务中：
(defn run
  [nvecs nitems nthreads niters]
  (let [vec-refs
        (->> (* nvecs nitems)
             (range)
             (into [] (comp (partition-all nitems)
                            (map vec)
                            (map ref))))

        swap
        #(let [v1 (rand-int nvecs)
               v2 (rand-int nvecs)
               i1 (rand-int nitems)
               i2 (rand-int nitems)]
          (dosync
            (let [tmp (nth @(vec-refs v1) i1)]
              (alter (vec-refs v1) assoc i1 (nth @(vec-refs v2) i2))
              (alter (vec-refs v2) assoc i2 tmp))))

        report
        #(->> vec-refs
              (into [] (comp (map deref)
                             (map (fn [v] (prn v) v))
                             cat
                             (distinct)))
              (count)
              (println "Distinct:"))]

    (report)

    (->> #(dotimes [_ niters] (swap))
         (repeat nthreads)
         (apply pcalls)
         (dorun))

    (report)))(run 100 10 10 100000);; =>[0 1 2 3 4 5 6 7 8 9][10 11 12 13 14 15 16 17 18 19]
  ...[990 991 992 993 994 995 996 997 998 999]Distinct: 1000[382 318 466 963 619 22 21 273 45 596][808 639 804 471 394 904 952 75 289 778]
  ...[484 216 622 139 651 592 379 228 242 355]Distinct: 1000nil