jennykwan
jennykwan

Reputation: 2701

Clojure repeated `defmacro` wrapping a form that creates a Java class

I'm wrapping defrecord with a defmacro, because I want a specific type of concrete map to register itself with a central place (for serde).

The macro is:

(defmacro def-thing! [name [& fields] & opts+specs]
  `(let [klass# (defrecord ~name ~fields ~@opts+specs)
         map-constructor-fn# (resolve (symbol (str 'map-> ~name)))]
     ; use the map-constructor-fn#
     ))

The problem is that the let form gets macroexpanded differently based on whether the macro has already been called before (like when I reload the namespace source file in nrepl).

The first time, name is a Symbol.

The second time, name is a Java class.

I can, of course, test to see if name~ is a Java class, and then call .getSimpleName() and do the appropriate thing.

But the core contributors to Clojure must have run into this? How is this handled? And why don't I see an explicit solution in the Clojure core source for defrecord or emit-defrecord? What am I missing?

Upvotes: 2

Views: 75

Answers (1)

jennykwan
jennykwan

Reputation: 2701

Well, I'm an idiot. I found this in "Joy of Clojure":

(defmacro def-thing! [name [& fields] & opts+specs]
  `(let [klass# (defrecord ~name ~fields ~@opts+specs)
         map-constructor-fn# (resolve (symbol (str 'map-> '~name)))]
     ; use the map-constructor-fn#
     ))

Notice the (quote (unquote <symbol>)) - i.e. '~<symbol> - form. That idiom forces capture of the raw, unqualified symbol passed in.

The curious thing is that I'm not passing that to the defrecord call. Internally, defrecord does the same capture of raw symbol, except through a var call, presumably because defrecord, when called the first time, would assign the symbol to a fully qualified class var. In fact, '~name specifically does not work with the defrecord call, and I get a clojure.lang.Cons cannot be cast to clojure.lang.Named exception.

This seems like inconsistency to me, but what do I know?

Upvotes: 1

Related Questions