Day03 為函數與巨集命名


為函數命名

  • 函數與資料範圍 (data scope) 的關系
  • 純函數與副作用函數
  • 函數命名與 namespace 命名之關系 --- 狹窄性

函數與資料範圍 (data scope) 的關系

在程式的執行期間,資料範圍 (data scope) 是指可以在我們所在的線程 (thread) 看得到的所有資料,包含了 function parameterslet-bound valuesclosed-over valuesglobal vars 。函數通常對資料範圍做三種不同的事:

  1. pull: 從外界拉資料進入資料範圍,比方說 http get request
  2. push: 從資料範圍推資料到外界,比方說 http post request
  3. 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) 來說明。

#name #Clojure





Elements of Clojure 一書,談的不只是 Clojure ,而是 senior programmer 了解、卻不易對他人明說的隱性知識 (tacit knowledge)。

留言討論