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. 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 also “named let,” section 4.2.4.
(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.
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.
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) ⇒ 70
(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. 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. 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>, 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.
(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
(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. 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. 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)