Clojure

Clojure/ˈklʒər/[15][16]Lisp编程语言Java平台上的现代、动态函数式方言。[17][18] 与其他Lisp一样,Clojure视代码为数据且拥有一套Lisp系统。[19] 其开发过程目前由社区驱动,[20] 其作者里奇·希基(Rich Hickey)则以终身仁慈独裁者(BDFL)的身份进行监督。[21]

Clojure
多重编程范型:
設計者里奇·希基(Rich Hickey)
2007年2007
穩定版本
1.10.1[8]
(2019年6月6日2019-06-06
型態系統
系统平台
許可證Eclipse公共许可证
文件扩展名
  • .clj
  • .cljs
  • .cljc
  • .edn
網站clojure.org
啟發語言
影響語言

Clojure提倡不可变性(immutability)与持久数据结构(persistent data structures)并鼓励程序员显式地管理标识(identity)及其状态(state)。[22] 对利用不可变值(immutable values)及显式时间进展构造(explicit progression-of-time constructs)进行编程的专注旨在促进更加健壮的(尤其是并发)程序的开发。[23][24][15] Clojure的类型系统是完全动态的,但人们近期也开始探索其基于渐进类型化(gradual typing)的实现。[25]

对Clojure的商业支持由Cognitect公司提供。[26] 每年都会在全球范围内举办年度Clojure会议,其中最著名的是Clojure/conj[27]

历史与开发过程

里奇·希基,Clojure的创造者

里奇·希基(Rich Hickey)是Clojure语言的创造者。[17] 在Clojure之前,他开发过类似但基于.NET平台的项目——dotLisp。[28] 在那之前,他还有过三次旨在在Lisp与Java之间提供互操作(interoperability)的尝试:“Common Lisp的Java外语接口”[Java foreign language interface for Common Lisp (jfli)][29]、“Lisp的外语对象接口”[Foreign Object Interface for Lisp (FOIL)][30]以及“Lisp友好的Java Servlet接口”[Lisp-friendly interface to Java Servlets (Lisplets)][31]

在公开发布之前,里奇·希基在Clojure的工作上花了大约两年半的时间。期间,在没有外部资金的情况下,他将其大部分时间都专门投入到了Clojure的工作上。在这段时间结束时,里奇·希基以电子邮件的形式向他在Common Lisp社区里的一些朋友宣布了该语言。

Clojure的开发过程由社区驱动[20]并且在Clojure JIRA项目网页上进行管理。[32] 该网站可用于提交问题报告(issue)。一般的开发讨论是在“Clojure谷歌网上论坛”(Clojure Google Group)上进行的。[33] 任何人都可以提交错误报告和想法,而贡献补丁(patches)则需要先签署“Clojure贡献者协议”(Clojure Contributor Agreement)。[34] JIRA错误报告(JIRA ticket)由一个筛选者(screener)团队进行处理并最终由里奇·希基批准更改。[35]

设计理念

里奇·希基开发Clojure的原因是因为他想要一款适合函数式编程的现代Lisp。该语言既需要与已建立的Java平台共生又需要有适合并发性的设计。[23][24][36][15]

Clojure对待变化(change)的方式以标识(identity)的概念为特征。[22] 标识是指随着时间的推移而产生的一系列状态(state)。而状态则是指标识在某一特定时间点上的值(value)。需要强调的是,这里的值是不可变的(immutable)。由此引申,由于状态是不可变的值,任意数量的工作单位(workers)都可以在其上以并行(parallel)的方式实施操作。因此,并发性(concurrency)就成为一道管理状态间变化的问题[注意,这里的“变化”是指从一个状态到另外一个状态的跃迁(transition)而不是状态本身的变化(mutation)。]为此,Clojure提供了几个可变的(mutable)引用类型(reference type)。每个引用类型都有其明确定义的语义用于控制状态之间的跃迁。[22]

语言概述

版本 发布日期 主要功能/改进
2007年10月16日[37] 首次公开发布
1.0 2009年5月4日[38] 首个稳定版
1.1 2009年12月31日[39] 将来(future)
1.2 2010年8月19日[40] 协议(protocol)
1.3 2011年9月23日[41] 增强对原始类型(primitive type)的支持
1.4 2012年4月15日[42] 读取器字面量(reader literal)
1.5 2013年3月1日[43] 归纳器(reducer)
1.5.1 2013年3月10日[44] 修复内存泄漏
1.6 2014年3月25[45] Java API、经过改进的哈希算法
1.7 2015年6月30日[46] 变换归纳器(transducer)、读取器条件表达式(reader conditionals)
1.8 2016年1月19日[47] 附加的字符串函数、直接连接(direct linking)、套接字服务器(socket server)
1.9 2017年12月8日[48] 集成spec、命令行工具
1.10 2018年12月17日[49] 经过改进的错误报告、Java兼容性
當前版本: 1.10.1 2019年6月6日[8] 解决Java性能回归问题并改进clojure.main的错误报告
格式:
舊版本
舊版本,仍被支援
当前版本
最新的预览版
未来版本

Clojure执行于Java平台之上,因此,与Java紧密集成并完全支持从Clojure调用Java代码。[50][15] 与此同时,也可以从Java调用Clojure代码。[51] Leiningen是社区中普遍使用的项目自动化工具。Leiningen为Maven集成提供支持,处理项目包管理和依赖项。Leiningen的配置使用的则是Clojure语法。[52]

与其他大多数Lisp一样,Clojure的语法建立在S-表达式之上。S-表达式在被编译之前先由读取器(reader)解析为数据结构。[53][15] 除了列表(list)之外,Clojure的读取器还支持映射(map)、集合(set)及向量(vector)等的字面量(literal)语法。这些字面量随后会被直接编译成上述数据结构。[53] Clojure是Lisp-1且有一套与其它Lisp不兼容的数据结构,因此,Clojure不支持与Lisp的其它方言之间的代码级兼容性。[19]

作为一门Lisp方言,函数在Clojure中是一等公民。此外,Clojure还支持读取﹣求值﹣输出循环(REPL)以及一套宏系统。[6] Clojure的Lisp宏系统与Common Lisp的系统极为相似。唯一不同的是,Clojure的重音符[称为“语法引用”(syntax quote)]用名字空间(namespace)来限定符号(symbol)。这有助于防止意外的名字捕获(unintended name capture),因为Clojure禁止绑定(binding)到用名字空间限定的名字(namespace-qualified name)上。如果需要强制捕获宏扩展(capturing macro expansion,)那么就需要显示地完成该过程。Clojure不支持用户定义的读取器宏(reader macro,)但Clojure的读取器支持更具约束力的语法扩展(syntactic expansion)形式。[54] Clojure支持多重方法(multimethods。)[55] 对于类似接口的抽象,Clojure提供基于协议(protocol)[56]的多态性(polymorphism)以及基于记录(record)[57]的数据类型系统。 Clojure通过这些设计来提供高性能且动态的多态性以避免所谓的“表达式问题”("expression problem"。)

Clojure支持惰性序列,并鼓励不可变性(immutability)与持久数据结构(persistent data structure。)Clojure作为一门函数式编程语言将重点放在递归高阶函数上而不是基于副作用循环流程上。Clojure不支持自动尾调用优化,因为JVM还不支持该项优化,[58][59][60]但是,可以用recur关键字显式地进行该项优化。[61] 对于并行并发计算,Clojure提供软件事务内存[62] 响应式代理系统[1]及基于通道(channel)的并发编程。[63]

Clojure 1.7引入了读取器条件表达式(reader conditional)从而允许在同一名字空间中嵌入Clojure与ClojureScript代码。[46][53] 变换归纳器(transducer)的加入则提供了另一种组合变换的方法。变换归纳器可以使高阶函数(如,mapfold)更加抽象从而使之独立于其输入数据源。传统地说,这些函数一般被应用于序列(sequence)上,而变换归纳器允许这些函数被应用于通道(channel)上并让用户定义她们自己的变换归纳(transduction)模型。[64][65][66]

平台

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

人气

随着对函数式编程的兴趣的持续升温,Clojure也越来越多地受到Java平台上的软件开发人员的青睐。该语言也一度成为知名软件开发老将,如Brian Goetz[78][79][80]、Eric Evans[81][82]詹姆斯·高斯林(James Gosling)[83]保罗·格雷厄姆(Paul Graham)[84]及Robert C. Martin(俗称“鲍勃大叔(Uncle Bob)”)[85][86][87][88]等,的首选或推荐语言。

在由Snyk和Java Magazine合作编写的“JVM生态系统报告2018”(据称是“Java开发人员有史以来规模最大的调查”)中,Clojure被评为用于“主要应用程序”的第二大人气编程语言(仅次于Java)。[89]

业内使用Clojure的公司有苹果公司[90][91]Atlassian[92]、Funding Circle[93]Netflix[94]、 Puppet [95]沃尔玛[96]及其他大型软件公司[97]美国国家航空航天局[98]等政府机构。Clojure也一度被用于创意计算,包括视觉艺术、音乐、游戏和诗歌。[99]

美国知名软件咨询公司ThoughtWorks在为其“技术雷达”[100]评估函数式编程语言时表达了他们对Clojure的青睐,称其为“Lisp在JVM上的简单及优雅实现”,并在2012年将其状态提升为“采用”(“ADOPT”)[101]

越来越多的非官方和/或实验性的其他平台实现也验证了该语言的人气:

  • CljPerl[102]:Clojure的Perl实现
  • Clojerl[103]:BEAM(Erlang虚拟机)上的Clojure
  • clojure-py[104]:Clojure的纯Python实现
  • Ferret[105]:可被编译成运行于微控制器的自包含(self-contained)C++11
  • Joker[106]:用Go实现的解释器(interpreter)和linter
  • Las3r[107]:执行于ActionScript虚拟机(Adobe Flash Player平台)的Clojure子集
  • Pixie[108]:受Clojure启发并用RPython实现的Lisp方言
  • Rouge[109]:Clojure基于YARV的Ruby实现

开发工具

Clojure的开发工具在近几年得到了显著的改善。以下是目前最具人气的集成开发环境(IDE)/编辑器及其Clojure插件。[110]这些工具的结合为Clojure编程提供了出色的支持。

集成开发环境/编辑器及其Clojure插件
集成开发环境/编辑器 Clojure插件
Atom Chlorine[111]
Emacs CIDER[112]
IntelliJ IDEA Clojure-Kit[113]或Cursive[114](提供免费的非商业许可证)
Light Table[115] (不适用)
Vim fireplace.vim[116][117], vim-iced[118]或Conjure(仅限Neovim)[119][120]
Visual Studio Code Calva[121]

除了社区提供的开发工具之外,官方的命令行界面(CLI)工具[122]也随着Clojure 1.9一起发布并可在GNU/Linux、macOS及Windows上使用。[123]

功能示例

以下示例均可在Clojure REPL中运行(如,使用Clojure命令行界面工具[122]启动的REPL或在REPL.it[124]上提供的在线REPL。)

简单性

由于强调简单性,典型的Clojure程序主要包括函数和简单的数据结构(即列表,向量,映射和集合):

;; 一个典型的Clojure程序的入口
(defn -main ; 函数名
  [& args] ; 参数向量 (`&`表示可变参数)
  (println "Hello, World!")) ; 函数体

REPL编程

与其他Lisp一样,Clojure的标志性特征之一是基于REPL的交互式编程。[125]在以下示例中,;;表示一行注释的开始,而;; =>则表示输出:

;; 定义一个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: 1000
nil

参考文献

  1. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  2. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  3. Hickey, Rich; contributors. . GitHub.com. [2019-07-07]. (原始内容存档于2019-06-16).
  4. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  5. Nolen, David; Hickey, Rich; contributors. . GitHub.com. [2019-07-07]. (原始内容存档于2019-06-14).
  6. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  7. Esterhazy, Paulus. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  8. Miller, Alex. . Clojure.org. 2019-06-06 [2019-06-08]. (原始内容存档于2019-06-07).
  9. Fogus, Michael. . CodeQuarterly.com. 2011 [2019-07-08]. (原始内容存档于2017-01-11).
  10. Hickey, Rich. . Amazon.com. [2019-07-07]. (原始内容存档于2017-10-03).
  11. Bonnaire-Sergeant, Ambrose. (Thesis). The University of Western Australia. 2012.
  12. (PDF). OReilly.com. [2013-04-30]. (原始内容存档 (PDF)于2015-05-21).
  13. Baldridge, Timothy. . PixieLang.org. [2019-07-07]. (原始内容存档于2019-07-06).
  14. Ramachandra, Ramkumar. . GitHub.org. [2019-07-07]. (原始内容存档于2019-09-24).
  15. Edwards, Kathryn. . Computerworld.com.au. 2009-08-10 [2019-09-11]. (原始内容存档于2019-08-26).
  16. Hickey, Rich. . Google.com. 2009-01-05.
  17. Krill, Paul. . InfoWorld.com. 2012-03-22 [2019-07-08]. (原始内容存档于2019-07-08).
  18. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-03).
  19. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-08).
  20. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-07).
  21. Hickey, Rich. . GitHub.com. 2018-11-26 [2019-07-08]. (原始内容存档于2019-05-26).
  22. . Clojure.org. [2019-07-07]. (原始内容存档于2016-01-02).
  23. Hickey, Rich. . Clojure.org. [2019-07-07]. (原始内容存档于2016-01-04).
  24. Torre, Charles. . MSDN.com. 2009-10-06 [2018-11-01]. (原始内容存档于2018-11-03).
  25. . GitHub.com. [2019-07-07]. (原始内容存档于2018-10-09).
  26. . Cognitect.com. [2019-07-07]. (原始内容存档于2019-07-05).
  27. . Clojure-Conj.org. [2019-07-07]. (原始内容存档于2015-09-13).
  28. Hickey, Rich. . Google.com. 2002-10-16 [2018-11-03]. (原始内容存档于2011-01-22).
  29. Hickey, Rich. . SourceForge.net. 2013-04-15 [2018-11-03]. (原始内容存档于2018-11-03).
  30. Hickey, Rich. . SourceForge.net. 2013-04-03 [2018-11-03]. (原始内容存档于2018-11-03).
  31. Hickey, Rich. . SourceForge.net. 2013-03-07 [2018-11-03]. (原始内容存档于2018-11-03).
  32. . Atlassian.net. [2019-07-07]. (原始内容存档于2019-06-23).
  33. . Google.com. [2019-07-07]. (原始内容存档于2011-01-22).
  34. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  35. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  36. Elmendorf, Dirk. . LinuxJournal.com. 2010-04-01 [2018-11-03]. (原始内容存档于2018-11-03).
  37. Hickey, Rich. . BlogSpot.com. 2007-10-16 [2018-11-04]. (原始内容存档于2018-11-04).
  38. Hickey, Rich. . BlogSpot.com. 2009-05-04 [2018-11-04]. (原始内容存档于2018-11-04).
  39. Hickey, Rich. . BlogSpot.com. 2009-12-31 [2018-11-04]. (原始内容存档于2018-11-04).
  40. Hickey, Rich. . Google.com. 2010-08-19 [2018-11-03]. (原始内容存档于2011-01-22).
  41. Redinger, Christopher. . Google.com. 2011-09-23 [2018-11-03]. (原始内容存档于2011-01-22).
  42. Dipert, Alan. . Google.com. 2012-04-17 [2018-11-03]. (原始内容存档于2011-01-22).
  43. Halloway, Stuart. . Google.com. 2013-03-01 [2018-11-03]. (原始内容存档于2011-01-22).
  44. Halloway, Stuart. . Google.com. 2013-03-10 [2018-11-03]. (原始内容存档于2011-01-22).
  45. Miller, Alex. . Google.com. 2014-03-25 [2018-11-03]. (原始内容存档于2011-01-22).
  46. Miller, Alex. . Clojure.org. 2015-06-30 [2019-07-08]. (原始内容存档于2019-07-05).
  47. Miller, Alex. . Clojure.org. 2016-01-19 [2019-07-08]. (原始内容存档于2019-07-05).
  48. Miller, Alex. . Clojure.org. 2017-12-08 [2019-07-08]. (原始内容存档于2019-07-05).
  49. Miller, Alex. . Clojure.org. 2018-12-17 [2018-12-17]. (原始内容存档于2018-12-17).
  50. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  51. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  52. Hagelberg, Phil; contributors. . Leiningen.org. [2019-07-07]. (原始内容存档于2019-07-16).
  53. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  54. Hickey, Rich. . GitHub.com. [2019-07-07]. (原始内容存档于2019-03-02).
  55. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  56. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  57. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  58. Goetz, Brian. . YouTube.com. 2014-11-20 [2018-11-04]. (原始内容存档于2016-12-19).
  59. Rose, John. . Oracle.com. 2007-07-12 [2018-11-04]. (原始内容存档于2018-11-04).
  60. Rose, John. . Java.net. 2009-02-11 [2018-11-04]. (原始内容存档于2018-11-04).
  61. . Clojure.org. [2019-07-07]. (原始内容存档于2019-07-02).
  62. . Clojure.org. [2019-07-07]. (原始内容存档于2016-01-09).
  63. Hickey, Rich. . Clojure.org. 2013-06-28 [2019-07-08]. (原始内容存档于2019-07-05).
  64. Hickey, Rich. . YouTube.com. 2014-09-17 [2018-11-04]. (原始内容存档于2018-10-01).
  65. Hickey, Rich. . Cognitect.com. 2014-08-06 [2019-07-08]. (原始内容存档于2019-07-07).
  66. Hickey, Rich. . YouTube.com. 2014-11-20 [2018-11-04]. (原始内容存档于2018-12-07).
  67. . ClojureScript.org. [2019-07-06]. (原始内容存档于2019-07-01).
  68. . ClojureScript.org. [2018-02-04]. (原始内容存档于2018-02-05).
  69. . GitHub.com. [2012-06-28]. (原始内容存档于2013-08-25).
  70. Emerick, Chas. . CEmerick.com. 2013-11-18 [2018-11-04]. (原始内容存档于2018-10-29).
  71. . WuFoo.com. [2015-09-17]. (原始内容存档于2015-10-19).
  72. Gehtland, Justin. . Cognitect.com. 2016-01-28 [2018-11-04]. (原始内容存档于2018-11-04).
  73. . GitHub.io. [2019-07-06]. (原始内容存档于2019-07-18).
  74. . GitHub.com. [2019-07-06]. (原始内容存档于2019-07-12).
  75. Prokopov, Nikita. . GitHub.com. [2019-07-06]. (原始内容存档于2019-12-22).
  76. Nolen, David. . GitHub.com. [2019-07-06]. (原始内容存档于2019-06-19).
  77. Coupland, Tom. . InfoQ.com. 2014-01-17 [2019-07-08]. (原始内容存档于2019-07-06).
  78. Goetz, Brian. . Twitch.tv. 2020-05-24 [2020-08-07]. (原始内容存档于2020-07-28).
  79. Goetz, Brian. . Twitch.tv. 2020-05-24 [2020-08-07]. (原始内容存档于2020-07-28).
  80. Goetz, Brian. . Twitch.tv. 2020-05-24 [2020-08-07]. (原始内容存档于2020-07-28).
  81. Evans, Eric. . YouTube.com. 2018-08-14 [2019-11-10]. (原始内容存档于2019-09-30).
  82. Evans, Eric. . Twitter.com. 2014-11-21.
  83. . YouTube.com. 2016-10-11 [2019-06-08]. (原始内容存档于2019-09-24).
  84. Graham, Paul. . Twitter.com. 2016-05-06 [2019-06-08]. (原始内容存档于2020-01-27).
  85. Martin, Robert. . CleanCoder.com. 2019-08-22 [2019-08-23]. (原始内容存档于2019-08-23).
  86. Martin, Robert. . Twitter.com. 2018-11-29 [2019-06-08]. (原始内容存档于2019-09-24).
  87. Martin, Robert. . CleanCoders.com. 2018-08 [2019-07-08]. (原始内容存档于2019-07-08).
  88. Martin, Robert. . CleanCoder.com. 2017-07-11 [2019-07-08]. (原始内容存档于2019-07-08).
  89. Maple, Simon; Binstock, Andrew. . Snyk.io. 2018-10-17 [2019-06-08]. (原始内容存档于2019-06-08).
  90. Liutikov, Roman. . Twitter.com. 2017-12-17 [2018-11-02]. (原始内容存档于2019-02-15).
  91. . Apple.com. [2019-07-06]. (原始内容存档于2018-12-08).
  92. Borges, Leonardo. . YouTube.com. 2015-07-07 [2019-06-08]. (原始内容存档于2018-09-06).
  93. Pither, Jon. . JUXT.pro. 2016-10-04 [2018-11-02]. (原始内容存档于2018-12-08).
  94. Williams, Alex. . TheNewStack.io. 2014-08-03 [2019-06-08]. (原始内容存档于2019-06-08).
  95. Price, Chris. . Puppet.com. 2014-04-11 [2020-08-06]. (原始内容存档于2019-09-03).
  96. Phillips, Marc. . Cognitect.com. 2015-07-14 [2018-11-03]. (原始内容存档于2018-11-03).
  97. . Clojure.org. [2018-10-27]. (原始内容存档于2018-10-28).
  98. . GitHub.com. [2019-07-06]. (原始内容存档于2019-03-22).
  99. Meier, Carin. . OReilly.com. 2015-05-06 [2018-11-04]. (原始内容存档于2018-10-29).
  100. . ThoughtWorks.com. [2019-02-10]. (原始内容存档于2019-02-07).
  101. . ThoughtWorks.com. [2019-02-10]. (原始内容存档于2019-02-12).
  102. Hu, Wei. . MetaCPAN.org. [2019-07-06]. (原始内容存档于2019-06-04).
  103. Facorro, Juan. . GitHub.com. [2019-07-06]. (原始内容存档于2019-06-20).
  104. Baldridge, Timothy. . GitHub.com. [2019-07-06]. (原始内容存档于2019-06-20).
  105. Akkaya, Nurullah. . Ferret-Lang.org. [2019-07-06]. (原始内容存档于2019-07-06).
  106. Bataev, Roman. . Joker-Lang.org. [2019-07-06]. (原始内容存档于2019-09-24).
  107. Cannon, Aemon. . GitHub.com. [2019-07-06]. (原始内容存档于2019-05-11).
  108. Baldridge, Timothy. . PixieLang.org. [2019-07-06]. (原始内容存档于2019-07-06).
  109. Connor, Ashe. . GitHub.com. [2019-07-06]. (原始内容存档于2018-06-11).
  110. Miller, Alex. . Clojure.org. 2019-02-04 [2019-07-05]. (原始内容存档于2019-07-05).
  111. Szabo, Maurício. . Atom.io. [2019-07-05]. (原始内容存档于2019-07-05).
  112. Batsov, Bozhidar; contributors. . CIDER.mx. [2019-07-05]. (原始内容存档于2019-06-08).
  113. Shrago, Greg. . JetBrains.com. [2019-07-05]. (原始内容存档于2019-05-31).
  114. Fleming, Colin. . JetBrains.com. [2019-07-05]. (原始内容存档于2019-05-20).
  115. . LightTable.com. [2019-07-05]. (原始内容存档于2019-07-03).
  116. Pope, Tim. . VIM.org. [2019-07-05]. (原始内容存档于2019-08-22).
  117. Monroe, Dominic. . JUXT.pro. 2016-12-13 [2019-07-15]. (原始内容存档于2019-07-14).
  118. Masashi, Iizuka. . GitHub.com. [2020-03-13]. (原始内容存档于2020-04-10).
  119. Caldwell, Oliver. . GitHub.com. [2019-11-09]. (原始内容存档于2019-11-22).
  120. Caldwell, Oliver. . oli.me.uk. 2019-11-06 [2019-11-10]. (原始内容存档于2019-11-11).
  121. Strömberg, Peter. . VisualStudio.com. [2019-07-05]. (原始内容存档于2019-08-22).
  122. Miller, Alex. . Clojure.org. [2019-07-08]. (原始内容存档于2019-07-02).
  123. Miller, Alex. . Cognitect.com. 2017-12-08 [2019-07-05]. (原始内容存档于2019-07-05).
  124. . REPL.it. [2019-07-08]. (原始内容存档于2019-07-09).
  125. . Clojure.org. [2018-12-04]. (原始内容存档于2018-12-04).

