4.6.1 Parallel Binding: let
(let ([id expr] ) body )
The ids are bound “in parallel.” That is, no id is bound in the right-hand side expr for any id, but all are available in the body. The ids must be different from each other.
("Bob" "Robert" "Bobby")
eval:7:0: let: duplicate identifier at: me in: (let ((me
"Bob") (me "Robert")) me)
The fact that an id’s expr does not see its own binding is often useful for wrappers that must refer back to the old value:
Occasionally, the parallel nature of let bindings is convenient for swapping or rearranging a set of bindings:
The characterization of let bindings as “parallel” is not meant to imply concurrent evaluation. The exprs are evaluated in order, even though the bindings are delayed until all exprs are evaluated.
4.6.2 Sequential Binding: let*
(let* ([id expr] ) body )
The difference is that each id is available for use in later exprs, as well as in the body. Furthermore, the ids need not be distinct, and the most recent binding is the visible one.
(("Borroughs") ("Rice" "Borroughs") ("Edgar" "Rice" "Borroughs"))
("Edgar" "Rice" "Borroughs")
("Edgar" "Rice" "Borroughs")
4.6.3 Recursive Binding: letrec
(letrec ([id expr] ) body )
While let makes its bindings available only in the bodys, and let* makes its bindings available to any later binding expr, letrec makes its bindings available to all other exprs – even earlier ones. In other words, letrec bindings are recursive.
(vine vine tarzan vine)
While the exprs of a letrec form are typically lambda expressions, they can be any expression. The expressions are evaluated in order, and after each value is obtained, it is immediately associated with its corresponding id. If an id is referenced before its value is ready, the result is #<undefined>, as just as for internal definitions.
4.6.4 Named let
A named let is an iteration and recursion form. It uses the same syntactic keyword let as for local binding, but an identifier after the let (instead of an immediate open parenthesis) triggers a different parsing.
(let _proc-id ([_arg-id _init-expr] )
A named let form is equivalent to
(proc-id init-expr ...))
That is, a named let binds a function identifier that is visible only in the function’s body, and it implicitly calls the function with the values of some initial expressions.
> (duplicate 1 (list "apple" "cheese burger!" "banana"))
("apple" "cheese burger!" "cheese burger!" "banana")
4.6.5 Multiple Values: let-values, let*-values, letrec-values
(let-values ([(id ) expr] )
(let*-values ([(id ) expr] )
(letrec-values ([(id ) expr] )
Each expr must produce as many values as corresponding ids. The binding rules are the same for the forms without -values forms: the ids of let-values are bound only in the bodys, the ids of let*-valuess are bound in exprs of later clauses, and the ids of letrec-values are bound for all exprs.