6.8.9 Macro Expansion

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 tricksy regarding modes, so unless you are building a macro-expanding tool, we suggest to avoid invoking it directly.