使用宏创建记录

Sta*_*lin 2 macros record clojure

我有一个记录 (defrecord Rec [id])

我喜欢它

(def my ( Rec. 2 ))
(println (:id my))
Run Code Online (Sandbox Code Playgroud)

现在我想用宏替换记录def.所以我可以写

(r 2) 
(println (:id my))
Run Code Online (Sandbox Code Playgroud)

我写了宏

(defmacro r [id]
   (list 'def 'my (symbol "(") 'Rec. id (symbol ")")))
Run Code Online (Sandbox Code Playgroud)

我用macroexpand检查了它

(macroexpand-1 '(r 2))  => (def my ( Rec. 2 ))
Run Code Online (Sandbox Code Playgroud)

但我得到RuntimeException: Too many arguments to def(r 2).

Ale*_*lex 10

从左边的paren中创建一个符号与使用左边的paren的evaling文本不同.前者没有特别的意义; 后者使读者产生一个嵌套列表,然后对其进行评估.

换句话说,Clojure评估数据结构,而不是文本(或符号列表).当您在REPL中键入内容时,该文本将被读入数据结构,然后评估数据结构.

为了使其正常工作,宏需要自己生成嵌套列表:

(defmacro r [id]
  (list 'def 'my (list 'Rec. id)))
Run Code Online (Sandbox Code Playgroud)

或者更好的是,使用语法quote运算符:

(defmacro r [id]
  `(def my (Rec. ~id)))
Run Code Online (Sandbox Code Playgroud)

为了便于说明,您可以看到当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)
Run Code Online (Sandbox Code Playgroud)