Chapter 5. Binding Forms

This chapter describes Chez Scheme extensions to the set of Revised6 Report binding forms. See Chapter 4 of The Scheme Programming Language, 4th Edition or the Revised6 Report for a description of standard binding forms.

Section 5.1. Definitions

A definition in Revised6 Report Scheme is a variable definition, keyword definition, or derived definition, i.e., a syntactic extension that expands into a definition. In addition, the forms within a begin expression appearing after a sequence of definitions is spliced onto the end of the sequence of definitions so that definitions at the front of the begin expression are treated as if they were part of the outer sequence of definitions. A let-syntax or letrec-syntax form is treated similarly, so that definitions at the front of the body are treated as if they were part of the outer sequence of definitions, albeit scoped where the bindings of the let-syntax or letrec-syntax form are visible.

Chez Scheme extends the set of definitions to include module forms, import forms, import-only forms, meta definitions, and alias forms, although the module, import, import-only, meta, and alias keywords are not available in a library or RNRS top-level program unless the scheme library is included in the library or top-level programs imports. These forms are described in Chapter 11.

In Revised6 Report Scheme, definitions can appear at the front of a lambda or similar body (e.g., a let or letrec body), at the front of a library body, or intermixed with expressions within an RNRS top-level program body. In Chez Scheme, definitions may also be used in the interactive top-level, i.e., they can be intermixed with expressions in the REPL or in program text to be loaded from a file via load (Section 12.4). The Revised6 Report does not mandate the existence nor specify the semantics of an interactive top-level, nor of a load procedure.

The macro expander uses the same two-pass algorithm for expanding top-level begin expressions as it uses for a lambda, library, or top-level program body. (This algorithm is described in Section 8.1 of The Scheme Programming Language, 4th Edition.) As a result,

(begin
  (define-syntax a (identifier-syntax 3))
  (define x a))

and

(begin
  (define x a)
  (define-syntax a (identifier-syntax 3)))

both result in the giving x the value 3, even though an unbound variable reference to a would result if the two forms within the latter begin expression were run independently at top level.

Similarly, the begin form produced by a use of

(define-syntax define-constant
  (syntax-rules ()
    [(_ x e)
     (begin
       (define t e)
       (define-syntax x (identifier-syntax t)))]))

and the begin form produced by a use of

(define-syntax define-constant
  (syntax-rules ()
    [(_ x e)
     (begin
       (define-syntax x (identifier-syntax t))
       (define t e))]))

are equivalent.

The Revised6 Report specifies that internal variable definitions be treated like letrec*, while earlier reports required internal variable definitions to be treated like letrec. By default, Chez Scheme implements the Revised6 Report semantics for internal variable definitions, as for all other things, but this behavior may be overridden via the internal-defines-as-letrec* parameter.

thread parameter: internal-defines-as-letrec*
libraries: (chezscheme)

When this parameter is set to #t (the default), internal variable definitions are evaluated using letrec* semantics. It may be set to #f to revert to the letrec semantics for internal variable definitions, for backward compatibility.

Section 5.2. Multiple-value Definitions

syntax: (define-values formals expr)
libraries: (chezscheme)

A define-values form is a definition and can appear anywhere other definitions can appear. It is like a define form but permits an arbitrary formals list (like lambda) on the left-hand side. It evaluates expr and binds the variables appearing in formals to the resulting values, in the same manner as the formal parameters of a procedure are bound to its arguments.

(let ()
  (define-values (x y) (values 1 2))
  (list x y)) <graphic> (1 2)
(let ()
  (define-values (x y . z) (values 1 2 3 4))
  (list x y z)) <graphic> (1 2 (3 4))

A define-values form expands into a sequence of definitions, the first for a hidden temporary bound to a data structure holding the values returned by expr and the remainder binding each of the formals to the corresponding value or list of values, extracted from the data structure via a reference to the temporary. Because the temporary must be defined before the other variables are defined, this works for internal define-values forms only if internal-defines-as-letrec* is set to the default value #t.

Section 5.3. Recursive Bindings

syntax: (rec var expr)
returns: value of expr
libraries: (chezscheme)

The syntactic form rec creates a recursive object from expr by establishing a binding of var within expr to the value of expr. In essence, it is a special case of letrec for self-recursive objects.

This form is useful for creating recursive objects (especially procedures) that do not depend on external variables for the recursion, which are sometimes undesirable because the external bindings can change. For example, a recursive procedure defined at top level depends on the value of the top-level variable given as its name. If the value of this variable should change, the meaning of the procedure itself would change. If the procedure is defined instead with rec, its meaning is independent of the variable to which it is bound.

