LionelGoulet
LionelGoulet

Reputation: 577

Can I reference a macro from within a macro in Clojure?

Clojure. Cursive. REPL. Windows 10. All up-to-date.

Here's my 'base' macro:

(defmacro PutProp! [Sym Val PName]
  `(reset-meta! (var ~Sym) (assoc (meta (var ~Sym)) ~PName ~Val)))

I have no idea if this is even correct in doing what I want to do, which is to modify Sym's metadata keyname PName with the new Val value. I'm new at this and I'm SURE I've jumped into the pool at the deep end. I'm using a macro because I thought it would avoid messy variable binding issues which I don't understand in Clojure.

Then I wrote another macro to define a lot of metadata on a single call:

(defmacro DefMeta! [L]  ;; Given a symbol (first L), define metadata for it given in (next L).
  `(let [S (first ~@L)]     ;; The symbol we're modifying is the first element in list L.
     (map (fn [[K V]] (PutProp! S V K)) (next ~@L))))   ;; (next L) is the list of key/value pairs.

I'm also not sure this is what I want to write. Working at the REPL, I define a symbol:

(def Sym 0)

Then I do a macroexpand-1 of DefMeta!:

(macroexpand-1 '(DefMeta! [Sym :VName :VValue]))

I get the following:

(clojure.core/let
 [thic.core/S (clojure.core/first Sym :VName :VValue)]
 (clojure.core/map
  (clojure.core/fn [[thic.core/K thic.core/V]] (thic.core/PutProp! thic.core/S thic.core/V thic.core/K))
  (clojure.core/next Sym :VName :VValue)))

which doesn't macro-expand the PutProp! macro.

I'm stumped. I have four books on Clojure programming and none of them mentions macro calls from within macro calls. Is it even legal? If so, how do I fully expand the inner macro so that I can see if what I've written is what I want? [...the perennial programmer problem...]

PS I tried macroexpand-all and I'm overwhelmed with the macroexpansion of EVERYTHING, even Clojure core functions.
Something less than that, please. Please?

Upvotes: 2

Views: 593

Answers (2)

Alan Thompson
Alan Thompson

Reputation: 29958

It is perfectly legal (& common) for the definition of a macro to make use of another macro.

You can see an example of the best way IMHO to build up a macro step-by-step in this question.

I don't like using the macroexpand-* functions. The answer in the above link uses simple helper functions with println and friends so you can see what is occurring one step at a time in the macro writing process. Enjoy!

Upvotes: 2

coredump
coredump

Reputation: 38789

which doesn't macro-expand the PutProp! macro.

Yes, macroexpand-1 only performs one pass of macroexpansion. The whole process of macroexpansion is a fixpoint computation where each tree is expanded until it reaches a form that cannot be expanded anymore.

You may want to use macroexpand-all

Also, the input list in your macro is a list of expressions, and you want to take the first and next of this list at macroexpansion time.

(first ~@L)

This above expands as:

(clojure.core/first Sym :VName :VValue)

Where your in fact want only Sym, which is:

~(first L)

Upvotes: 3

Related Questions