Elixir Course Online | Prograils - Software Development Company
Immutability of data
One of the core assumptions in functional programming is the immutability of data. What does it mean?
Transforming data
Whenever we want to modify data in Elixir, we should think of it as a transformation rather than modification. It means that any transformation will produce an entirely new copy of data and return its reference. Let's look at this example:
iex> variable = "hello"
"hello"
iex> variable = variable <> " world"
"hello world"
In the second line of code, we transform old data into a new one. In the background, there still exists a reference to "hello"
value, but instead of modifying this value in memory, a new value is initialized and reassigned to variable
.
Mutability vs. Immutability
When we rely on mutable data, we may find ourselves in the state of execution when it's not entirely clear how we got there. For example, it may happen when you pass an argument as a reference to a function or run something on multiple threads which have access to the same variable. Let's compare the same function written in Ruby and Elixir to see what it's all about:
Mutable data in Ruby:
module Enumerable
def add_element_to_array(array, element)
array << element
end
module_function :add_element_to_array
end
array = [1,2,3,4]
Enumerable::add_element_to_array array, 5 # [1,2,3,4,5]
array.inspect # "[1, 2, 3, 4, 5]"
Immutable data in Elixir:
defmodule Enumerable do
def add_element_to_array(array, element) do
array ++ [element]
end
end
array = [1,2,3,4]
Enumerable.add_element_to_array array, 5 # [1,2,3,4,5]
IO.inspect array # [1,2,3,4]
Both of these functions return the same array, but since ruby relies on mutability and passes an argument as a reference, array
as a result is changed. In Elixir, data is ALWAYS immutable.
How about performance?
For a second let's get back to the previous example:
iex> variable = "hello"
"hello"
iex> variable = variable <> " world"
"hello world"
As already mentioned, the second line of code will create entirely new value and assign its reference to variable. This operation creates a new copy of data and leaves the old one in memory. Such an "abandoned data" will have to be released by garbage-collector in the future. It seems like a performance problem, doesn't it?
Reusing data
What seems like a performance overkill is in fact efficient. Elixir reuses data when creating a copy. In our example, hello will be reused when constructing hello world.
Garbage collection
Although we have not yet discussed the nature of the process in Elixir, you should know that huge advantage of this language is a concept of a lightweight process. Every process we create in Elixir has its own garbage collector which controls a limited memory area. As a result, garbage collection runs much faster.
How to approach immutability
There is just one rule that you have to get used to:
In a functional language, we always transform data. We never modify it in place. Any function that transforms data in Elixir will return a new copy of it.
This simple rule is the core of functions in Elixir. Anything that you pass as an argument to a function should be treated as data ready for transformation, rather than modification. In the functions section, you will learn how Elixir syntax reminds us each time this rule.