2012-08-01 157 views
2

記錄我有一個記錄(defrecord Rec [id])創建具有宏觀

我與它的工作就像

(def my (Rec. 2)) 
(println (:id my)) 

現在我想用宏替換記錄畫質。所以,我可以寫只是

(r 2) 
(println (:id my)) 

我寫了宏觀

(defmacro r [id] 
    (list 'def 'my (symbol "(") 'Rec. id (symbol ")"))) 

我macroexpand

(macroexpand-1 '(r 2)) => (def my (Rec. 2)) 

檢查的話,但我得到RuntimeException: Too many arguments to def(r 2)

+0

「現在我想用宏替換記錄def」。爲什麼? – 2012-08-01 22:35:16

+0

@Alex Taggart,因爲我想創建一個類似於DSL的東西。這個問題的例子也被簡化了。 – 2012-08-02 07:27:48

回答

10

從左側文字中創建符號與用左側文字評估文本不同。前者沒有特別的意義,後者導致讀者產生一個嵌套列表,然後進行評估。

換句話說,Clojure評估數據結構,而不是文本(或符號列表)。當您在REPL中輸入某些內容時,該文本將被讀入數據結構中,然後對數據結構進行評估。

爲了使其正常工作,宏需要產生一個嵌套表本身:

(defmacro r [id] 
    (list 'def 'my (list 'Rec. id))) 

或者更好的是,使用語法引用操作:

(defmacro r [id] 
    `(def my (Rec. ~id))) 

爲了便於說明,你可以看看當Clojure代碼被讀爲文本時會發生什麼:

(read-string "(def my (Rec. 2))") 
=> (def my (Rec. 2)) 
(map type (read-string "(def my (Rec. 2))")) 
=> (clojure.lang.Symbol clojure.lang.Symbol clojure.lang.PersistentList) 
+0

+1我正在寫幾乎完全相同的單詞 – skuro 2012-08-01 20:21:26

+0

非常感謝您的提醒。 – 2012-08-01 20:24:52