Welcome back! Now that you've explored higher-order functions, let's shift our focus to immutability — a fundamental concept in Elixir and functional programming. Mastering immutability will help you write reliable, predictable, and maintainable code.
Immutability in Elixir means that once you create a data structure like a list or a map, it cannot be changed. If you need to "change" something, Elixir doesn't modify the original structure but instead creates a new one. Think of it like writing on a piece of paper: if you want to change a word, you don't erase it — you write the new version on a fresh sheet.
In this unit, you will learn about immutability in Elixir, which means that once a data structure is created, it cannot be changed. Instead, any operation that modifies the data produces a new data structure. This might sound restrictive, but it actually brings many benefits.
Let's look at an example:
Elixir1list = [1, 2, 3] 2new_list = List.delete_at(list, 1) 3IO.inspect(list, label: "Original list") # Output: Original list: [1, 2, 3] 4IO.inspect(new_list, label: "New list") # Output: New list: [1, 3]
In this code snippet, the original list [1, 2, 3]
remains unchanged even after we delete an element to create new_list
. Note, that the delete_at
function is a part of the List
module in Elixir which is used to delete an element at a specific index. In this case, the element at index 1
is deleted - notice that the index starts from 0
, so the element at index 1
is the second element in the list 2
.
Note, that we used an additional label
option in the IO.inspect
function to print the label along with the output. This is a useful feature to help you understand the output better.
Let's also see how this works with maps:
Elixir1map = %{name: "Elixir", age: 10} 2new_map = Map.put(map, :age, 11) 3IO.inspect(map, label: "Original map") # Output: Original map: %{name: "Elixir", age: 10} 4IO.inspect(new_map, label: "New map") # Output: New map: %{name: "Elixir", age: 11}
Here, the original map %{name: "Elixir", age: 10}
is not altered even after we update the :age
key. The Map.put
function is used to update the value of a key in a map. In this case, the value of the :age
key is updated from 10
to 11
.
Understanding immutability is critical for several reasons:
-
Predictability: Since data structures don't change, it's easier to understand how your program behaves. You can be sure that a value won't be altered unexpectedly elsewhere in the code.
-
Concurrency: Immutability makes it safer to write concurrent programs. With immutable data, there's no risk of one part of your program changing a data structure while another part is reading it, which can lead to bugs.
-
Debugging: Debugging is simpler when data doesn't change unexpectedly. If you can trust your data to remain constant, it's easier to locate the source of an issue.
-
Optimized Copying: When an operation requires a change to a data structure, Elixir creates a new version of that structure rather than modifying the existing one. Only the parts of the data structure that are affected by the change are copied, while the parts that remain unchanged can be shared between the original and new versions. This efficient copying mechanism ensures that memory usage is optimized, making immutability practical and performant in Elixir.
By understanding and embracing immutability, you'll be equipped to write more robust and reliable programs. Ready to see immutability in action? Let's dive into the practice section and solidify these concepts together.