Next: , Previous: , Up: baselib expressions   [Index]


4.4.6 Binding constructs

The binding constructs described in this section create local bindings for variables that are visible only in a delimited region. The syntax of the constructs let, let*, letrec, and letrec* is identical, but they differ in the regions they establish for their variable bindings and in the order in which the values for the bindings are computed.

In addition, the binding constructs let-values and let*-values generalize let and let* to allow multiple variables to be bound to the results of expressions that evaluate to multiple values. They are analogous to let and let* in the way they establish regions: in a let-values expression, the initial values are computed before any of the variables become bound; in a let*-values expression, the bindings are performed sequentially.

Syntax: let ?bindings ?body

?bindings must have the form:

((?variable1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies. Any variable must not appear more than once in the ?variables.

The ?inits are evaluated in the current environment (in some unspecified order), the ?variables are bound to fresh locations holding the results, the ?body is evaluated in the extended environment, and the values of the last expression of ?body are returned. Each binding of a ?variable has ?body as its region.

(let ((x 2) (y 3))
  (* x y))                      ⇒ 6

(let ((x 2) (y 3))
  (let ((x 7)
        (z (+ x y)))
    (* z x)))                   ⇒ 35

See also named let.

Syntax: let* ?bindings ?body

?bindings must have the form:

((?variable1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies.

The let* form is similar to let, but the ?inits are evaluated and bindings created sequentially from left to right, with the region of each binding including the bindings to its right as well as ?body. Thus the second ?init is evaluated in an environment in which the first binding is visible and initialized, and so on.

(let ((x 2) (y 3))
  (let* ((x 7)
         (z (+ x y)))
    (* z x)))             ⇒ 70

NOTE While the variables bound by a let expression must be distinct, the variables bound by a let* expression need not be distinct.

Syntax: letrec ?bindings ?body

?bindings must have the form:

((?variable1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies. Any variable must not appear more than once in the ?variables.

The ?variables are bound to fresh locations, the ?inits are evaluated in the resulting environment (in some unspecified order), each ?variable is assigned to the result of the corresponding ?init, the ?body is evaluated in the resulting environment, and the values of the last expression in ?body are returned. Each binding of a ?variable has the entire letrec expression as its region, making it possible to define mutually recursive procedures.

(letrec ((even?
          (lambda (n)
            (if (zero? n)
                #t
                (odd? (- n 1)))))
         (odd?
          (lambda (n)
            (if (zero? n)
                #f
                (even? (- n 1))))))
  (even? 88))
⇒ #t

It should be possible to evaluate each ?init without assigning or referring to the value of any ?variable. In the most common uses of letrec, all the ?inits are lambdas and the restriction is satisfied automatically. Another restriction is that the continuation of each ?init should not be invoked more than once.

Implementation responsibilities: Implementations must detect references to a ?variable during the evaluation of the ?init expressions (using one particular evaluation order and order of evaluating the ?init expressions). If an implementation detects such a violation of the restriction, it must raise an exception with condition type &assertion. Implementations may or may not detect that the continuation of each ?init is invoked more than once. However, if the implementation detects this, it must raise an exception with condition type &assertion.

Syntax: letrec* ?bindings ?body

?bindings must have the form:

((?variable1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies. Any variable must not appear more than once in the ?variables.

The ?variables are bound to fresh locations, each ?variable is assigned in left–to–right order to the result of evaluating the corresponding ?init, the ?body is evaluated in the resulting environment, and the values of the last expression in ?body are returned. Despite the left–to–right evaluation and assignment order, each binding of a ?variable has the entire letrec* expression as its region, making it possible to define mutually recursive procedures.

(letrec* ((p
           (lambda (x)
             (+ 1 (q (- x 1)))))
          (q
           (lambda (y)
             (if (zero? y)
                 0
                 (+ 1 (p (- y 1))))))
          (x (p 5))
          (y x))
  y)
⇒  5

It must be possible to evaluate each ?init without assigning or referring to the value of the corresponding ?variable or the ?variable of any of the bindings that follow it in ?bindings. Another restriction is that the continuation of each ?init should not be invoked more than once.

Implementation responsibilities: Implementations must, during the evaluation of an ?init expression, detect references to the value of the corresponding ?variable or the ?variable of any of the bindings that follow it in ?bindings. If an implementation detects such a violation of the restriction, it must raise an exception with condition type &assertion. Implementations may or may not detect that the continuation of each ?init is invoked more than once. However, if the implementation detects this, it must raise an exception with condition type &assertion.

Syntax: let-values ?mv-bindings ?body

?mv-bindings must have the form:

((?formals1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies. Any variable must not appear more than once in the set of ?formals.

The ?inits are evaluated in the current environment (in some unspecified order), and the variables occurring in the ?formals are bound to fresh locations containing the values returned by the ?inits, where the ?formals are matched to the return values in the same way that the ?formals in a lambda are matched to the arguments in a procedure call. Then, the ?body is evaluated in the extended environment, and the values of the last expression of ?body are returned. Each binding of a variable has ?body as its region. If the ?formals do not match, an exception with condition type &assertion is raised.

(let-values (((a b) (values 1 2))
             ((c d) (values 3 4)))
  (list a b c d))
⇒ (1 2 3 4)

(let-values (((a b . c) (values 1 2 3 4)))
  (list a b c))
⇒ (1 2 (3 4))

(let ((a 'a) (b 'b) (x 'x) (y 'y))
  (let-values (((a b) (values x y))
               ((x y) (values a b)))
    (list a b x y)))
⇒ (x y a b)

Notice the following special case:

(let-values ((all (values 1 2 3)))
  all)
⇒ (1 2 3)

which corresponds to the lambda formals:

(define f
  (lambda all
    all))

(f 1 2 3)       ⇒ (1 2 3)
Syntax: let*-values ?mv-bindings ?body

?mv-bindings must have the form:

((?formals1 ?init1) …)

where each ?init is an expression, and ?body is as described in baselib bodies. In each ?formals, any variable must not appear more than once.

The let*-values form is similar to let-values, but the ?inits are evaluated and bindings created sequentially from left to right, with the region including the bindings to its right as well as ?body. Thus the second ?init is evaluated in an environment in which the bindings of the first ?formals is visible and initialized, and so on.

(let ((a 'a) (b 'b) (x 'x) (y 'y))
  (let*-values (((a b) (values x y))
                ((x y) (values a b)))
    (list a b x y)))
⇒ (x y x y)

NOTE While all of the variables bound by a let-values expression must be distinct, the variables bound by different ?formals of a let*-values expression need not be distinct.


Next: , Previous: , Up: baselib expressions   [Index]