clojureで「再代入できない」と言われる意図は何なのか


`clojure`初心者です。  
非常に初歩的な質問ですみませんが、`clojure`といえども、`var`に対する再束縛は問題なくできると考えてよいのでしょうか？

[code]

    user => (def a 100)
    user => a
    ; 100と表示
    user => (def a 200)
    user => a
    ; 200と表示
    
[/code]

`clojure`が変数(`var`のこと)の参照先オブジェクトを変更できないイミュータブルな性質を持つことは`java`等の経験から理解できます。

その一方で、関数型言語では「再代入」はできない、なんていう言葉も何度もそれとなく聞いておりまして、それはなんとなくそういうものなのかと思っていました。それゆえ、本日思わぬ形で上記の例に出会ってしまい、今までも似たようなものは見てたのでしょうが、はたと気がついて混乱しております。

私の拙い理解では、これだと再代入は「できている」、`clojure`流の言葉では「再束縛」が起きているとでも言えばいいのでしょうか、とにかく同じ`var`に別の値を結びつけることができています。

[code]

    user=> (defn xyz []                               
             (let [abc 100]                    
                  (let [abc 200] (+ abc 100))))
    user=> (xyz)
    300
    
[/code]

`let` を使っても同じことです。

変数の値の再代入は他の言語でも普通にありますし、この動作がおかしいなどと言うつもりは毛頭ないのですが、いわゆる「関数型言語に再代入はない」的な言説が何を言わんとしているのかがわからなくなりました。再代入ができないのではなく、再代入を避けやすい、という意味で使われることが多いのでしょうか。

「プログラミングclojure」という本を読みながら学習しているのですが、このことについては特に記述がないか、見つけられません。なので、しょーもない質問とは思いますが、おかしな理解をするのも良くないので、どなたか教えてくださると嬉しいです。




`def`は、その名前空間のトップレベルのvar（≒グローバル変数）を新しく作ります。まあこれは例外ですね。現代的なプログラミング言語では、グローバル変数の変更はあまり重要ではありませんし。

`let`の入れ子の場合、「同じ名前だけど別の変数」を定義しているだけです。内側の`let`で`abc`に別の値を束縛しても、外側の`let`の`abc`は別の変数なので、影響を与えません。別の関数の仮引数の変数名が呼び出し元の変数名とカブっている場合（以下のコード片参照）とまったく同じです。

[code]

    (defn foo [abc] (+ abc 100))
    (defn xyz [] (let [abc 100] (foo 200)))
    (xyz)       ;;=> 300
    
[/code]

変数を再代入できるというのは、同じ名前というだけではなく、同じ変数の値を変更できることです。たとえば以下のRubyコード片のような書き方は、Clojureだとそのままではできません（やるにはrefなどを使う）。

[code]

    x = 3
    3.times { x *= 2 }
    x           #=> 24
    
[/code]




`def`は、その名前空間のトップレベルのvar（≒グローバル変数）を新しく作ります。まあこれは例外ですね。現代的なプログラミング言語では、グローバル変数の変更はあまり重要ではありませんし。

`let`の入れ子の場合、「同じ名前だけど別の変数」を定義しているだけです。内側の`let`で`abc`に別の値を束縛しても、外側の`let`の`abc`は別の変数なので、影響を与えません。別の関数の仮引数の変数名が呼び出し元の変数名とカブっている場合（以下のコード片参照）とまったく同じです。

[code]

    (defn foo [abc] (+ abc 100))
    (defn xyz [] (let [abc 100] (foo 200)))
    (xyz)       ;;=> 300
    
[/code]

変数を再代入できるというのは、同じ名前というだけではなく、同じ変数の値を変更できることです。たとえば以下のRubyコード片のような書き方は、Clojureだとそのままではできません（やるにはrefなどを使う）。

[code]

    x = 3
    3.times { x *= 2 }
    x           #=> 24
    
[/code]

