天天看點

《Clojure程式設計樂趣》—— 第1章,第1.1節Clojure之道

本節書摘來自異步社群《clojure程式設計樂趣》一書中的第1章,第1.1節1.1 clojure之道,作者 【美】michael fogus , chris houser,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

1.1 clojure之道

clojure程式設計樂趣我們會慢些起步。

clojure是一門觀點鮮明的語言,它并不打算涵蓋所有程式設計範式,也不準備提供清單列出每個重要特性。相反,它隻提供以 clojure 之道解決各種真實問題所需的特性。要從 clojure中獲得最大收益,我們就要寫出遵循語言自身願景的代碼。在本書中,我們會依次介紹語言特性,但我們想說的并不隻是一個特性做了些什麼,更重要的是,為什麼會有這樣的特性,以及如何利用好這樣的特性。

但是,開始之前,我們先來從宏觀上了解一下clojure最重要的哲學基礎。圖1.1列出了rich hickey設計clojure時頭腦中一些大緻的目标,以及為了支援這些目标而内建在語言中的一些更具體的決策。

如圖1.1所示,clojure的總目标由一些支援目标和功能綜合而成,稍後幾節,我們會逐一談及。

《Clojure程式設計樂趣》—— 第1章,第1.1節Clojure之道

圖1.1 clojure的大緻目标:本圖展示了構成clojure哲學的一些概念,以及這些概念之間的互動

1.1.1 簡單

複雜問題很難有一個簡單的解決方案。但是,如果把事情搞得不必要的複雜,即便是有經驗的程式員也會栽倒,這就是“偶然複雜性”,與其相對的是任務的本質複雜性(moseley 2006)。clojure緻力于幫我們解決各種複雜問題,而不引入偶然複雜性,比如,各種資料需求、多并發線程、獨立開發的程式庫等。它還提供了一些工具,減少了一些初看起來像本質複雜性的東西。如此一來,最終的特性集合或許看起來并不簡單,尤其在我們對這些特性還不甚熟悉時,但随着通讀本書,我們認為,你會逐漸體會到clojure去除了多少的複雜性。

偶然複雜性有一個例子,就是現代面向對象程式設計語言的一個發展趨勢,即它要将所有可運作代碼打包在類定義、繼承和類型聲明這樣的層次裡。clojure通過支援“純函數”去除了所有這些東西,所謂純函數就是傳入幾個實參,然後,隻根據這些實參産生一個傳回值。clojure很大一部分就是建構在這樣的函數基礎上的,絕大多數應用也可以如此,這意味着,嘗試解決手頭問題時,需要考慮的東西會更少。

1.1.2 專注

寫代碼總是要和幹擾做鬥争,每當語言讓我們思考文法、運算符優先級、繼承層次結構時,隻會讓幹擾增多。clojure盡力讓一切保持盡可能簡單,無需為探索一個想法經曆“編譯—運作”的循環,無需類型聲明,等等。它還提供了一些工具,讓我們可以改造語言,使詞彙和文法能夠更好地适應問題領域,是以,clojure極具表現力。這種做法影響極大,可以在不犧牲可了解性的前提下,很好地完成一些極其複雜的任務。

之是以能夠保持專注,關鍵一點在于恪守對動态系統的承諾。clojure程式中定義的幾乎所有一切都是可以重新定義的,即便程式尚在運作:函數、多重方法、類型、類型層次結構,甚至java的方法實作。動态重定義這些東西貌似很可怕,尤其是在生産系統上,但它卻為思考如何編寫程式打開了另一種奇妙的可能性。我們可以對不熟悉的api進行更多的實驗和探索,這是一種樂趣,而這種樂趣卻常常為更靜态的語言、漫長的編譯周期所阻礙。

但是,clojure并不隻有樂趣。樂趣隻是一種副産品,更重要的是,它可以讓程式員有能力獲得超乎想象的高效。

1.1.3 實用

某些程式設計語言生來隻為展示學術成果,或是探索某種計算理論。clojure不在此列。rich hickey曾在很多場合說過,在建構有趣且有用的應用方面,clojure是很有價值的。

為達此目标,clojure努力做到務實 — 一種用于完成工作的工具。在clojure裡,如果某一設計決策要在實用和聰明、花哨或是純理論的解決方案進行權衡,勝者往往是那些實用的解決方案。clojure曾試圖讓我們遠離java,但這樣做要在程式員和程式庫之間插入大量api,這種做法可能會讓第三方java程式庫很難用。是以,clojure選擇了另一條路:不做封裝、編譯成相同的位元組碼,能夠直接通路java的類和方法。clojure字元串就是java字元串;clojure函數調用就是java方法調用。這樣做簡單、直接、務實。

使用java虛拟機(jvm)這個決策本身就是一個務實的做法。jvm存在某些技術上的缺陷,諸如啟動時間、記憶體使用、缺乏尾遞歸優化(tail-call optimization,tco)1。但是,它也是一個驚人的務實平台—成熟、快速、部署廣泛。其支援各種硬體和作業系統,擁有數量衆多的程式庫,以及支援工具,由于這個極盡務實的決策,所有這一切都可以為clojure所用。

