Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms
The three binding constructs let
, let*
, and letrec
,
give Scheme block structure. The syntax of the three 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 evaluations and bindings are sequentially interleaved.
And in a letrec
expression, all the bindings are in effect while
the initial values are being computed (thus allowing mutually recursive
definitions).
The inits are evaluated in the current environment (in some unspecified order), the variables are bound to fresh locations holding the results, the expressions are evaluated sequentially in the extended environment, and the value of the last expression is returned. Each binding of a variable has the expressions as its region.
MIT/GNU Scheme allows any of the inits to be omitted, in which case the corresponding variables are unassigned.
Note that the following are equivalent:
(let ((variable init) ...) expression expression ...) ((lambda (variable ...) expression expression ...) init ...)Some examples:
(let ((x 2) (y 3)) (* x y)) => 6 (let ((x 2) (y 3)) (let ((foo (lambda (z) (+ x y z))) (x 7)) (foo 4))) => 9See Iteration, for information on “named
let
”.
let*
is similar tolet
, but the bindings are performed sequentially from left to right, and the region of a binding is that part of thelet*
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.Note that the following are equivalent:
(let* ((variable1 init1) (variable2 init2) ... (variableN initN)) expression expression ...) (let ((variable1 init1)) (let ((variable2 init2)) ... (let ((variableN initN)) expression expression ...) ...))An example:
(let ((x 2) (y 3)) (let* ((x 7) (z (+ x y))) (* z x))) => 70
The variables are bound to fresh locations holding unassigned values, the inits are evaluated in the extended environment (in some unspecified order), each variable is assigned to the result of the corresponding init, the expressions are evaluated sequentially in the extended environment, and the value of the last expression is returned. Each binding of a variable has the entire
letrec
expression as its region, making it possible to define mutually recursive procedures.MIT/GNU Scheme allows any of the inits to be omitted, in which case the corresponding variables are unassigned.
(letrec ((even? (lambda (n) (if (zero? n) #t (odd? (- n 1))))) (odd? (lambda (n) (if (zero? n) #f (even? (- n 1)))))) (even? 88)) => #tOne restriction on
letrec
is very important: it shall be possible to evaluated each init without assigning or referring to the value of any variable. If this restriction is violated, then it is an error. The restriction is necessary because Scheme passes arguments by value rather than by name. In the most common uses ofletrec
, all the inits arelambda
ordelay
expressions and the restriction is satisfied automatically.