It would be easy to assume that this approach to programming is inefficient. After all, you have to create a new copy of data whenever you update it, and that’s going to leave lots of old values around to be garbage-collected. Let’s look at these in turn.
Although common sense might dictate that all this copying of data is inefficient, the reverse is true. Because Elixir knows that existing data is immutable, it can reuse it, in part or as a whole, when building new structures.
Consider the following code. (It uses a new operator, [ head | tail ], which builds a new list with head as its first element and tail as the rest. We’ll spend a whole chapter on this when we talk about lists and recursion. For now, just trust.)
| iex> list1 = [ 3, 2, 1 ] |
| [3, 2, 1] |
| iex> list2 = [ 4 | list1 ] |
| [4, 3, 2, 1] |
In most languages, list2 would be built by creating a new list containing the values 4, 3, 2, and 1. The three values in list1 would be copied into the tail of list2. And that would be necessary because list1 would be mutable.
But Elixir knows list1 will never change, so it simply constructs a new list with a head of 4 and a tail of list1.
The other performance issue with a transformational language is that you quite often end up leaving old values unused when you create new values from them. This leaves a bunch of things using up memory on the heap, so garbage collection has to reclaim them.
Most modern languages have a garbage collector, and developers have grown to be suspicious of them—they can impact performance quite badly.
But the cool thing about Elixir is that you write your code using lots and lots of processes, and each process has its own heap. The data in your application is divvied up between these processes, so each individual heap is much, much smaller than would have been the case if all the data had been in a single heap. As a result, garbage collection runs faster. If a process terminates before its heap becomes full, all its data is discarded—no garbage collection is required.