This section describes forms that establish bindings between keywords and transformers. Keyword bindings may be established at top level, using define-syntax, or locally, using let-syntax, letrec-syntax, or internal define-syntax. Existing keyword bindings may be rebound temporarily with fluid-let-syntax.
(define-syntax keyword exp) | syntax |
returns: unspecified |
exp must evaluate to a transformer.
The following example defines let* as a syntactic extension, specifying the transformer with syntax-rules (see Section 8.2).
(define-syntax let* (syntax-rules () (( () e1 e2 ...) (let () e1 e2 ...)) (( ((i1 v1) (i2 v2) ...) e1 e2 ...) (let ((i1 v1)) (let* ((i2 v2) ...) e1 e2 ...)))))
define-syntax forms appearing at top level behave similarly to top-level variable definitions, and define-syntax forms appearing at the front of a lambda or other body behave similarly to internal variable definitions. That is, a binding established by a top-level define-syntax form is visible globally, whereas one established by an internal define-syntax form is visible only within the body in which the define-syntax form appears.
All bindings established by a set of internal definitions, whether keyword or variable definitions, are visible within the definitions themselves. For example, the expression
(let () (define even? (lambda (x) (or (= x 0) (odd? (- x 1))))) (define-syntax odd? (syntax-rules () (( x) (not (even? x))))) (even? 10))
is valid and should return #t. It must be possible for the expander to determine the set of syntax and variable definitions that appears at the front of a body without referring to any of the locally defined identifiers. It is not legal, therefore, for an internal definition to affect the status of a (potential) internal definition in the same sequence of forms. For example,
(let () (define-syntax bind-to-zero (syntax-rules () (( id) (define id 0)))) (bind-to-zero x) x)
is not valid, since it would require the expander to expand (bind-to-zero x) in order to recognize it as a syntax definition. Rewritten as follows it returns 0.
(let () (define-syntax bind-to-zero (syntax-rules () (( id) (define id 0)))) (let () (bind-to-zero x) x))
A top-level syntactic definition must be established before its first use in order for that use to be recognized.
(let-syntax ((keyword exp) ...) form1 form2 ...) | syntax |
(letrec-syntax ((keyword exp) ...) form1 form2 ...) | syntax |
returns: see explanation |
Each exp must evaluate to a transformer. For both let-syntax and letrec-syntax, each keyword is bound within the forms form1 form2 …. For letrec-syntax the binding scope also includes each exp.
A let-syntax or letrec-syntax form may expand into one or more expressions anywhere expressions are permitted, in which case the resulting expressions are treated as if enclosed in a begin expression. This allows a let-syntax or letrec-syntax form to expand into a definition or sequence of definitions anywhere definitions are permitted, in which case the definitions are treated as if they appeared in place of the let-syntax or letrec-syntax form. (This differs from the Revised5 Report treatment of these forms; see page 183.)
The following example highlights how let-syntax and letrec-syntax differ.
(let ((f (lambda (x) (+ x 1)))) (let-syntax ((f (syntax-rules () (( x) x))) (g (syntax-rules () (( x) (f x))))) (list (f 1) (g 1)))) ⇒ (1 2) (let ((f (lambda (x) (+ x 1)))) (letrec-syntax ((f (syntax-rules () (( x) x))) (g (syntax-rules () (( x) (f x))))) (list (f 1) (g 1)))) ⇒ (1 1)
The two expressions are identical except that the let-syntax form in the first expression is a letrec-syntax form in the second. In the first expression, the f occurring in g refers to the let-bound variable f, whereas in the second it refers to the keyword f whose binding is established by the letrec-syntax form.
(fluid-let-syntax ((keyword exp) ...) form1 form2 ...) | syntax |
returns: see explanation |
Each exp must evaluate to a transformer. fluid-let-syntax is similar to let-syntax, except that instead of introducing new bindings for the keywords keyword …, fluid-let-syntax temporarily alters the existing bindings for the keywords during the expansion of its body. That is, during the expansion of form1 form2 …, the visible lexical (or top-level) binding for each keyword is temporarily replaced by a new association between the keyword and the corresponding transformer. This affects any references to the keyword that resolve to the same lexical (or top-level) binding whether the references occur in the text of the body or are introduced during its expansion. In contrast, let-syntax captures only those references that occur within the text of its body.
The following example shows how fluid-let-syntax differs from let-syntax.
(let ((f (lambda (x) (+ x 1)))) (let-syntax ((g (syntax-rules () (( x) (f x))))) (let-syntax ((f (syntax-rules () (( x) x)))) (g 1)))) ⇒ 2 (let ((f (lambda (x) (+ x 1)))) (let-syntax ((g (syntax-rules () (( x) (f x))))) (fluid-let-syntax ((f (syntax-rules () (( x) x)))) (g 1)))) ⇒ 1
The two expressions are identical except that the inner let-syntax form in the first expression is a fluid-let-syntax form in the second. In the first expression, the f occurring in the expansion of (g 1) refers to the let-bound variable f, whereas in the second it refers to the keyword f by virtue of the uid syntax binding for f.