mirror of
https://git.savannah.gnu.org/git/guile.git
synced 2025-05-01 12:20:26 +02:00
Document top-level pseudo-hygiene
* doc/ref/api-macros.texi (Hygiene and the Top-Level): Add a section documenting our pseudo-hygienic top-level names.
This commit is contained in:
parent
ba578eb044
commit
03dfed840b
1 changed files with 103 additions and 0 deletions
|
@ -44,6 +44,7 @@ languages}, or EDSLs.}.
|
||||||
* Syntax Parameters:: Syntax Parameters.
|
* Syntax Parameters:: Syntax Parameters.
|
||||||
* Eval When:: Affecting the expand-time environment.
|
* Eval When:: Affecting the expand-time environment.
|
||||||
* Macro Expansion:: Procedurally expanding macros.
|
* 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.
|
* Internal Macros:: Macros as first-class values.
|
||||||
@end menu
|
@end menu
|
||||||
|
|
||||||
|
@ -1272,6 +1273,108 @@ tricksy regarding modes, so unless you are building a macro-expanding
|
||||||
tool, we suggest to avoid invoking it directly.
|
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
|
@node Internal Macros
|
||||||
@subsection Internal Macros
|
@subsection Internal Macros
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue