The Scheme Programming Language, Third Edition [Electronic resources]

Jean-Pierre Hbert, R. Kent Dybvig

نسخه متنی -صفحه : 98/ 54
نمايش فراداده

8.1. Keyword Bindings

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.