Elixir is a dynamic, functional laguage for building scalable and maintainable applications.
This is a short description of Elixir that we can get from it’s documentation. Although small, this description provides some information on how this language works.
For example, being a functional language implies that it doesn’t have structures like for, foreach, or while. At least the way we know it from imperative languages like C, C#, Java and so on.
It’s normal to see questions arise from the statements above. The main question I had when I started learning Elixir, which might be the one going through your mind right now, is how can I handle loops? How about working with lists? These are pertinent questions and in this post we’ll answer them.
Why doesn’t it have any loop structure?
Erlang, Elixir and a lot of functional programming languages don’t have loops simply because of their nature. In functional languages the state is immutable, so loops like the following cannot be done.
for (i = 0; i < sizeof(array); i++) {
write(i);
}
Note that the variable i
is mutating at every loop and this is not possible in Elixir.
NOTE: In Erlang we can’t even reassign a variable (strange at first, but makes sense later).
Loops through recursion
Basically, common repetition structures are not needed in this type of language. Functional languages are made up of functions, so we can do everything using functions, including loops.
In Elixir, we can handle loops using recursion and pattern matching. We will need functions that will represent the basic parts of a loop, which are:
- Initialization;
- The body of the loop (operations that are performed when a condition is evaluated as true);
- The end of the loop.
In most cases, each piece of the loop will be covered by a function. Approaching this through a practical example, we can implement a simple algorithm to add items in a list that is made up of real numbers.
The base case
The idea of our algorithm is simple: given a list [1,2,3], it should return the value 6 which is the sum of it’s contents. We can start trivially with the following code.
defmodule MathRecursion do
def sum(list) do
:not_implemented_yet
end
end
The code above is really simple and when calling sum
only an atom is returned.
The body
To function as the body of the loop, we can create a private function to support our public function, as shown below.
defmodule MathRecursion do
def sum(list) do
sum_recursively(list)
end
defp sum_recursively([head|tail]) do
head + sum_recursively(tail)
end
end
The sum()
function is just our facade, all recursive work is done by sum_recursively()
which takes the list and separates the head
from the tail
and does a sum between the head and the recursion call of the tail. To better understand how it works, let’s assume that the list [1, 2, 3], the execution of sum_recursively
will be as shown.
step 1> sum_recursively([1, 2, 3]) # head = 1 // tail = [2,3]
step 2> 1 + sum_recursively([2, 3]) # head = 2 // tail = [3]
step 3> 1 + 2 + sum_recursively([3]) # head = 3 // tail = []
step 4> 1 + 2 + 3 + sum_recursively([]) # ???
Everything will work as expected until the fourth step. In the fouth step we have our desired output, but what about how sum_recursively
will deal with an empty list? In this particular case we’ll get an exception, because there is no pattern to handle an empty list, Elixir will try to match the following pattern [head|tail] = []
and it will fail.
The end of loop
To summarise our problem: our loop is infinite. In some cases it will result in an exception and in other cases it will run forever, it really depends. Let’s focus on our case and add the end of our loop by adding a clause to the empty list.
defmodule MathRecursion do
def sum(list) do
sum_recursively(list)
end
defp sum_recursively([head|tail]) do
head + sum_recursively(tail)
end
defp sum_recursively([]) do
0
end
end
Now our code is working. When the list is empty the function sum_recursively
will return 0
and the fourth step of our algorithm will be performed as shown below.
step 4> 1 + 2 + 3 + sum_recursively([]) ==> 1 + 2 + 3 + 0
Result: 6
After all that, sum()
returns the result.
Running it
Add the complete code in a file and save it with .ex
extension.
After that open the Elixir prompt in the same folder, compile and run this file. In my case I’ve named it math_recursion.ex
and in my terminal I ran the following commands:
iex(1)> c("math_recursion.ex")
[MathRecursion]
iex(2)> MathRecursion.sum([])
0
iex(3)> MathRecursion.sum([1,2,3])
6
iex(4)> MathRecursion.sum([10, 5, 35])
50
Summary
In this post we saw how to handle loops in Elixir. The concepts presented here are on a basic level and I intend on making more posts in the future talking about accumulators, tail-calls and a lot more.
For now it’s enough to understand the base pattern that you should keep in mind when trying to solve problems involving loops and related things.
That’s all, see ya!
Important links
- Install Elixir – https://elixir-lang.org/install.html
- Elixir interactive mode – https://elixir-lang.org/getting-started/introduction.html#interactive-mode
We want to work with you. Check out our "What We Do" section!