Lexical Binding - MIT/GNU Scheme 9.2
Description: MIT/GNU Scheme 9.2

Next: Dynamic Binding, Previous: Lambda Expressions, Up: Special Forms

### 2.2 Lexical Binding

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).

— special form: let ((variable init) ...) expression expression ...

> 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)))                           =>  9

> See Iteration, for information on “named `let`”.

— special form: let* ((variable init) ...) expression expression ...

> `let*` is similar to `let`, but the bindings are performed sequentially from left to right, and the region of a binding 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.

> 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

— special form: letrec ((variable init) ...) expression expression ...

> 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))                           =>  #t

> One 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 of `letrec`, all the inits are `lambda` or `delay` expressions and the restriction is satisfied automatically.