Immutability has a lot of benefits that are orthogonal to variable bindings. Consider this example from Ruby:
irb(main):001:0> a = {hello: "world"}
=> {:hello=>"world"}
irb(main):002:0> b = {greeting: a}
=> {:greeting=>{:hello=>"world"}}
irb(main):003:0> a[:foo] = 'bar'
=> "bar"
irb(main):004:0> b
=> {:greeting=>{:hello=>"world", :foo=>"bar"}}
irb(main):005:0> a = 1
=> 1
irb(main):006:0> b
=> {:greeting=>{:hello=>"world", :foo=>"bar"}}
Notice how by changing a
we also essentially made an implicit and hidden change to b
. Later we rebind a
but b
remains changed. In Elixir this doesn’t happen:
iex(1)> a = %{hello: "world"}
%{hello: "world"}
iex(2)> b = %{greeting: a}
%{greeting: %{hello: "world"}}
iex(3)> Map.put(a, :foo, "bar")
%{foo: "bar", hello: "world"}
iex(4)> b
%{greeting: %{hello: "world"}}
Notice that this holds true even if we rebind a
:
iex(5)> a = Map.put(a, :foo, "bar")
%{foo: "bar", hello: "world"}
iex(6)> b
%{greeting: %{hello: "world"}}
Importantly, immutability means that b
does NOT have to copy the original a
value in order to make this work. Rather, maps are persistent data structures, and so b
can continue pointing to a
's original place in memory. When we “update” a
that’s when a spot in memory is allocated for the new key value pair. If values could be updated in place, we’d either get ruby like behavior, or every data structure would always have to deep copy every other datastructure it pointed to.