Immutability in Elixir
It has been said that one of the best way to cement knowledge is to share it with others. To follow that principle I’m starting to share some concepts I’m learning about the Elixir programming language. Coming from OOP, some of these ideas might look strange at the beginning, one of them is immutability.
In many programming languages we are allowed to assign a value to a variable, and then change it during the execution of a program. We can replace the value in a certain memory position with another value, it seems legit after all and the readability of our program certainly increase because without reassignment we will end up doing something like this all the time:
# Without reassignment
name = "john"
name_after_one_function = function1(name)
name_after_two_functions = function2(name_after_one_function)
name_after_three_functions = function3(name_after_two_functions)
During the program execution we usually don’t know the exact time when this change happens, and we usually don’t write our programs taking care of that because it’s certainly more complicated.
But what happens when a value in memory change while it’s being used by multiple instances of our program? Suppose that not only the exact value change, but also the type.
This behavior is known as mutability, and in a concurrent environment it is source of subtle bugs that are difficult to track and replicate. It also drive towards overcomplicated code, written ad-hoc to deal with synchronisation issues, thus taking care of the time variable. This may reduce the risks of concurrent processes accessing the same resources, but at a high price.
Other programming languages instead, like Erlang and obviously Elixir that is built on top of it, embraces immutability.
They simply don’t allow values in a certain memory location to change. Never. Until the variable gets garbage collected or is out of scope.
This way when we reference the value
1 with the variable
a we know for sure that that value won’t change during the execution of your program, and we simply don’t have to take care of synchronisation issues in a concurrent environment.
That’s also why we say “reference the value with”. Look at the following example:
# Simple assignment
a = 1
b = 3
= is the match operator and the snippet above is a match, or binding, we are referencing the values
3 with the variables
b. The meaning of the
= operator has to be intended like its algebraic meaning, not like a real assignment.
Based on the first example in this post you might be right think that, if Elixir embraces immutability, we are ending up doing stuff like in the first example of this post. We need to bind a new variable at every computation result (at least if we don’t execute every command on the same line).
Well that’s only partially true, because Elixir allows variable rebinding:
# rebinding a variable
a = 1
a = 3
Rebinding doesn’t change the state of an object at all, the value is still in the same memory location, but it’s label (variable) now points to another memory location, so immutability is preserved. Rebinding is not available in Erlang, but while it is in Elixir this is not braking any constraint imposed by the Erlang VM, thanks to its implementation.
The reasons behind this choice are well explained by Josè Valim in this gist and I’ll turn back on them while writing about macros.
It might be important to note that there are two cases in which Elixir doesn’t allow rebinding:
- during pattern matching
- variables with the pin
In the first case, displayed in the following example, the match will fail, it’s not possible to match in the same instruction the variable
# Failed rebinding in pattern matching
[a, b, a] = [1, 2, 3]
# ** (MatchError) no match of right hand side value: [1, 2, 3]
In the second case, the pin
^ operator, means that we want to treat the variable as a literal value, disallowing a rebinding. You can see an example in the following code:
# Pin operator a = 1
a = 3^
a = 5
# ** (MatchError) no match of right hand side value: 5
That’s all for today.