summaryrefslogtreecommitdiff
path: root/doc/ref/api-macros.texi
diff options
context:
space:
mode:
Diffstat (limited to 'doc/ref/api-macros.texi')
-rw-r--r--doc/ref/api-macros.texi178
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