2
votes

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).

1
"Now I want to replace record def with macro." Why?Alex Taggart
@Alex Taggart, Because I want to create a DSL like stuff. Also example in question is simplified.Stan Kurilin

1 Answers

10
votes

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)