Clojure

Clojure其作者裡奇·希基則以終身仁慈獨裁者的身份監督。Clojure提倡不可變性與持久資料結構並鼓勵程式設計師顯式地管理標識及其狀態。Clojure的型別系統是完全動態的，但人們近期也開始探索其基於漸進型別化的實現。對Clojure的商業支援由Cognitect公司提供。 每年都會在全球範圍內舉辦年度Clojure會議，其中最著名的是Clojure/conj。歷史與開發過程.裡奇·希基，Clojure的創造者裡奇·希基創造了Clojure語言。此前，他開發過類似但基於.NET平臺的專案——dotLisp。在那之前，他還嘗試了三次在Lisp與Java之間提供互操作：Common Lisp的Java外語介面、Lisp的外語物件介面以及Lisp友好的Java Servlet介面。在公開發布之前，裡奇·希基花了大約兩年半的時間開發Clojure。他在沒有外部資金的情況下，將其大部分時間都專門投入到了Clojure的工作上。開發快要完成，裡奇·希基向Common Lisp社群裡的一些朋友釋出電子郵件，宣佈他完成開發了Clojure。Clojure的開發過程在Clojure JIRA專案網頁由社群驅動並管理。該網站可用於提交問題報告。一般的開發討論是在Clojure谷歌網上論壇上進行的。 任何人都可以提交錯誤報告和想法，而貢獻補丁前則需要先簽署Clojure貢獻者協議。 JIRA錯誤報告由一組篩選者處理，最終由裡奇·希基批准更改。設計理念.裡奇·希基開發Clojure的原因是因為他想要一款適合函數語言程式設計的現代Lisp。該語言既需要與已建立的Java平臺共生又需要有適合併發性的設計。在Clojure中，狀態以標識的概念為特徵。一系列狀態隨著時間的推移而產生，就是標識。由於狀態是不可變的值，任意數量的工作單位都可以在其上並行實施操作，併發性就成為一道管理狀態到狀態的變化的問題。因此，Clojure提供了幾個可變的引用型別。每個引用型別都有其明確定義的語義用於控制狀態之間的躍遷。語言概述.Clojure執行於Java平臺之上，因此，與Java緊密整合並完全支援從Clojure呼叫Java程式碼。 與此同時，也可以從Java呼叫Clojure程式碼。 Leiningen是社群中普遍使用的專案自動化工具。Leiningen為Maven整合提供支援，處理專案包管理和依賴項。Leiningen的配置使用的則是Clojure語法。與其他大多數Lisp一樣，Clojure的語法建立在S-表示式之上。S-表示式在被編譯之前先由讀取器解析為資料結構。 除了列表之外，Clojure的讀取器還支援對映、集合及向量等的字面量語法。這些字面量隨後會被直接編譯成上述資料結構。 Clojure是Lisp-1且有一套與其它Lisp不相容的資料結構，因此，Clojure不支援與Lisp的其它方言之間的程式碼級相容性。作為一門Lisp方言，函式在Clojure中是一等公民。此外，Clojure還支援讀取﹣求值﹣輸出迴圈以及一套宏系統。 Clojure的Lisp宏系統與Common Lisp的系統極為相似。唯一不同的是，Clojure的重音符Clojure支援惰性序列，並鼓勵不可變性與持久資料結構Clojure作為一門函數語言程式設計語言將重點放在遞迴與高階函式上而不是基於副作用的迴圈流程上。Clojure不支援自動尾呼叫最佳化，因為JVM還不支援該項最佳化，但是，可以用codice_1關鍵字顯式地執行該項最佳化。 對於並行與併發計算，Clojure提供軟體事務記憶體、 響應式代理系統及基於通道的併發程式設計。Clojure 1.7引入了讀取器條件表示式從而允許在同一名字空間中嵌入Clojure與ClojureScript程式碼。 變換歸納器的加入則提供了另一種組合變換的方法。變換歸納器可以使高階函式更加抽象從而使之獨立於其輸入資料傳統地說，這些函式一般被應用於序列上，而變換歸納器允許這些函式被應用於通道上並讓使用者定義她們自己的變換歸納模型。平臺.Clojure的主要平臺是Java但也存在其他目標平臺上的實現。其中，最值得關注的是ClojureScript。 人氣較高的ClojureScript專案包括React實現，如Reagent、re-frame、Rum及Om。人氣.隨著對函數語言程式設計的興趣的持續升溫，Clojure也越來越多地受到Java平臺上的軟體開發人員的青睞。該語言也一度成為知名軟體開發老將的首選或推薦語言，如Brian Goetz、Eric Evans、詹姆斯·高斯林、保羅·格雷厄姆及Robert C. Martin等人。在由Snyk和Java Magazine合作編寫的“JVM生態系統報告2018”。業內使用Clojure的公司有蘋果公司、Atlassian、Funding Circle、Netflix、 Puppet 、沃爾瑪及其他大型軟體公司與美國國家航空航天局等政府機構。Clojure也一度被用於創意計算，包括視覺藝術、音樂、遊戲和詩歌。美國知名軟體諮詢公司ThoughtWorks在為其“技術雷達”評估函數語言程式設計語言時表達了他們對Clojure的青睞，稱其為“Lisp在JVM上的簡單及優雅實現”，並在2012年將其狀態提升為“採用”。越來越多的非官方和/或實驗性的其他平臺實現也驗證了該語言的人氣：開發工具.Clojure的開發工具在近幾年得到了顯著的改善。以下是目前最具人氣的整合開發環境/編輯器及其Clojure外掛。這些工具的結合為Clojure程式設計提供了出色的支援。除了社群提供的開發工具之外，官方的命令列介面工具也隨著Clojure 1.9一起釋出並可在GNU/Linux、macOS及Windows上使用。功能示例.以下示例均可在Clojure REPL中執行簡單性.由於強調簡單性，典型的Clojure程式主要包括函式和簡單的資料結構：(defn -main ; 函式名[&amp; args] ; 引數向量 (`&amp;`表示可變引數)(println "Hello, World!")) ; 函式體REPL程式設計.與其他Lisp一樣，Clojure的標誌性特徵之一是基於REPL的互動式程式設計。在以下示例中，codice_4表示一行註釋的開始，而codice_5則表示輸出：clojure.core/remremainder of dividing numerator by denominator.(defn rem"remainder of dividing numerator by denominator."{:added "1.0":static true[num div](. clojure.lang.Numbers (remainder num div)))執行時可用的名字.與Clojure不同，其他語言的編譯器會將程式中的名字編譯掉使得它們在執行時不可用。而在Clojure中，可以用普通的資料結構對其執行時進行觀察：:doc "返回`(rem n 10)`",:line 1,:column 1,:file "user.clj",:name foo,程式碼即資料.與其他Lisp類似，Clojure也具有同像性。從下面的示例中可以看到，用Clojure編寫程式碼從而修改程式碼本身是非常容易的：(map (fn [form](case form1 'one+ 'plus))(quote (+ 1 1)))富有表現力的資料變換運算子.穿梭宏可以在語法上表達一個數據集在一系列變換間穿梭的抽象：(map inc)(filter even?))利用變換歸納器也可以更有效地實現該過程：(filter even?))(range 10))標識及其狀態的執行緒安全管理.執行緒安全的唯一序列號生成器：(defn generate-unique-id"每次呼叫會返回一個唯一數字ID。"(swap! i inc))宏.codice_9的一個匿名子類:(def bit-bucket-writer(proxy [java.io.Writer] [](write [buf] nil)(close [] nil)(flush [] nil)))(defmacro noprint"在對給定的`forms`求值的同時靜音所有向`*out*`列印的內容。"[&amp; forms]`(binding [*out* bit-bucket-writer]~@forms))(noprint(println "Hello, nobody!"))Java互操作.作為其主要設計目標之一，Clojure從一開始就將其宿主平臺視為其不可分割的一部分。Clojure與Java之間出色的互操作即得益於此：(.put "apple" 1)(.put "banana" 2))(.add 1)(.add 2)(.add 3)))(javax.swing.JOptionPane/showMessageDialognil"Hello, World!")軟體事務記憶體.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)))[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