Immutability beginner question

hello,
As I stated in another topic, I’m a complete beginner in Elixir.

I approached two learning resources in order to learn Elixir:

  • Pragmatic Studio;
  • Learn Functional Programming in Elixir.

I have a general question about immutability.

If we are able to change a value of a variable (I mean to change the binding of a variable),

for example:
a = 1
and then:
a = 2

this is a mutable state actually. Isn’t it?
I think about this topic at the keyword “val” in Scala.

So why we talk about immutability of variables?

I have got also an example from Pragmatic Studio where you are able to change the values inside a map (not the maps’ structure itself) with a:

%{ conv | resp_body: “Bears, Lions, Tigers” }

That change the value of resp_body inside the map conv.

So my question is a philosophical one:
in which meaning Elixir think about “immutability”?

Thank you in advance for your answers.

1 Like

Hello,

Here is the classic example for immutability rebinding (improperly named immutability, thanks @hauleth ):

iex(1)> a = 1
1
iex(2)> f = fn -> a end
#Function<45.97283095/0 in :erl_eval.expr/5>
iex(3)> a = 2
2
iex(4)> f.()
1

As you said it, it is a new binding. The value of a is not changed, but the name “a” is bound to a new value.

Also, your conv map is not changed, you are creating a new map:

iex(1)> conv = %{a: 1, resp_body: "Hello"}
%{a: 1, resp_body: "Hello"}
iex(2)> %{ conv | resp_body: "Bears, Lions, Tigers" }
%{a: 1, resp_body: "Bears, Lions, Tigers"}
iex(3)> conv
%{a: 1, resp_body: "Hello"}
5 Likes

Those 2 are different a’s … It’s almost as same as you would write:

a1 = 1
a2 = 2

The only difference is that originally first value is lost and second example value is kept in other variable (a1).

This syntax creates a new map which origin is conv and it’s response_body is changed to “Bears, Lions, Tigers”.

1 Like

It’s not mutable state. It’s however “variable rebinding”. Your example means there are now two values in memory. 1 and 2. So no memory was mutated. However the value the binding a points to changed. It no longer points to the value of 1, but the value 2.

Erlang doesn’t even have variable rebinding (and you can see that in the code elixir compiles to as well). There you’d essentially need to write:

a1 = 1
a2 = 2
2 Likes

No, you create new map with changed value.

In immutability as memory immutability. You must not confuse single assignment with immutability. You can have immutability with multiple assignments and you can have mutability with single assignment. These are 2 completely different topics.

2 Likes

Thank you all for your reply!

The replies from you all clarified a little bit more the topic.

So - if I well understood - I have this situation actually:

variables |------ vs --------| memory (example: heap)

a = 1

a |------ vs --------| 1

a = 2

Imperative - not functional - approach:

a |------ vs --------| 2

The value is overwritten: it means that I overwrite the new value in the same memory cells.

Elixir approach:

a = 2

  •            |------ vs --------|  1
    

a |------ vs --------| 2

Is it correct?

That means that there is a new memory cell occupation for the new value.

(That make me think about the “garbage collection” of the values no more bonded).

Is it correct?

Also how can I have the pointer to old values (for other processes, for example). I think I can not.

(and yes:

%{ conv | resp_body: “Bears, Lions, Tigers” }

this was a mistake of my interpretation).

That works in this way, because this a closure to previous value. I think a different topic in respect to re-binding. It isn’t?

Yes, but it is about immutability. In languages with mutability you can change value of a and it will change in the closure. It is how “old-school” private members in JS worked:

function Counter() {
  var value = 0;

  return {
    inc: function() { value += 1; return value; },
    dec: function() { value -= 1; return value; }
  }
}

var counter = Counter();

counter.inc() // => 1
counter.inc() // => 2
counter.dec() // => 1

Simplifying things - yes. However sometimes new binding can reuse old value due to compiler optimisations, for example:

def foo({a, b}) when a > 10, do: {a, b}

Can reuse tuple, so in the end it will be the same as:

def foo({a, _} = tuple) when a > 10, do: tuple

Instead of creating new tuple.

However conceptually you can assume that each assignment will use new memory cell. That will simplify the reasoning.

2 Likes

That works in this way, because this a closure to previous value. I think a different topic in respect to re-binding. It isn’t?

I guess you are right, it is actually about scope. But it generally helps to understand how variables behave in Elixir.

Also how can I have the pointer to old values (for other processes, for example). I think I can not.

You cannot, unless of course you keep a variable bound to it. Processes do not share memory (with a few exceptions), so if you pass a value to another process the value be copied to the other process own memory space.

That means that there is a new memory cell occupation for the new value.

As @hauleth said values can be reused but also values like the first a in basic code like a = 1; do_stuff(a); a = 2 will typically only exist on the stack as they are short lived.

1 Like

Thank you all for your replies!

1 Like