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.
(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 ⟨init⟩s are evaluated in the current environment (in some unspecified order), the ⟨variable⟩s 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 Iteration for a description of “named let”.
(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 ⟨variable⟩s need not
be distinct.
(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 ⟨variable⟩s are bound to fresh locations
holding unspecified values, the ⟨init⟩s 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 ⟨variable⟩s to the
values of the ⟨init⟩s. In the most common uses of letrec,
all the ⟨init⟩s are lambda expressions and the restriction
is satisfied automatically.
(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 ⟨variable⟩s 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) (f (/ (sum g ton) n)))) (sum (lambda (g ton) (if (null? ton) (+) (if (number? ton) (g ton) (+ (sum g (car ton)) (sum g (cdr ton))))))) (n (sum (lambda (x) 1) ton))) (values (mean values values) (mean exp log) (mean / /))))
Evaluating (means '(3 (1 4))) returns three
values:
8/3, 2.28942848510666
(approximately), and 36/19.
(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 ⟨init⟩s 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 ⟨init⟩s, 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
(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 ⟨init⟩s are evaluated and bindings
created sequentially from left to right, with the region of the bindings
of each ⟨formals⟩ including the ⟨init⟩s 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 '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)