Usually, macros are expanded on behalf of the user as needed. Macro
expansion is an integral part of eval
and compile
. Users
can also expand macros at the REPL prompt via the expand
REPL
command; See Compile Commands.
Macros can also be expanded programmatically, via macroexpand
,
but the details get a bit hairy for two reasons.
The first complication is that the result of macro-expansion isn’t
Scheme: it’s Tree-IL, Guile’s high-level intermediate language.
See Tree-IL. As “hygienic macros” can produce identifiers that are
distinct but have the same name, the output format needs to be able to
represent distinctions between variable identities and names. Again,
See Tree-IL, for all the details. The easiest thing is to just run
tree-il->scheme
on the result of macro-expansion:
(macroexpand '(+ 1 2)) ⇒ #<tree-il (call (toplevel +) (const 1) (const 2))> (use-modules (language tree-il)) (tree-il->scheme (macroexpand '(+ 1 2))) ⇒ (+ 1 2)
The second complication involves eval-when
. As an example, what
would it mean to macro-expand the definition of a macro?
(macroexpand '(define-syntax qux (identifier-syntax 'bar))) ⇒ ?
The answer is that it depends who is macro-expanding, and why. Do you
define the macro in the current environment? Residualize a macro
definition? Both? Neither? The default is to expand in “eval” mode,
which means an eval-when
clauses will only proceed when
eval
(or expand
) is in its condition set. Top-level
macros will be eval
’d in the top-level environment.
In this way (macroexpand foo)
is equivalent to
(macroexpand foo 'e '(eval))
. The second argument is the
mode ('e
for “eval”) and the third is the
eval-syntax-expanders-when parameter (only eval
in this default
setting).
But if you are compiling the macro definition, probably you want to
reify the macro definition itself. In that case you pass 'c
as
the second argument to macroexpand
. But probably you want the
macro definition to be present at compile time as well, so you pass
'(compile load eval)
as the esew parameter. In fact
(compile foo #:to 'tree-il)
is entirely equivalent to
(macroexpand foo 'c '(compile load eval))
; See The Scheme Compiler.
It’s a terrible interface; we know. The macroexpander is somewhat tricky regarding modes, so unless you are building a macro-expanding tool, we suggest to avoid invoking it directly.