diff options
Diffstat (limited to 'doc/ref/api-macros.texi')
-rw-r--r-- | doc/ref/api-macros.texi | 178 |
1 files changed, 177 insertions, 1 deletions
diff --git a/doc/ref/api-macros.texi b/doc/ref/api-macros.texi index c2910a44e..9964e6b06 100644 --- a/doc/ref/api-macros.texi +++ b/doc/ref/api-macros.texi @@ -43,6 +43,8 @@ languages}, or EDSLs.}. * Identifier Macros:: Identifier macros. * Syntax Parameters:: Syntax Parameters. * Eval When:: Affecting the expand-time environment. +* Macro Expansion:: Procedurally expanding macros. +* Hygiene and the Top-Level:: A hack you might want to know about. * Internal Macros:: Macros as first-class values. @end menu @@ -806,7 +808,7 @@ Return the name of the module whose source contains the identifier @var{id}. @end deffn -@deffn {Scheme Procedure} syntax-local-binding id +@deffn {Scheme Procedure} syntax-local-binding id [#:resolve-syntax-parameters?=#t] Resolve the identifer @var{id}, a syntax object, within the current lexical environment, and return two values, the binding type and a binding value. The binding type is a symbol, which may be one of the @@ -819,6 +821,12 @@ of @code{eq?}) identifying this binding. @item macro A syntax transformer, either local or global. The value is the transformer procedure. +@item syntax-parameter +A syntax parameter (@pxref{Syntax Parameters}). By default, +@code{syntax-local-binding} will resolve syntax parameters, so that this +value will not be returned. Pass @code{#:resolve-syntax-parameters? #f} +to indicate that you are interested in syntax parameters. The value is +the default transformer procedure, as in @code{macro}. @item pattern-variable A pattern variable, bound via @code{syntax-case}. The value is an opaque object, internal to the expander. @@ -1224,6 +1232,174 @@ the example above. Other uses of @code{eval-when} may void your warranty or poison your cat. @end deffn +@node Macro Expansion +@subsection Macro Expansion + +Usually, macros are expanded on behalf of the user as needed. Macro +expansion is an integral part of @code{eval} and @code{compile}. Users +can also expand macros at the REPL prompt via the @code{expand} REPL +command; @xref{Compile Commands}. + +Macros can also be expanded programmatically, via @code{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. +@xref{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, +@xref{Tree-IL}, for all the details. The easiest thing is to just run +@code{tree-il->scheme} on the result of macro-expansion: + +@lisp +(macroexpand '(+ 1 2)) +@result{} +#<tree-il (call (toplevel +) (const 1) (const 2))> + +(use-modules (language tree-il)) +(tree-il->scheme (macroexpand '(+ 1 2))) +@result{} +(+ 1 2) +@end lisp + +The second complication involves @code{eval-when}. As an example, what +would it mean to macro-expand the definition of a macro? + +@lisp +(macroexpand '(define-syntax qux (identifier-syntax 'bar))) +@result{} +? +@end lisp + +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 @code{eval-when} clauses will only proceed when +@code{eval} (or @code{expand}) is in its condition set. Top-level +macros will be @code{eval}'d in the top-level environment. + +In this way @code{(macroexpand @var{foo})} is equivalent to +@code{(macroexpand @var{foo} 'e '(eval))}. The second argument is the +mode (@code{'e} for ``eval'') and the second is the +eval-syntax-expanders-when parameter (only @code{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 @code{'c} as +the second argument to @code{macroexpand}. But probably you want the +macro definition to be present at compile time as well, so you pass +@code{'(compile load eval)} as the @var{esew} parameter. In fact +@code{(compile @var{foo} #:to 'tree-il)} is entirely equivalent to +@code{(macroexpand @var{foo} 'c '(compile load eval))}; @xref{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. + + +@node Hygiene and the Top-Level +@subsection Hygiene and the Top-Level + +Consider the following macro. + +@lisp +(define-syntax-rule (defconst name val) + (begin + (define t val) + (define-syntax-rule (name) t))) +@end lisp + +If we use it to make a couple of bindings: + +@lisp +(defconst foo 42) +(defconst bar 37) +@end lisp + +The expansion would look something like this: + +@lisp +(begin + (define t 42) + (define-syntax-rule (foo) t)) +(begin + (define t 37) + (define-syntax-rule (bar) t)) +@end lisp + +As the two @code{t} bindings were introduced by the macro, they should +be introduced hygienically -- and indeed they are, inside a lexical +contour (a @code{let} or some other lexical scope). The @code{t} +reference in @code{foo} is distinct to the reference in @code{bar}. + +At the top-level things are more complicated. Before Guile 2.2, a use +of @code{defconst} at the top-level would not introduce a fresh binding +for @code{t}. This was consistent with a weaselly interpretation of the +Scheme standard, in which all possible bindings may be assumed to exist, +at the top-level, and in which we merely take advantage of toplevel +@code{define} of an existing binding being equivalent to @code{set!}. +But it's not a good reason. + +The solution is to create fresh names for all bindings introduced by +macros -- not just bindings in lexical contours, but also bindings +introduced at the top-level. + +However, the obvious strategy of just giving random names to introduced +toplevel identifiers poses a problem for separate compilation. Consider +without loss of generality a @code{defconst} of @code{foo} in module +@code{a} that introduces the fresh top-level name @code{t-1}. If we +then compile a module @code{b} that uses @code{foo}, there is now a +reference to @code{t-1} in module @code{b}. If module @code{a} is then +expanded again, for whatever reason, for example in a simple +recompilation, the introduced @code{t} gets a fresh name; say, +@code{t-2}. Now module @code{b} has broken because module @code{a} no +longer has a binding for @code{t-1}. + +If introduced top-level identifiers ``escape'' a module, in whatever +way, they then form part of the binary interface (ABI) of a module. It +is unacceptable from an engineering point of view to allow the ABI to +change randomly. (It also poses practical problems in meeting the +recompilation conditions of the Lesser GPL license, for such modules.) +For this reason many people prefer to never use identifier-introducing +macros at the top-level, instead making those macros receive the names +for their introduced identifiers as part of their arguments, or to +construct them programmatically and use @code{datum->syntax}. But this +approach requires omniscience as to the implementation of all macros one +might use, and also limits the expressive power of Scheme macros. + +There is no perfect solution to this issue. Guile does a terrible thing +here. When it goes to introduce a top-level identifier, Guile gives the +identifier a pseudo-fresh name: a name that depends on the hash of the +source expression in which the name occurs. The result in this case is +that the introduced definitions expand as: + +@lisp +(begin + (define t-1dc5e42de7c1050c 42) + (define-syntax-rule (foo) t-1dc5e42de7c1050c)) +(begin + (define t-10cb8ce9fdddd6e9 37) + (define-syntax-rule (bar) t-10cb8ce9fdddd6e9)) +@end lisp + +However, note that as the hash depends solely on the expression +introducing the definition, we also have: + +@lisp +(defconst baz 42) +@result{} (begin + (define t-1dc5e42de7c1050c 42) + (define-syntax-rule (baz) t-1dc5e42de7c1050c)) +@end lisp + +Note that the introduced binding has the same name! This is because the +source expression, @code{(define t 42)}, was the same. Probably you +will never see an error in this area, but it is important to understand +the components of the interface of a module, and that interface may +include macro-introduced identifiers. + + @node Internal Macros @subsection Internal Macros |