10

I couldn't understand the difference between macroexpand and macroexpand-1.

Could you provide examples?

Ertuğrul Çetin
  • 5,131
  • 5
  • 37
  • 76

2 Answers2

16

Let's say we have the following code:

(defmacro inner-macro [arg]
  `(println ~arg))

(defmacro top-level-macro [arg]
  `(inner-macro ~arg))

(defn not-a-macro [] nil)

Then, doc of macroexpand-1 says:

If form represents a macro form, returns its expansion, else returns form.

Indeed, it does:

user> (macroexpand-1 '(inner-macro "hello"))
(clojure.core/println "hello")

user> (macroexpand-1 '(top-level-macro "hello"))
(user/inner-macro "hello")

user> (macroexpand-1 '(not-a-macro))
(not-a-macro)

In other words, macroexpand-1 does just one step of macroexpansion if supplied form is a macro form.

Then, doc of macroexpand:

Repeatedly calls macroexpand-1 on form until it no longer represents a macro form, then returns it.

Example:

user> (macroexpand '(top-level-macro "hello"))
(clojure.core/println "hello")

What happened? As soon as, (top-level-macro "hello") expands to (user/inner-macro "hello"), which is macro form, macroexpand will perform expansion once again. The result of second expansion is (clojure.core/println "hello"). It is not a macro form, so macroexpand just returns it.

So, just to rephrase the doc, macroexpand will recursively do expansion until top level form is not a macro form.

Also there's additional note in macroexpand's doc:

Note neither macroexpand-1 nor macroexpand expand macros in subforms.

What does that mean? Let's say we have one more macro:

(defmacro subform-macro [arg]
  `(do
     (inner-macro ~arg)))

Let's try to expand it:

user> (macroexpand-1 '(subform-macro "hello"))
(do (user/inner-macro "hello"))

user> (macroexpand '(subform-macro "hello"))
(do (user/inner-macro "hello"))

Since, (do ...) form is not a macro macroexpand-1 and macroexpand just return it and nothing more. Don't expect that macroexpand will do the following:

user> (macroexpand '(subform-macro "hello"))
(do (clojure.core/println "hello"))
OlegTheCat
  • 4,443
  • 16
  • 24
7

the difference is quite simple. First of all the background: when the compiler sees macro call it tries to expand it according to its definition. If the code, generated by this macro contains other macros, they are also expanded by compiler, and so on, until resulting code is totally macros-free. So macroexpand-1 just expands the topmost macro and shows the result (no matter does it generate another macro calls), while macroexpand tries to follow the compiler's pipeline (partially, not expanding macros in subforms. To make the complete expansion you should take a look at clojure.walk/maxroexpand-all).

small example:

user> (defmacro dummy [& body]
        `(-> ~@body))
#'user/dummy

this silly macro generates the call to another macro ( -> )

user> (macroexpand-1 '(dummy 1 (+ 1)))
(clojure.core/-> 1 (+ 1))

macroexpand-1 just expands dummy, but keeps -> unexpanded

user> (macroexpand '(dummy 1 (+ 1)))
(+ 1 1)

macroexpand expands dummy and then expands ->

leetwinski
  • 17,408
  • 2
  • 18
  • 42