除了直接的方法調用外,clojure還有proxy、gen-class、gen-interface(參見第10章)、reify、definterface、deftype和defrecord(參見9.3節),為互操作性提供了許多選擇,所有這些都是為了完成工作。務實對clojure很重要,當然,許多其他語言也同樣務實。我們後面會看到clojure擺脫混亂的一些做法,正是這些地方讓它顯得與衆不同。

1.1.4 清晰

下面有一段簡單的代碼,可能是用python寫的:

《Clojure程式設計樂趣》—— 第1章,第1.1節Clojure之道

執行這段代碼之後,x的值是什麼呢?如果假設process沒有改變x的内容,那就應該是[6],對吧?但是,怎樣才能做這樣的假設呢?如果不了解process做了些什麼,調用了怎樣的函數等,我們根本無法确認。

就算process不會改變x的值,這時,再加入多線程,我們還是會有一大堆顧慮。如果在第一行和第三行之間,另一個線程改變了x會怎麼樣?還有更糟糕的,如果在第三行做指派時,某個東西設定了x,那又該如何?你能保證你的平台寫變量是原子操作嗎?或者,是不是最終的值可能是多個寫操作混雜的結果?我們可以抱着獲得某種清晰的想法,将這個思維訓練無休止地進行下去,但結果是一樣的—我們根本無法得到清晰,隻會适得其反:混亂。

clojure為代碼的清晰做着努力,提供了一些工具規避幾種不同的混亂。就剛才描述的那種情況而言,采用它所提供的不變局部量和持久化集合,便可一并消除了單線程和多線程的大部分問題。

當我們所用的語言将不相關的行為合在一個構造裡時,我們不難發現,自己已深陷多種泥潭。clojure通過分離關注點讓我們保持警醒,應對這樣的情況。一旦事物得到分離,思路就會清晰許多,隻在必要時重新組合。從某種程度上說,這樣的做法對某些特定問題非常有用。表1.1将某些語言把概念混雜在一起的正常方式,同clojure類似概念分離的做法進行了對比,本書稍後會對clojure的做法進行更詳盡的解釋。

表1.1 clojure中的分離關注點

《Clojure程式設計樂趣》—— 第1章,第1.1節Clojure之道

有時,很難在腦子裡将這些概念區分開來,但如果能做到的話,就會非常清晰了,為了這種強大和靈活,我們值得努力一試。我們有那麼多不同的概念要處理,以一緻的方式表現代碼和資料就顯得很重要了。

1.1.5 一緻

clojure在兩個具體的方面提供了一緻性:文法和資料結構。

文法一緻性指的是,相關的概念在形式上是類似的。有個簡潔有力的例子,for和doseq這兩個宏之間的文法是一樣的。

它們做的事情不盡相同—for傳回的是一個惰性seq,而doseq隻是為了産生副作用—但二者支援相同的迷你語言(mini-language):嵌套疊代、解構、:when和:while衛語句。比較下面這個例子就不難看出相似性:

《Clojure程式設計樂趣》—— 第1章,第1.1節Clojure之道

這種相似的價值在于,隻要學習一種基本文法即可應對兩種情況,必要時,在兩種用法間切換也會容易許多。

類似地,資料結構的一緻性表現在clojure持久化集合類型的精心設計上,它為各個類型提供了盡可能相似的接口,并盡可能廣泛地去使用這些接口。這種做法實際上是lisp經典的“代碼即資料”哲學的擴充。clojure資料結構不隻可以持有一大堆應用的資料,還可以持有應用自身的一些表達式元素。它們可以描述對form的解構,還可以為各種内建函數提供命名選項(named options)。其他面向對象語言可能會鼓勵應用定義多個彼此不相容的類,以持有不同類型的應用資料,而clojure則鼓勵使用行為上類似于map的對象。

這樣做的好處在于,同樣一套處理clojure資料結構的函數可以用于下列所有情形:大規模資料存儲、應用代碼和應用資料對象。用into可以建構任意類型,用seq可以擷取一個用于周遊的惰性seq,用filter可以選擇滿足特定條件的元素,等等。一旦習慣了所有這些豐富且随處可用的函數,用java或c++處理應用中的person或address這樣的類就會讓人覺得處處掣肘。

簡單、專注、實用、一緻和清晰。

clojure程式設計語言裡幾乎所有元素都是為了提振這些目标。編寫clojure代碼處理真實問題時,請将“簡單、實用、專注”等方面推向極緻,将此銘記于心,我們相信你會發現,clojure就是你到達成功彼岸所需的工具。

1如果你不了解尾遞歸優化是什麼,請不必擔心。如果你知道tco是什麼,也不必擔心jvm在這方面的欠缺會成為lisp或是像clojure這樣函數式語言的緻命缺陷。我們會在7.3節讨論所有這些顧慮。在此之前,放松就好。

2譯注:本小節的标題是清晰,而這段英文繞密碼故意展現出不清晰的效果,這裡不做翻譯。