我有一条记录(defrecord Rec [id])
我像
一样使用它(def my ( Rec. 2 ))
(println (:id my))
现在我想用宏替换记录def。所以我只能写
(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 :(得分:10)
从左边的paren中创建一个符号与使用左边的paren的eval-ing文本不同。前者没有特别的意义;后者使读者产生一个嵌套列表,然后对其进行评估。
换句话说,Clojure评估数据结构,而不是文本(或符号列表)。当您在REPL中键入内容时,该文本将被读入数据结构,然后评估数据结构。
为了使其正常工作,宏需要自己生成一个嵌套列表:
(defmacro r [id]
(list 'def 'my (list 'Rec. id)))
或者更好的是,使用语法quote operator:
(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)