The binding constructs let
, let*
, letrec
,
and letrec*
give Scheme a block structure, like Algol 60.
The syntax of these 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.
Syntax: let
((
pattern
init
)
...
)
body
Declare new local variables as found in the
pattern
s. Eachpattern
is matched against the correspondinginit
. Theinit
s are evaluated in the current environment (in left-to-right onder), thevariable
s in thepatterns
s are bound to fresh locations holding the matched results, thebody
is evaluated in the extended environment, and the values of the last expression of body are returned. Each binding of a variable hasbody
as its region.(let ((x 2) (y 3)) (* x y)) ⇒ 6(let ((x 2) (y 3)) (let ((x 7) (z (+ x y))) (* z x))) ⇒ 35An example with a non-trivial pattern:
(let (([a::double b::integer] (vector 4 5))) (cons b a)) ⇒ (5 . 4.0)
Syntax: let*
((
pattern
init
)
...
)
body
The
let*
binding construct is similar tolet
, but the bindings are performed sequentially from left to right, and the region of avariable
s in apattern
is that part of thelet*
expression to the right of thepattern
. Thus the second pattern is matched in an environment in which the bindings from the first pattern are visible, and so on.(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) ⇒ 70
Syntax: letrec
((
variable
[::
] type
init
)
...
)
body
Syntax: letrec*
((
variable
[::
] type
init
)
...
)
body
The
variable
s are bound to fresh locations, eachvariable
is assigned in left-to-right order to the result of the correspondinginit
, thebody
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 avariable
has the entireletrec
orletrec*
expression as its region, making it possible to define mutually recursive procedures.In Kawa
letrec
is defined as the same asletrec*
. In standard Scheme the order of evaluation of theinit
s is undefined, as is the order of assignments. If the order matters, you should useletrec*
.If it is not possible to evaluate each
init
without assigning or referring to the value of the correspondingvariable
or the variables that follow it, it is an error.(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) ⇒ #t