(map (rec sum
       (lambda (x)
         (if (= x 0)
             0
             (+ x (sum (- x 1))))))
     '(0 1 2 3 4 5)) <graphic> (0 1 3 6 10 15)

(define cycle
  (rec self
    (list (lambda () self))))

(eq? ((car cycle)) cycle) <graphic> #t

The definition below expands rec in terms of letrec.

(define-syntax rec
  (syntax-rules ()
    [(_ x e) (letrec ((x e)) x)]))

Section 5.4. Fluid Bindings

syntax: (fluid-let ((var expr) ...) body1 body2 ...)
returns: the values of the body body1 body2 ...
libraries: (chezscheme)

The syntactic form fluid-let provides a way to temporarily assign values to a set of variables. The new values are in effect only during the evaluation of the body of the fluid-let expression. The scopes of the variables are not determined by fluid-let; as with set!, the variables must be bound at top level or by an enclosing lambda or other binding form. It is possible, therefore, to control the scope of a variable with lambda or let while establishing a temporary value with fluid-let.

Although it is similar in appearance to let, its operation is more like that of set!. Each var is assigned, as with set!, to the value of the corresponding expr within the body body1 body2 .... Should the body exit normally or by invoking a continuation made outside of the body (see call/cc), the values in effect before the bindings were changed are restored. Should control return back to the body by the invocation of a continuation created within the body, the bindings are changed once again to the values in effect when the body last exited.

Fluid bindings are most useful for maintaining variables that must be shared by a group of procedures. Upon entry to the group of procedures, the shared variables are fluidly bound to a new set of initial values so that on exit the original values are restored automatically. In this way, the group of procedures itself can be reentrant; it may call itself directly or indirectly without affecting the values of its shared variables.

Fluid bindings are similar to special bindings in Common Lisp [30], except that (1) there is a single namespace for both lexical and fluid bindings, and (2) the scope of a fluidly bound variable is not necessarily global.

(let ([x 3])
  (+ (fluid-let ([x 5])
       x)
     x)) <graphic> 8

(let ([x 'a])
  (letrec ([f (lambda (y) (cons x y))])
    (fluid-let ([x 'b])
      (f 'c)))) <graphic> (b . c)

(let ([x 'a])
  (call/cc
    (lambda (k)
       (fluid-let ([x 'b])
         (letrec ([f (lambda (y) (k '*))])
           (f '*)))))
  x) <graphic> a

fluid-let may be defined in terms of dynamic-wind as follows.

(define-syntax fluid-let
  (lambda (x)
    (syntax-case x ()
      [(_ () b1 b2 ...) #'(let () b1 b2 ...)]
      [(_ ((x e) ...) b1 b2 ...)
       (andmap identifier? #'(x ...))
       (with-syntax ([(y ...) (generate-temporaries #'(x ...))])
         #'(let ([y e] ...)
             (let ([swap (lambda ()
                           (let ([t x]) (set! x y) (set! y t))
                           ...)])
               (dynamic-wind swap (lambda () b1 b2 ...) swap))))])))

Section 5.5. Top-Level Bindings

The procedures described in this section allow the direct manipulation of top-level bindings for variables and keywords. They are intended primarily to support the definition of interpreters or compilers for Scheme in Scheme but may be used to access or alter top-level bindings anywhere within a program whether at top level or not.

procedure: (define-top-level-value symbol obj)
procedure: (define-top-level-value symbol obj env)
returns: unspecified
libraries: (chezscheme)

define-top-level-value is used to establish a binding for the variable named by symbol to the value obj in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

An exception is raised with condition type &assertion if env is not mutable.

A call to define-top-level-value is similar to a top-level define form, except that a call to define-top-level-value need not occur at top-level and the variable for which the binding is to be established can be determined at run time, as can the environment.

(begin
  (define-top-level-value 'xyz "hi")
  xyz) <graphic> "hi"

(let ([var 'xyz])
  (define-top-level-value var "mom")
  (list var xyz)) <graphic> (xyz "mom")

procedure: (set-top-level-value! symbol obj)
procedure: (set-top-level-value! symbol obj env)
returns: unspecified
libraries: (chezscheme)

set-top-level-value! assigns the variable named by symbol to the value obj in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a variable in env or if the variable or environment is not mutable.

set-top-level-value! is similar to set! when set! is used on top-level variables except that the variable to be assigned can be determined at run time, as can the environment.

(let ([v (let ([cons list])
           (set-top-level-value! 'cons +)
           (cons 3 4))])
  (list v (cons 3 4))) <graphic> ((3 4) 7)

procedure: (top-level-value symbol)
procedure: (top-level-value symbol env)
returns: the top-level value of the variable named by symbol in env
libraries: (chezscheme)

If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a variable in env.

top-level-value is similar to a top-level variable reference except that the variable to be referenced can be determined at run time, as can the environment.

(let ([cons +])
  (list (cons 3 4)
        ((top-level-value 'cons) 3 4))) <graphic> (7 (3 . 4))

(define e (copy-environment (scheme-environment)))
(define-top-level-value 'pi 3.14 e)
(top-level-value 'pi e) <graphic> 3.14
(set-top-level-value! 'pi 3.1416 e)
(top-level-value 'pi e) <graphic> 3.1416

procedure: (top-level-bound? symbol)
procedure: (top-level-bound? symbol env)
returns: #t if symbol is defined as a variable in env, #f otherwise
libraries: (chezscheme)

If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

This predicate is useful in an interpreter to check for the existence of a top-level binding before requesting the value with top-level-value.

(top-level-bound? 'xyz) <graphic> #f

(begin
  (define-top-level-value 'xyz 3)
  (top-level-bound? 'xyz)) <graphic> #t

(define e (copy-environment (interaction-environment)))
(define-top-level-value 'pi 3.14 e)
(top-level-bound? 'pi) <graphic> #f
(top-level-bound? 'pi e) <graphic> #t

procedure: (top-level-mutable? symbol)
procedure: (top-level-mutable? symbol env)
returns: #t if symbol is mutable in env, #f otherwise
libraries: (chezscheme)

If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

This predicate is useful in an interpreter to check whether a variable can be assigned before assigning it with set-top-level-value!.

(define xyz 3)
(top-level-mutable? 'xyz) <graphic> #t
(set-top-level-value! 'xyz 4)
(top-level-value 'xyz) <graphic> 4

(define e (copy-environment (interaction-environment) #f))
(top-level-mutable? 'xyz e) <graphic> #f
(set-top-level-value! 'xyz e) <graphic> exception: xyz is immutable

procedure: (define-top-level-syntax symbol obj)
procedure: (define-top-level-syntax symbol obj env)
returns: unspecified
libraries: (chezscheme)

define-top-level-syntax is used to establish a top-level binding for the identifier named by symbol to the value of obj in the environment env. The value must be a procedure, the result of a call to make-variable-transformer, or the result of a call to top-level-syntax. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

An exception is raised with condition type &assertion if env is not mutable.

A call to define-top-level-syntax is similar to a top-level define-syntax form, except that a call to define-top-level-syntax need not occur at top-level and the identifier for which the binding is to be established can be determined at run time, as can the environment.

(define-top-level-syntax 'let1
  (syntax-rules ()
    [(_ x e b1 b2 ...) (let ([x e]) b1 b2 ...)]))
(let1 a 3 (+ a 1)) <graphic> 4

define-top-level-syntax can also be used to attach to an identifier arbitrary compile-time bindings obtained via top-level-syntax.

procedure: (top-level-syntax symbol)
procedure: (top-level-syntax symbol env)
returns: unspecified
libraries: (chezscheme)

top-level-syntax is used to retrieve the transformer, compile-time value, or other compile-time binding to which the identifier named by symbol is bound in the environment env. If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3). All identifiers bound in an environment have compile-time bindings, including variables.

An exception is raised with condition type &assertion if the identifier named by symbol is not defined as a keyword in env.

(define-top-level-syntax 'also-let (top-level-syntax 'let))
(also-let ([x 3] [y 4]) (+ x y)) <graphic> 7

(define foo 17)
(define-top-level-syntax 'also-foo (top-level-syntax 'foo))
also-foo <graphic> 17
(set! also-foo 23)
also-foo <graphic> 23
foo <graphic> 23

The effect of the last example can be had more clearly with alias:

(define foo 17)
(alias also-foo foo)
also-foo <graphic> 17
(set! also-foo 23)
also-foo <graphic> 23
foo <graphic> 23

procedure: (top-level-syntax? symbol)
procedure: (top-level-syntax? symbol env)
returns: #t if symbol is bound as a keyword in env, #f otherwise
libraries: (chezscheme)

If env is not provided, it defaults to the value of interaction-environment, i.e., the top-level evaluation environment (Section 12.3).

All identifiers bound in an environment have compile-time bindings, including variables, so this predicate amounts to a bound check, but is more general than top-level-bound?, which returns true only for bound variables.

(define xyz 'hello)
(top-level-syntax? 'cons) <graphic> #t
(top-level-syntax? 'lambda) <graphic> #t
(top-level-syntax? 'hello) <graphic> #t

(top-level-syntax? 'cons (scheme-environment)) <graphic> #t
(top-level-syntax? 'lambda (scheme-environment)) <graphic> #t
(top-level-syntax? 'hello (scheme-environment)) <graphic> #f

Chez Scheme Version 9 User's Guide
Copyright © 2023 Cisco Systems, Inc.
Licensed under the Apache License Version 2.0 (full copyright notice.).
Revised October 2023 for Chez Scheme Version 9.6.4
about this book