Elixirのimmutableについてまとめます。
Elixirでは代入ではなく, 変数を右辺の値に 束縛(バインド) することです。
##Elixirのデータ構造はimmutableなのか
破壊的操作ができるmutableなデータ構造をElixirは持ちません。
Rubyではmutableなデータ構造があるので, 比較をRubyとElixirのコードで説明していきたいと思います。
まずはRubyのコードです。
irb(main):001:0> scores = { "Alice" => 50, "Bob" => 60, "Carol" => 90, "David" => 40 }
=> {"Alice"=>50, "Bob"=>60, "Carol"=>90, "David"=>40}
irb(main):002:0> scores.delete("Carol")
=> 90
irb(main):003:0> scores
=> {"Alice"=>50, "Bob"=>60, "David"=>40}
RubyではHashがmutableなデータ構造なので, scoresのKeyがCarolのKeyと値が削除されたのがわかると思います。
次はElixirのコードを見ていきます。
#Elixir
iex(1)> scores = %{ "Alice" => 50, "Bob" => 60, "Carol" => 90, "David" => 40 }
%{"Alice" => 50, "Bob" => 60, "Carol" => 90, "David" => 40}
iex(2)> Map.delete(scores, "Carol")
%{"Alice" => 50, "Bob" => 60, "David" => 40}
iex(3)> scores
%{"Alice" => 50, "Bob" => 60, "Carol" => 90, "David" => 40}
Elixir破壊的操作ができるデータ構造はないので, 変数がバインドしているデータが変わることはありません。
ガーベジコレクトされるか, スコープの範囲外になるまで。
###メリット
- コード上で変数がバインドしているデータがimmutableなので曖昧さがない。
scores.delete("Carol")
このようなケースで, scoresの内部表現を変更するのか, "Carol"がkeyのkeyと値を削除したHashを返すだけなのか, またはどちらもなのか と曖昧さがあるのが
Elixirではデータを変換した新しいコピーを返すだけなので起こらない。
##Elixirの変数はimmutableなのか
変数はimmutableではない
Elixirの変数はmutableです。
Elixirでの変数はラベルのような感覚だなと自分では思っています。
変数に値が入っているのではなく, 値の別名をつけるイメージです。
再バインドはオブジェクトの状態をまったく変えません。
iex(3)> scores
%{"Alice" => 50, "Bob" => 60, "Carol" => 90, "David" => 40}
iex(4)> scores = %{"Alice" => 50, "Bob" => 60, "Carol" => 90}
値は同じメモリの位置にあるが、変数は別のメモリの値を指すようになり、不変性が保持されます。
Erlangでは再バインドは利用できませんが、Elixirでは、Erlang VMの実装のおかげで、再バインドをすることができます。
Erlangで変数を再バインドしたい時は、パターンマッチよりもはるかに多いとjoseさんが言っており, それを考慮されこのような実装になっているのではないかと思います。
そして, Elixirで再バインドを防ぎパターンマッチをしたい場合はピン演算子を使います。
iex> x = 1
1
iex> y = 2
2
iex> x = y
2
iex> x
2
iex> ^x = 1
** (MatchError) no match of right hand side value: 1