If nested lets define variables by the same name, then the
uses of that name in the body of the inner let will refer to the
bindings created by the inner let.
Consider the following code fragment:
(let ((x 10) ; outer binding of x
(a 20)) ; binding of a
(foo x)
(let ((x (bar)) ; inner binding of x
(b (baz x x))) ; binding of b
(quux x a)
(quux y b))
(baz x a) ; refers to outer x (and a)
(baz x b)) ; illegal?
When control enters the outer let, the inital values for the
variables are computed. In this case, that's just the literal values
10 and 20. Then storage is allocated for the variables,
and initialized with those values. Once that's done, the meaning of
the names x and a changes--they now refer to the new
storage for (bindings of) the let variables x and
a---and then the body expressions are evaluated.
Similarly, when control enters the inner let, the inital values
are computed by the calls to bar and baz, and then
storage for x and b is allocated and initialized with
those values. Then the meanings of the names x and b
change, to refer to the new storage (bindings) of those variables.
(For example, the value of x when (baz x x) is evaluated
is still 10, because x still refers to the outer x.)
In this example, the meaning of the identifier x changes when
we enter the inner let. We say that the inner let variable
x shadows the outer one, within the body of the let.
The outer x is no longer visible, because the inner one is.
When we exit a let (after evaluating its body expressions), the
bindings introduced by the let "go out of scope," i.e., aren't visible
anymore. (For example, when we evaluate the expression (baz x a)
in the body of the outer let, x refers to the binding
introduced by the outer let---the x introduced by the
inner let is no longer visible.
Likewise, in the example code fragment, the b in the last
expression, (baz x b), does not refer to the inner let's
binding of b. Unless there is a binding of b in some
outer scope we haven't shown (such as a top-level binding), then
this will be an error.