I couldn't understand the difference between macroexpand and macroexpand-1.
Could you provide examples?
Macros are useful for writing new, convenient syntactic constructs. And when we talk about syntax, we are typically talking about raw, unevaluated sexpressions. clojure. core/when is a syntactic sugar macro which transforms into an if with a do for a then and no else.
Clojure has a programmatic macro system which allows the compiler to be extended by user code. Macros can be used to define syntactic constructs which would require primitives or built-in support in other languages. Many core constructs of Clojure are not, in fact, primitives, but are normal macros.
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"))
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 ->
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With