Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a record with macro

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

like image 878
Stan Kurilin Avatar asked Apr 27 '26 07:04

Stan Kurilin


1 Answers

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)
like image 59
Alex Avatar answered Apr 30 '26 11:04

Alex



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!