For Gauche 0.9.10

Next: , Previous: , Up: Macros   [Contents][Index]

5.5 Identifiers

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 macor would have used symbol?. To compare identifiers, you need to use the “compare” procedure passed to the er-macro expander, or free-identifier=?.

Builtin Class: <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.

Function: identifier? obj

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.

Function: wrapped-identifier? obj

Returns #t iff obj is a wrapped identifier.

This is R6RS’s identifier?.

Function: identifier->symbol obj

Returns the symbol that is the origin of the obj, which must be either a symbol or a wrapped identifier.

Function: free-identifier=? id1 id2

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.

Function: unwrap-syntax form

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.

Function: unravel-syntax form

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.

Next: , Previous: , Up: Macros   [Contents][Index]