請評價一下Clojure語言的設計?
在嘗試了一段時間的 Scheme 之后偶爾用了一下 Clojure,發現 Clojure 有些設計實在是太好用了, 比如之前經常會去找 Scheme 上 let 的擴展實現,其實 Clojure 的 let 實現的 destructuring binding 正是我想要的功能, Clojure 的實現更加完整統一。所以我想知道
1. Clojure 能夠脫穎而出最主要原因是什么,
2. 評價一下 Clojure 中的一些設計, 比如 STM 等,
3. Clojure 有哪些設計上的不足(如果有), 或者說這么問,Scheme 中有哪些優秀的東西是 Clojure 沒有的(如果有)?
1. Clojure 能夠吸引人的很重要一點是它是 JVM 之上的語言,這個決定非常關鍵。
首先,因為根植于 JVM 之上,并且做到了跟 Java 語言的相互調用,它能吸引很多成熟的 Java 開發者。其次,它可以使用 Java 社區豐富的開源軟件,不需要從頭去構建一個社區,你可以看到很多 Clojure 開源代碼都是簡單地包裝 Java 的開源包,但是通過 Clojure 高度抽象簡單的語法提供更便利的使用的方式;
第三,由于 JVM 平臺本身的高度成熟和優化,clojure 的編譯器生成的 byte code 跟 Java 編譯器生成的 byte code 并無二致(不完全是),它的性能和穩定性也能馬上得到保證,這比從頭構建一個新平臺成本低得多。
構建于 JVM 之上,Clojure 就是一門“嚴肅”的語言,而非很多人眼中的 Lisp“玩具”語言,你學習后可以馬上使用并且實踐。但是 Clojure 又是 Lisp 方言,Lisp 的神奇能力它還都保留,這樣兼具美學和實用的語言如何讓人不愛?我相信很多熟悉 Scheme 之類方言的童鞋,并且有 Java 背景的,都會對 Clojure 有相見恨晚的感覺。
2. Clojure 的設計原則可以概括成 5 個詞匯:簡單、專注、實用、一致和清晰。這不是我概括的,而是《The joy of clojure》概括的。
- 簡單: 鼓勵純函數,極簡的語法(少數 special form),個人也認為 clojure 不能算是多范式的語言(有部分 OO 特性),為了支持多范式引入的復雜度,我們在 C++ 和 Scala 身上都看到了。
- 專注:前綴運算符不需要去考慮優先級,也沒有什么菱形繼承的問題,動態類型系統(有利有弊),REPL 提供的探索式編程方法(告別修改/編譯/運行的死循環,所見即所得)。
- 實用:前面提到,構建在 JVM 之上,跟 Java 語言的互操作非常容易。直接調用 Java 方法,不去發明一套新的調用語法,努力規避 Java 語言中繁瑣的地方(doto,箭頭宏等等)。
- 清晰:純函數(前面提到),immutable var,immutable 數據結構,STM 避免鎖問題。不可變減少了心智的負擔,降低了多線程編程的難度,純函數也更利于測試和調試。
- 一致:語法的一致性:例如 doseq 和 for 宏類似,都支持 destructring,支持相同的 guard 語句(when,while)。數據結構的一致性:sequence 抽象之上的各種高階函數。 </ul>
具體到 STM,我個人認為這個特性在日常編程中,其實你用到的機會不多。在 web 編程里,你的并發模型 Web Container 已經幫你處理(tomcat,jetty),事務也是數據庫幫你處理,幾乎找不到場合去使用 STM。這個特性在做一些中間件或者底層 framework 的時候才可能用到。這個特性的設計上面已經提到,跟 clojure 的設計目標是緊密相關的,跟 immutable 數據結構也是密不可分,同時它也不是沒有代價,事務歷史記錄和慢事務頻繁回滾的代價,有時候你還是需要退回去使用 Java 那套鎖機制,慶幸的是 Clojure 不阻止你去使用,并且提供了類似 locking 這樣的宏來方便你使用。
3. Scheme 我對它的了解也就是做過 SICP 的習題,粗粗看過《Programming Scheme》,兩者對比的優缺點似乎談不上來。需要對 Scheme 更熟悉的專家來做個對比。
Clojure 的設計缺陷不能說是缺陷,這是由于它設計的目標決定的,有得必有失。
首先還是 JVM,基于 JVM 有種種好處,但是 JVM 的啟動速度實在悲劇,因此用 Clojure 寫一些小的 script 處理日常事務,顯得還是不夠得心應手,這樣的工作我還是用 Ruby,Python 的腳本語言來搞定更便捷。不過目前 Clojure 有一些其他語言之上的實現,比如 rouge-lang/rouge · GitHub 和 halgari/clojure-py 路 GitHub 這些實現應該會比 JVM 的啟動快很多(抱歉,我沒測試過)。
不僅如此,因為 Clojure 跟 JVM 平臺的綁定如此之深,并且為了真正發揮 Clojure 的威力,你還需要去熟悉 Java 平臺的東西,熟悉 Java 語言、類庫、內存模型、GC 優化、多線程和網絡編程、開源類庫等等。可以這樣認為:想成為一個好的 Clojure 程序員,首先需要是一名好的 Java 程序員。這也一定程度上阻礙了 Clojure 的推廣,提高了學習成本。
其次,Clojure 的 API 設計上,有時候不符合你的直覺,而是符合 Clojure 的哲學,比如 contains?函數對 vector 等數組型集合的調用上。關于這一點,Rich 的回答是“Elegance and familiarity are orthogonal.”,也就是優雅和熟悉是正交關系的。保持 API 內在的一致性,比直覺的“熟悉”更重要。Clojure 不妥協于你的“直覺”。
第三,弱類型的好處足夠多,靈活,減少聲明代碼,適合探索式編程;同樣,壞處也不是沒有,沒有類型保障,錯誤可能要等到運行時才能發現,靜態代碼檢查工具也沒有辦法幫你發現,這就需要你一定程度的測試代碼來保證運行時行為。
第四,性能上,雖然 clojure 生成的字節碼已經很高效,也有 type hint 這樣的技術來幫助提升性能,但是會有不少的轉型(checkcast)、裝箱和拆箱(boxing and unboxing)以及類型判斷分支跳轉的多余指令,這在一些性能敏感的應用里可能會暴露出來。盡管我認為大多數網站型的應用瓶頸都會落在 IO 上。
以上是我對這個問題的一些看法,歡迎探討。謝謝。
P.S. 我們的整個網站幾乎都是基于 clojure 架構的,有興趣可以看下 AVOS Cloud,每天承載的請求量也在億次級別。
<span id="shareA4" class="fl">
</span>