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）。一般的開發討論是在「ClojureGoogle網上論壇」（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