PREV UP next SCM

4.10: Syntactic Hooks for Hygienic Macros

SCM provides a synthetic identifier type for efficient implementation of hygienic macros (for example, syntax-rules 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.

Function: identifier? obj
Returns #t if obj is a symbol or a synthetic identifier, and #f otherwise.

If it is necessary to distinguish between symbols and synthetic identifiers, use the predicate symbol?.

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 procedure->macro, procedure->memoizing-macro, or procedure->syntax).

Function: renamed-identifier parent env
Returns a synthetic identifier. parent must be an identifier, and env must either be #f or a lexical environment passed to a macro expander. renamed-identifier returns a distinct object for each call, even if passed identical arguments.

There is no direct way to access 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.

4.10.1: Use of synthetic identifiers

renamed-identifier may be used as a replacement for gentemp:

(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 may denote that variable. If an identifier returned by 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 of foo.

(define foo 'top-level)
(let ((foo 'local))
  (top-level-foo))  => top-level

In other words, we can avoid capturing foo.

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 renamed-identifier may be lexically bound only by the special forms @let-syntax or @letrec-syntax. No error is signaled if this restriction is not met, but synthetic identifier lookup will not work properly.

Special Form: @let-syntax
Special Form: @letrec-syntax
Behave as let and letrec, but may also put extra information in the lexical environment so that renamed-identifier will work properly during expansion of the macros bound by these forms.

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.

Function: identifier-equal? id1 id2 env
Returns #t if identifiers id1 and id2 denote the same binding in lexical environment env, and #f otherwise. env must be a lexical environment passed to a macro transformer during macro expansion.

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
Special Form: syntax-quote obj
Synthetic identifiers are converted to their parent symbols by quote and quasiquote so that literal data in macro definitions will be properly transcribed. syntax-quote behaves like quote, but preserves synthetic identifier intact.
Special Form: the-macro mac
the-macro is 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-macro may 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.
        ...)