4.2.2 Binding constructs

The binding constructs let, let*, letrec, letrec*, let-values, and let*-values give Scheme a block structure, like Algol 60. The syntax of the first four constructs is identical, but they differ in the regions they establish for their variable bindings. In a let expression, the initial values are computed before any of the variables become bound; in a let* expression, the bindings and evaluations are performed sequentially; while in letrec and letrec* expressions, all the bindings are in effect while their initial values are being computed, thus allowing mutually recursive definitions. The let-values and let*-values constructs are analogous to let and let* respectively, but are designed to handle multiple-valued expressions, binding different identifiers to the returned values.

syntax: (let bindings body)

Syntax: Bindings has the form

((variable1 init1) ),

where each init is an expression, and body is a sequence of zero or more definitions followed by a sequence of one or more expressions as described in section 4.1.4 (Procedures). It is an error for a variable to appear more than once in the list of variables being bound.

Semantics: 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 ((2) (3))
  (* x y))6
(let ((2) (3))
  (let ((7)
        ((+ x y)))
    (* z x)))35

See Iteration for a description of “named let”.

syntax: (let* bindings body)

Syntax: Bindings has the form

((variable1 init1) ),

and body is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4 (Procedures).

Semantics: The let* binding construct is similar to let, but the bindings are performed sequentially from left to right, and the region of a binding indicated by ‘(variable init)’ is that part of the let* expression to the right of the binding. Thus the second binding is done in an environment in which the first binding is visible, and so on. The variables need not be distinct.

syntax: (letrec bindings body)

Syntax: Bindings has the form

((variable1 init1) ),

and body is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4 (Procedures). It is an error for a variable to appear more than once in the list of variables being bound.

Semantics: The variables are bound to fresh locations holding unspecified values, 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

One restriction on letrec is very important: if it is not possible to evaluate each init without assigning or referring to the value of any variable, it is an error. The restriction is necessary because letrec is defined in terms of a procedure call where a lambda expression binds the variables to the values of the inits. In the most common uses of letrec, all the inits are lambda expressions and the restriction is satisfied automatically.

syntax: (letrec* bindings body)

Syntax: Bindings has the form

((variable1 init1) ),

and body is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4 (Procedures). It is an error for a variable to appear more than once in the list of variables being bound.

Semantics: The variables are bound to fresh locations, each variable is assigned in left-to-right order to the result of evaluating the corresponding init (interleaving evaluations and assignments), 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.

If it is not 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, it is an error. Another restriction is that it is an error to invoke the continuation of an init more than once.

;; Returns the arithmetic, geometric, and
;; harmonic means of a nested list of numbers
(define (means ton)
  (letrec*
     ((mean
        (lambda (f g)
          (((sum g tonn))))
      (sum
        (lambda (g ton)
          (if (null? ton)
            (+)
            (if (number? ton)
                (g ton)
                ((sum g (car ton))
                   (sum g (cdr ton)))))))
      ((sum (lambda (x1ton)))
    (values (mean values values)
            (mean exp log)
            (mean / /))))

Evaluating (means '(3 (1 4))) returns three values:
8/3, 2.28942848510666 (approximately), and 36/19.

syntax: (let-values mv binding spec body)

Syntax: Mv binding spec has the form

((formals1 init1) ),

where each init is an expression, and body is zero or more definitions followed by a sequence of one or more expressions as described in section 4.1.4 (Procedures). It is an error for a variable to appear more than once in the set of formals.

Semantics: The inits are evaluated in the current environment (in some unspecified order) as if by invoking call-with-values, and the variables occurring in the formals are bound to fresh locations holding 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 expression 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.

It is an error if the formals do not match the number of values returned by the corresponding init.

(let-values (((root rem) (exact-integer-sqrt 32)))
  (* root rem))35
syntax: (let*-values mv binding spec body)

Syntax: Mv binding spec has the form

((formals init) ),

and body is a sequence of zero or more definitions followed by one or more expressions as described in section 4.1.4 (Procedures). In each formals, it is an error if any variable appears more than once.

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

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