為函數命名
- 函數與資料範圍 (data scope) 的關系
- 純函數與副作用函數
- 函數命名與 namespace 命名之關系 --- 狹窄性
函數與資料範圍 (data scope) 的關系
在程式的執行期間,資料範圍 (data scope) 是指可以在我們所在的線程 (thread) 看得到的所有資料,包含了 function parameters
和 let-bound values
和 closed-over values
和 global vars
。函數通常對資料範圍做三種不同的事:
- pull: 從外界拉資料進入資料範圍,比方說 http get request
- push: 從資料範圍推資料到外界,比方說 http post request
- transform: 轉換已經存在於資料範圍的資料
純函數與副作用函數
如果要讓函數可以被組合,函數最好要只做 pull, push, transform 三件事中的其中一件事而已。只做 transform 的函數是『純函數』。會有 pull 或是 push 的函數是副作用函數。
- 純函數的命名,可以利用
->
符號來表現transform
。比方說payload->base64
- 副作用函數的命名,通常會在函數名稱裡,帶有副作用的動詞。比方說
get-payload
函數命名與 namespace 命名之關系 --- 狹窄性
理論上,一個 namespace 可以擁有無限個函數。然而,實務上,一個 namespace 只應該擁有帶有相同用途 (purpose) 的函數,如此一來,函數命名時,就可以從包含它們的 namespace 之命名取得狹窄性 (narrowness)
所有存在於同一個 namespace 的函數應該操作同一個資料範圍 (datascope),或是同一個資料型別 (datatype)。比方說,我們可以將 namespace 命名為 db
,這樣子就是操作同一個資料範圍。或者,我們可以將 namespace 命名為 payload
,這樣子就是操作同一個資料型別。
為巨集命名
- 語法可喻型 (syntactically understandable)
- 語意可喻型 (semantically understandable)
- 文件與合成名
在 Clojure 語言裡,我們可以將巨集 (macro) 分成兩大類: 語法可喻型、語意可喻型。
語法可喻型
比方說像 with-open
,光看命名看不懂它的意涵,將 macro 展開後就可以清楚地理解了。
(defmacro with-open [[sym form] & body]
`(let [~sym ~form]
(try
~@body
(finally
(.close ~sym)))))
然而,由於要使用這種巨集,我們居然需要先去看它的實作 (implementation) ,這種巨集並不是好的間接層 (indirection) 。
語意可喻型
比方說像 core.async/go
,實作超級複雜。我們只能透過它的語意來了解它。
然而,要有效地使用 go
這種巨集,使用者仍然需要先透過讀文件,才能去了解它的例外處理與失敗情況 (failure mode) 。所以,這種巨集也不是好的間接層 (indirection) 。
文件與合成名
無論是哪一種巨集 (語法可喻型、語意可喻型),巨集都不是好的間接層,使用者都無法單純從巨集的命名了解到足夠的意涵。於是,巨集的命名往往使用合成名 (synthetic names) 。同時,巨集特別需要清楚的文件 (documentation) 來說明。