Looping in Elixir
Elixir doesn’t have looping constructs like a
for loop or a
while loop. Instead, looping is achieved through recursion. If you’re not familiar with recursion, recursion is when a function keeps calling itself until some condition is met.
Fire up an
iex session and type in the following:
[head | tail] = [1, 2, 3] head # 1 tail # [2, 3]
Here we’ve pattern matched against the list using the cons operator (the
head is the first item in the list and
tail is a list of all the other items. We could continue and break down
tail until it is an empty list. For example:
[head | tail] = [1, 2, 3] head # 1 tail # [2, 3] [head | tail] = tail head # 2 tail #  [head | tail] = tail head # 3 tail # 
We can write a recursive function that uses the technique above to go through every item in the list and print out each number:
defmodule MyEnum do def each([head | tail]) do IO.puts(head) each(tail) end def each(), do: nil end MyEnum.each([1, 2, 3])
In Elixir, functions can have multiple clauses. If a function has multiple clauses, Elixir will try each one from top to bottom in the module until one of them matches.
In the example above, we’ve defined two clauses for the
each function. The first function clause will match whenever the first argument is a non-empty list, resulting in it getting called. This function will keep calling itself with
tail, which contains one less item on each successive call, until the list is empty, at which point the second clause will match and get called and the recursion will terminate.
Enum module has a function called
each/2 that makes the above more generic and reusable.
Enum.each/2 goes through every item in a list using recursion behind the scenes and invokes an anonymous function with each item as its argument. This is similar to
Enum.each [1, 2, 3], fn(x) -> IO.puts(x) end # 1 # 2 # 3
Let’s write our own implementation of this to learn more about looping through recursion in Elixir.
defmodule MyEnum do def each([head | tail], func) do func.(head) each(tail, func) end def each(, _func), do: nil end MyEnum.each [1, 2, 3], fn(x) -> IO.puts(x) end
Our implementation is very similar to our previous example. The only difference is that we’ve replaced
func.(head) to make it more generic and reusable.
Pretty neat, huh? Now let’s write our own implementation of
If you aren’t familiar with reduce, it is a way to reduce a list to a single value. Summing an array of numbers is one example. In Elixir, we can use
Enum.reduce/3. For example:
sum = Enum.reduce [1, 2, 3], 0, fn(x, total) -> x + total end sum # 6
This works similar to
defmodule MyEnum do def reduce([head | tail], total, func) do new_total = func.(head, total) reduce(tail, new_total, func) end def reduce(, total, _func), do: total end sum = MyEnum.reduce [1, 2, 3], 0, fn(x, total) -> x + total end
This example is pretty similar to the
each implementation that we did previously. Here, we invoke
func with each item and the accumulating total, which starts off at 0. This returns
new_total, which is passed into each recursive call. When
reduce is eventually called with an empty list, the second clause of
reduce will match, at which point the recursion still stop and
total will be returned.
Let’s move on and write our own implementation of
If you haven’t used a map function before, it is used to map one list to another list. For example, we can double a list of numbers by calling
doubled_numbers = Enum.map [1, 2, 3], fn(x) -> x * 2 end doubled_numbers # [2, 4, 6]
As we’ve learned, we can use the cons operator to destructure a list into two parts, the head, which is the first item, and the tail, which is another list of all remaining items.
[head | tail] = [1, 2, 3] head # 1 tail # [2, 3]
We can also use the cons operator to reconstruct a new list from a single value and another list:
[1 | [2, 3]] # [1, 2, 3] [1 | [2 | [3, 4]]] # [1, 2, 3, 4] [1 | ] #  [1 | [2, 3]] == [1, 2, 3] # true
With this in mind, we can rebuild
defmodule MyEnum do def map([head | tail], func) do [func.(head) | map(tail, func)] end def map(, _func), do:  end MyEnum.map [1, 2, 3], fn(x) -> x * 2 end
In this example, we are taking the list
[1, 2, 3] and mapping it to
[2 | [4 | [6 | ]]], which is the same as
[2, 4, 6].
Let’s move on to our last Enum function and rebuild
The filter function is used to take one list and filter it down by some condition which is represented as a function. The function is called for every item in the list. If the function returns a truthy value, that item is kept in the list. If the function returns a falsy value, that item doesn’t make it into the new list. Here is an example using the built-in
filtered_items = Enum.filter [1, 2, 3, 4, 5], fn(x) -> x >= 3 end filtered_items # [3, 4, 5]
Here is our own implementation:
defmodule MyEnum do def filter([head | tail], func) do result = func.(head) if result do [head | filter(tail, func)] else filter(tail, func) end end def filter(, _func), do:  end MyEnum.filter [1, 2, 3, 4, 5], fn(x) -> x >= 3 end
The implementation is similar to our map function, but with an extra
if statement. If
result is truthy, we use the cons operator to include it in the list and call
filter again with the tail. If
result is falsy, we just call filter again and disregard
head. We keep doing this until the list is empty, at which point the second
filter clause will get executed and end the recursion.
I found reimplementing
Enum.filter/2 a great exercise to learn how to loop in Elixir through recursion. I’m sure these functions in the
Enum module do more than our implementations, so you should still use those, but nevertheless it is a fun exercise to learn Elixir.
Disclaimer: Any viewpoints and opinions expressed in this article are those of David Tang and do not reflect those of my employer or any of my colleagues.