Macro(宏、巨集)在程式語言中是一個使用者自訂的程式語言擴展。在 Lisp 系語言中,macro 的威力相當強大,主要是來自於 Lisp 系語言的「程式碼 - 資料結構同像性」(homoiconic)。這裡簡單紀錄 Clojure 的求值模型。
(defmacro backwards
[form]
(reverse form))
(backwards (" backwards" " am" "I" str))
; => "I am backwards"
在 Lisp 系語言中,list 的第一個字必須處理下面的東西,然而,通過 macro,使得特殊形式的語法可以被接受。
Clojure 的求值步驟由兩部組成:
在多數語言中,程式碼首先被轉換成 AST(抽象語法樹)結構。在 Clojure 中,S-expression 本身就是與 AST 等價的結構了。原文提到了篇不錯的文章〈Syntax and Semetics〉。
Reader
在 Clojure 中,使用 read-string
讀入程式碼。這個函數接受一個字串參數,然後返回處理後的數據結構。接著可以把數據結構傳給其他函數,也可以用 eval
求取結果。在 Clojure 資料結構中,主要有下列四種形式。
(a b c)
是一個 list 結構str
符號(symbol)結構[1 2]
是一個 vector 結構{:a "b"}
是一個 map 結構這些可以原生轉換。不過,對於 lambda(匿名函數),例如 #(+ 1 %)
這類的,將被 reader macros 轉換為以 Clojure 關鍵字表達的形式。'
、#
、@
都是 macro 標誌。
'
會用 quote
展開#
會用 fn
展開@
會用 deref
展開註解則不展開。
Evaluator
List 的第一個元素會被程序搜尋對應的函數。而不是第一個元素且也不是 list 的其餘元素求值為自身,包括 true
、false
、{}
、:keyword
。
接著會查找由 def
根據地一個元素是否有前綴(prefix)在對應的命名空間建立的「符號-值」表查找對應的值(可能是個函數,或者是其他 macro)。順序如下:
fn
、if
)let
下),越內部的解析優先權越高。另外就是函數調用時,會先對每個元素都完全求值,才做為參數傳遞。