Reputation: 15792
I have a record (defrecord Rec [id])
I work with it like
(def my ( Rec. 2 ))
(println (:id my))
Now I want to replace record def with macro. So that I could write just
(r 2)
(println (:id my))
I wrote macro
(defmacro r [id]
(list 'def 'my (symbol "(") 'Rec. id (symbol ")")))
I checked it with macroexpand
(macroexpand-1 '(r 2)) => (def my ( Rec. 2 ))
But I get RuntimeException: Too many arguments to def
on (r 2)
.
Upvotes: 2
Views: 176
Reputation: 13941
Creating a symbol out of a left paren is not the same as eval-ing text with a left paren. No special significance is attached to the former; the latter causes the reader to produce a nested list which is then evaluated.
In other words, Clojure evaluates data structures, not text (or a list of symbols). When you type something in the REPL, that text is read into a data structure, then the data structure is evaluated.
For this to work correctly, the macro needs to produce a nested list itself:
(defmacro r [id]
(list 'def 'my (list 'Rec. id)))
Or better yet, use the syntax quote operator:
(defmacro r [id]
`(def my (Rec. ~id)))
For illustrative purposes, you can see what happens when Clojure code is read as text:
(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)
Upvotes: 10