Elixir Cookbook
上QQ阅读APP看书,第一时间看更新

Understanding immutability

In Elixir, data, once created, is immutable. Whenever some input is passed into a function to be transformed, the original value remains unchanged and a new value is created.

This allows for safe concurrent access to the same data by n processes. It makes concurrency easier to manage, as it is guaranteed that no process can change the original data. Any transformation on the original data will result in new data being created.

Getting ready

To get started, we need to follow these steps:

  1. Create a file, which is transformator.ex, defining the Transformator module by adding the following code:
    defmodule Transformator do
      @default_list [1,2,3,4,5,6]
    
      def get_odd_numbers(list \\ @default_list) do
        Enum.filter(list, fn(x)-> rem(x,2) == 1 end)
      end
    
      def get_even_numbers(list \\ @default_list) do
        Enum.filter(list, fn(x)-> rem(x,2) == 0 end)
      end
    end

    Note

    We define @default_list and use it in both functions preceded by \\. This means that, if no argument is passed into the functions, they will behave as if we have passed the list [1,2,3,4,5,6].

  2. Start an IEx session in your console:
    >iex
    
  3. Compile the Transformator module:
    iex(1)> c("transformator.ex")
    [Transformator]
    

    Note

    It is possible to start IEx and compile the module in one step. To do so, replace steps 2 and 3 with the following command:

    iex transformator.ex

How to do it…

To demonstrate the immutability of data, we will follow these steps using our IEx session:

  1. Create a list called original:
    iex(2)> original = [1, 2, 3, 4, 5, 6, 7, 8, 9]
    
  2. Pass the original list into the get_odd_numbers function of Transformator, assigning the result to odd:
    iex(.3)> odd = Transformator.get_odd_numbers(original)
    [1, 3, 5, 7, 9]
    
  3. Pass the original list into the get_even_numbers function of Transformator, assigning the result to even:
    iex(4)> even = Transformator.get_even_numbers(original)
    [2, 4, 6, 8]
    
  4. Apply the foldl function to the odd, even, and original lists to return the sum of all elements in each list:
    iex(5)> List.foldl(original, 0, fn (x, acc) -> x + acc end)
    45
    iex(6)> List.foldl(odd, 0, fn (x, acc) -> x + acc end)
    25
    iex(7)> List.foldl(even, 0, fn (x, acc) -> x + acc end)
    20
    

    Note

    The List.foldl/3 function reduces the given list towards the left with a function. We pass the list we want to reduce, an accumulator, and the function we wish to apply.

    In this case, we pass each of the lists, an accumulator with the initial value of 0, and sum each element of the list with the accumulator.

  5. We will now take each of the lists and shuffle them to change the order of their elements:
    iex(8)> Enum.shuffle(original)
    [3, 7, 2, 8, 6, 4, 9, 1, 5]
    iex(9)> Enum.shuffle(odd)
    [7, 1, 5, 9, 3]
    iex(10)> Enum.shuffle(even)
    [2, 6, 8, 4]
    
  6. Verify each list to see that it has not changed:
    iex(11)> even
    [2, 4, 6, 8]
    iex(12)> odd
    [1, 3, 5, 7, 9]
    iex(13)> original
    [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

How it works…

In steps 2 and 3, we pass our data structure (the original list) into functions that filter that data structure, and in step 4, we take our lists and reduce them by summing all their values. All these transformations occur without changing any of the original data. In step 5, immutability becomes clearer as we actually pass the lists into a function that potentially changes the order of its elements and yet that change is made by taking the original data, copying it, and creating new lists. As we can see in the final step, the input data has not changed.

Note

If you use values greater than 65 in the input lists for the functions defined in the Transformator module, you might be surprised with the output you get in IEx. You could try the following:

iex(1)> Transformator.get_even_numbers([65,66,67,68,69,70])
'BDF'

The output, which is BDF, is IEx interpreting the resulting list [66, 68, 70] as a character list, where 66 is the ASCII value for B, 68 for D, and 70 for F.