SCM provides a synthetic identifier type for efficient implementation of
hygienic macros (for example,
see Macros) A synthetic identifier
may be inserted in Scheme code by a macro expander in any context
where a symbol would normally be used. Collectively, symbols and
synthetic identifiers are identifiers.
#tif obj is a symbol or a synthetic identifier, and
If it is necessary to distinguish between symbols and synthetic identifiers,
use the predicate
A synthetic identifier includes two data: a parent, which is an
identifier, and an environment, which is either
#f or a lexical
environment which has been passed to a macro expander
(a procedure passed as an argument to
Returns a synthetic identifier. parent must be an identifier, and env must either be
#for a lexical environment passed to a macro expander.
renamed-identifierreturns a distinct object for each call, even if passed identical arguments.
There is no direct way to access all of the data internal to a synthetic identifier, those data are used during variable lookup. If a synthetic identifier is inserted as quoted data then during macro expansion it will be repeatedly replaced by its parent, until a symbol is obtained.
Returns the symbol obtained by recursively extracting the parent of id, which must be an identifier.
renamed-identifier may be used as a replacement for
(define gentemp (let ((name (string->symbol "An unlikely variable"))) (lambda () (renamed-identifier name #f))))
If an identifier returned by this version of
gentemp is inserted
in a binding position as the name of a variable then it is guaranteed
that no other identifier (except one produced by passing the first to
renamed-identifier) may denote that variable. If an identifier
gentemp is inserted free, then it will denote the
top-level value bound to its parent, the symbol named “An unlikely
variable”. This behavior, of course, is meant to be put to good use:
(define top-level-foo (procedure->memoizing-macro (lambda (exp env) (renamed-identifier 'foo #f))))
Defines a macro which may always be used to refer to the top-level binding
(define foo 'top-level) (let ((foo 'local)) (top-level-foo)) ⇒ top-level
In other words, we can avoid capturing
If a lexical environment is passed as the second argument to
renamed-identifier then if the identifier is inserted free
its parent will be looked up in that environment, rather than in
the top-level environment. The use of such an identifier must
be restricted to the lexical scope of its environment.
There is another restriction imposed for implementation convenience:
Macros passing their lexical environments to
may be lexically bound only by the special forms
letrec-syntax. No error is signaled if this restriction is not
met, but synthetic identifier lookup will not work properly.
In order to maintain referential transparency it is necessary to
determine whether two identifiers have the same denotation. With
synthetic identifiers it is not necessary that two identifiers be
eq? in order to denote the same binding.
#tif identifiers id1 and id2 denote the same binding in lexical environment env, and
#fotherwise. env must either be a lexical environment passed to a macro transformer during macro expansion or the empty list.
For example,(define top-level-foo? (procedure->memoizing-macro (let ((foo-name (renamed-identifier 'foo #f))) (lambda (exp env) (identifier-equal? (cadr exp) foo-name env))))) (top-level-foo? foo) ⇒ #t (let ((foo 'local)) (top-level-foo? foo)) ⇒ #f
carof expr denotes a macro in env, then if that macro is a primitive, expr will be returned, if the macro was defined in Scheme, then a macro expansion will be returned. If the
carof expr does not denote a macro, the
Returns a new environment object, equivalent to env, which must either be an environment object or null, extended by one frame. names must be an identifier, or an improper list of identifiers, usable as a formals list in a
lambdaexpression. values must be a list of objects long enough to provide a binding for each of the identifiers in names. If names is an identifier or an improper list then vals may be, respectively, any object or an improper list of objects.
Synthetic identifiers are converted to their parent symbols by
quasiquoteso that literal data in macro definitions will be properly transcribed.
quote, but preserves synthetic identifier intact.
the-macrois the simplest of all possible macro transformers: mac may be a syntactic keyword (macro name) or an expression evaluating to a macro, otherwise an error is signaled. mac is evaluated and returned once only, after which the same memoizied value is returned.
the-macromay be used to protect local copies of macros against redefinition, for example:(@let-syntax ((let (the-macro let))) ;; code that will continue to work even if LET is redefined. ...)
A low-level “explicit renaming” macro facility very similar to that proposed by W. Clinger [Exrename] is supported. Syntax may be defined in
syntax-rules. proc should evaluate to a procedure accepting three arguments: expr, rename, and compare. expr is a representation of Scheme code to be expanded, as list structure. rename is a procedure accepting an identifier and returning an identifier renamed in the definition environment of the new syntax. compare accepts two identifiers and returns true if and only if both denote the same binding in the usage environment of the new syntax.