# 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)) (1 2)
(let ()
(define-values (x y . z) (values 1 2 3 4))
(list x y z)) (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)) (0 1 3 6 10 15)
(define cycle
(rec self
(list (lambda () self))))
(eq? ((car cycle)) cycle) #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)) 8
(let ([x 'a])
(letrec ([f (lambda (y) (cons x y))])
(fluid-let ([x 'b])
(f 'c)))) (b . c)
(let ([x 'a])
(call/cc
(lambda (k)
(fluid-let ([x 'b])
(letrec ([f (lambda (y) (k '*))])
(f '*)))))
x) 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) "hi"
(let ([var 'xyz])
(define-top-level-value var "mom")
(list var xyz)) (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))) ((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))) (7 (3 . 4))
(define e (copy-environment (scheme-environment)))
(define-top-level-value 'pi 3.14 e)
(top-level-value 'pi e) 3.14
(set-top-level-value! 'pi 3.1416 e)
(top-level-value 'pi e) 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) #f
(begin
(define-top-level-value 'xyz 3)
(top-level-bound? 'xyz)) #t
(define e (copy-environment (interaction-environment)))
(define-top-level-value 'pi 3.14 e)
(top-level-bound? 'pi) #f
(top-level-bound? 'pi e) #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) #t
(set-top-level-value! 'xyz 4)
(top-level-value 'xyz) 4
(define e (copy-environment (interaction-environment) #f))
(top-level-mutable? 'xyz e) #f
(set-top-level-value! 'xyz e) 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)) 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)) 7
(define foo 17)
(define-top-level-syntax 'also-foo (top-level-syntax 'foo))
also-foo 17
(set! also-foo 23)
also-foo 23
foo 23`

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

`(define foo 17)
(alias also-foo foo)
also-foo 17
(set! also-foo 23)
also-foo 23
foo 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) #t
(top-level-syntax? 'lambda) #t
(top-level-syntax? 'hello) #t
(top-level-syntax? 'cons (scheme-environment)) #t
(top-level-syntax? 'lambda (scheme-environment)) #t
(top-level-syntax? 'hello (scheme-environment)) #f`

Chez Scheme Version 10 User's Guide
Copyright © 2024 Cisco Systems, Inc.
Licensed under the Apache License Version 2.0 (full copyright notice.). Revised February 2024 for Chez Scheme Version 10.0.0
about this book