Next: , Previous: Macros, Up: Macros


2.11.1 Binding Constructs for Syntactic Keywords

let-syntax, letrec-syntax, let*-syntax and define-syntax are analogous to let, letrec, let* and define, but they bind syntactic keywords to macro transformers instead of binding variables to locations that contain values.

Any argument named transformer-spec must be a macro-transformer expression, which is one of the following:

— special form: let-syntax bindings expression expression ...

Bindings should have the form

          ((keyword transformer-spec) ...)

Each keyword is an identifier, each transformer-spec is a a macro-transformer expression, and the body is a sequence of one or more expressions. It is an error for a keyword to appear more than once in the list of keywords being bound.

The expressions are expanded in the syntactic environment obtained by extending the syntactic environment of the let-syntax expression with macros whose keywords are the keywords, bound to the specified transformers. Each binding of a keyword has the expressions as its region.

          (let-syntax ((when (syntax-rules ()
                               ((when test stmt1 stmt2 ...)
                                (if test
                                    (begin stmt1
                                           stmt2 ...))))))
            (let ((if #t))
              (when if (set! if 'now))
              if))                           ⇒  now
          
          (let ((x 'outer))
            (let-syntax ((m (syntax-rules () ((m) x))))
              (let ((x 'inner))
                (m))))                       ⇒  outer
— special form: letrec-syntax bindings expression expression ...

The syntax of letrec-syntax is the same as for let-syntax.

The expressions are expanded in the syntactic environment obtained by extending the syntactic environment of the letrec-syntax expression with macros whose keywords are the keywords, bound to the specified transformers. Each binding of a keyword has the bindings as well as the expressions within its region, so the transformers can transcribe expressions into uses of the macros introduced by the letrec-syntax expression.

          (letrec-syntax
            ((my-or (syntax-rules ()
                      ((my-or) #f)
                      ((my-or e) e)
                      ((my-or e1 e2 ...)
                       (let ((temp e1))
                         (if temp
                             temp
                             (my-or e2 ...)))))))
            (let ((x #f)
                  (y 7)
                  (temp 8)
                  (let odd?)
                  (if even?))
              (my-or x
                     (let temp)
                     (if y)
                     y)))        ⇒  7
— special form: let*-syntax bindings expression expression ...

The syntax of let*-syntax is the same as for let-syntax.

The expressions are expanded in the syntactic environment obtained by extending the syntactic environment of the letrec-syntax expression with macros whose keywords are the keywords, bound to the specified transformers. Each binding of a keyword has the subsequent bindings as well as the expressions within its region. Thus

          (let*-syntax
             ((a (syntax-rules ...))
              (b (syntax-rules ...)))
            ...)

is equivalent to

          (let-syntax ((a (syntax-rules ...)))
            (let-syntax ((b (syntax-rules ...)))
              ...))
— special form: define-syntax keyword transformer-spec

Keyword is an identifier, and transformer-spec is a macro transformer expression. The syntactic environment is extended by binding the keyword to the specified transformer.

The region of the binding introduced by define-syntax is the entire block in which it appears. However, the keyword may only be used after it has been defined.

MIT/GNU Scheme permits define-syntax to appear both at top level and within lambda bodies. The Revised^4 Report permits only top-level uses of define-syntax.

When compiling a program, a top-level instance of define-syntax both defines the syntactic keyword and generates code that will redefine the keyword when the program is loaded. This means that the same syntax can be used for defining macros that will be used during compilation and for defining macros to be used at run time.

Although macros may expand into definitions and syntax definitions in any context that permits them, it is an error for a definition or syntax definition to shadow a syntactic keyword whose meaning is needed to determine whether some form in the group of forms that contains the shadowing definition is in fact a definition, or, for internal definitions, is needed to determine the boundary between the group and the expressions that follow the group. For example, the following are errors:

          (define define 3)
          
          (begin (define begin list))
          
          (let-syntax
            ((foo (syntax-rules ()
                    ((foo (proc args ...) body ...)
                     (define proc
                       (lambda (args ...)
                         body ...))))))
            (let ((x 3))
              (foo (plus x y) (+ x y))
              (define foo x)
              (plus foo x)))