In the discussion of hygienic macros, we keep saying the symbols are effectively renamed. What it means is that we don’t actually create a new symbol with a new name. We have to remember the origin of the renamed symbol to resolve the scope of the variable, and having a separate table to keep track of renamed symbols would be costly. Instead, the “rename” procedure wraps the symbols in the input with syntactic information.
When you play with macro internals, you’ll see an object
that is printed something like #<identifier user#foo.fb4ca828>
.
That’s the wrapped symbol.
If one macro output is passed to another macro expander, the wrapped symbol may further be wrapped.
The macro expander must assume that symbols in the input are already
wrapped by another macro expander. So, instead of calling it a “symbol”,
we call it an “identifier”. An identifier is something that usually
works as a variable or a syntactic keyword in the prorgam. It may be
a symbol or a wrapped identifier.
(Note that symbols in quoted literals are bare symbols, for the quote
form strips wrappers.)
Legacy Lisp macros sometimes examines the symbols in the input form.
In Scheme, you have to treat the input program as
a tree of identifiers and other objects. You can test whether an object
is an identifier or not by identifier?
, where traditional Lisp
macros would have used symbol?
. To compare identifiers, you need
to use the “compare” procedure passed to the er-macro expander,
or free-identifier=?
.
A class of wrapped identifier. It is created as a result of “renaming” in the hygienic macro expander.
A wrapped identifier contains transient information about the program source, and cannot be portably saved or passed around; it is only valid in the macro expansion phase.
For the details of identifier, see Identifiers.
Returns #t
if obj is either a symbol or a wrapped
identifier. Returns #f
otherwise.
Note: In R6RS, identifier?
only returns #t
for an
identifier object, which is of a disjoint type from symbols.
You can use wrapped-identifier?
below to check if an object
is an identifier other than a symbol.
Returns #t
iff obj is a wrapped identifier.
This is R6RS’s identifier?
.
Returns the symbol that is the origin of the obj, which must be either a symbol or a wrapped identifier.
When both arguments id1 and id2 are wrapped identifiers,
returns #t
if either (1) id1 and id2 both refer to
the same binding, or (2) id1 and id2 are both unbound.
Otherwise, #f
is returned.
If at least one of id1 or id2 is not a wrapped identifier,
#f
is returned. Note that bare symbols can’t be compared
with this procedure, for they lack the necessary lexical information.
To obtain a wrapped identifier, you need to pass a bare symbol
to the “rename” procedure passed to the er-macro transformer.
Usually you don’t need to use this procedure directly, for the compare procedure passed to the er-macro transformer is suffice.
Returns a copy of form, except
removing wrappings of identifiers in it.
The output of macro expanders contain wrapped identifiers,
which is bothersome to see. This procedure traverses form
and replaces any wrapped identifiers with its original symbol,
retrieved by identifier->symbol
.
Note that, although the result is an ordinary S-expression easier to read,
syntactic information is completely lost. For example, distinct identifiers
can become indistinguishable if they happen to have the same name
(it happens often when you generate temporary variables via
recursive calls of syntax-rules
).
If you distinguish newly inserted
identifiers with the same name, use unravel-syntax
.
Returns a copy of form while removing wrappings of identifiers in it, but attach suffix if two distinct identifiers have the same name, so that they won’t be confused.
For example, a common idiom of syntax-rules
to generate
temporary variables with recursions create all variables with
the same name, although each variable is different because they are
inserted by the different invocation of the expander. If you pass
its output to unwrap-syntax
, all syntactic information is stripped
and these variables can’t be distinguished from one another.
This procedure is automatically called with some macro utilities;
See Tracing macro expansion, and see Expanding macros manually,
for the example of output of unravel-syntax
.
Note that the global identifiers becomes bare symbols, so you are still unable to tell which module the global identifiers refer to.