延伸阅读

  • Sotnikov, Dmitri, 3rd, Pragmatic Bookshelf, 2020, ISBN 978-1-68050-682-2
  • Olsen, Russ, , Pragmatic Bookshelf, 2018 [2020-08-07], ISBN 978-1-68050-300-5, (原始内容存档于2020-08-07)
  • Miller, Alex; Halloway, Stuart; Bedra, Aaron, 3rd, Pragmatic Bookshelf, 2018 [2020-08-07], ISBN 978-1-68050-246-6, (原始内容存档于2020-08-07)
  • Rathore, Amit; Avila, Francis, 2nd, Manning, 2015, ISBN 978-1-61729-152-4
  • Higginbotham, Daniel, , No Starch Press, 2015 [2018-11-05], ISBN 978-1-59327-591-4, (原始内容存档于2018-11-29)
  • Gamble, Julian, , Pearson Publishing, 2015 [2018-11-05], ISBN 978-0-32192-773-6, (原始内容存档于2018-11-06)
  • Vandgrift, Ben; Miller, Alex, , Pragmatic Bookshelf, 2015 [2020-08-07], ISBN 978-1-68050-074-5, (原始内容存档于2020-08-07)
  • Rochester, Eric, 2nd, Packt Publishing, 2015 [2018-11-05], ISBN 978-1-78439-029-7, (原始内容存档于2018-11-05)
  • Fogus, Michael; Houser, Chris, 2nd, Manning, 2014 [2018-11-05], ISBN 1-617291-41-2, (原始内容存档于2018-10-23)
  • Kelker, Ryan, , Packt Publishing, 2013 [2020-08-07], ISBN 978-1-78216-650-4, (原始内容存档于2020-07-28)
  • Rochester, Eric, , Packt Publishing, 2014 [2018-11-05], ISBN 978-1-78328-413-9, (原始内容存档于2018-11-05)
  • Emerick, Chas; Carper, Brian; Grand, Christophe, , O'Reilly Media, April 19, 2012 [2018-11-05], ISBN 1-4493-9470-1, (原始内容存档于2019-01-07)
  • VanderHart, Luke; Sierra, Stuart, , Apress, June 7, 2010 [2018-11-05], ISBN 978-1-4302-7231-1, (原始内容存档于2018-11-05)

外部链接

官方网站

This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.