2012-08-01 120 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