Perhaps you'd be more comfortable considering this equivalent syntax:

(let [x 1]
  (let [y 2]
    (let [x (+ x y)]
      x)))

In principle, the multiple-binding let is transformed to this version
and thus has identical semantics; in practice, the compiler does
something more efficient but equivalent.

That is, the innermost let is establishing a new scope, with a new
binding x, whose value is computed by reference to the outer scope's
x. The outer x remains unchanged, as you can see if you do something
like

(let [x 1]
  (let [y 2]
    (let [x (+ x y)]
      x))
  x)

This notion of nested scopes is present in many common languages, as
is allowing nearby bindings to shadow more-distant bindings of the
same name. And of course you can write something that "looks"
imperative, like

(let [x 1
      x (inc x)
      x (* 2 x)]
  x)

But because the general principle of immutability holds, anyone
reading your code can easily "fold up" all the x-related expressions,
into (let [x (* 2 (inc 1))] x). The same would *not* be true if real
mutability were happening, as with a Java for loop, because statements
could be executed multiple times with different values of x. Not so
here: each expression is evaluated once, with a fixed value of x for
each.

Stylistically, it's often not very nice to rebind x a number of times;
it's better to choose descriptive names for the intermediate steps.
But there are certainly times occasions where using the same name can
clarify meaning: for example, you're just "tidying up" x into a nicer
form, or canonicalizing it in some way.

On Aug 27, 2:37 am, Terje Dahl <te...@terjedahl.no> wrote:
> I was surprised to discover that the following was possible:
>
> (let [
>        x 1
>        y 2
>        x (+ x y) ]
>      x )
>
> This runs and returns 3!
>
> This feels an awful lot like variables and procedural programming.
> It is left up to the developer to not resetting a "variable" - by
> convention - and if he reset the identifiers, he ends up with hard-to-
> debug spagetti-code.
>
> I would have thought that the system would have protested when I
> attempted to set 'x' to a new value!
> Amit Rathore writes in "Clojure In Action" about : "[...] are locally
> named values (they are like variables, but they can’t vary since
> Clojure’s data-structures are immutable)"
>
> In combination underscore identifiers for throw-away return values,
> one can in fact write a whole procedural program within the vector
> parens of a let-statement!
>
> Perhaps this is something that should be changed in future versions of
> the language?!

-- 
You received this message because you are subscribed to the Google
Groups "Clojure" group.
To post to this group, send email to clojure@googlegroups.com
Note that posts from new members are moderated - please be patient with your 
first post.
To unsubscribe from this group, send email to
clojure+unsubscr...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/clojure?hl=en

Reply via email to