Introduction
Elixir is a purely functional programming language that runs on the Erlang VM. That means our code is transpiled to Erlang and then executed by that virtual machine.
To use a familiar analogy, it is similar to what happens with Kotlin and Java. Kotlin code is transpiled to Java, compiled into .class files, and then executed by the virtual machine.
Interesting traits
Because Elixir is a purely functional language, you will not find classes. Instead, you work with modules that contain functions. Those functions receive parameters and can be chained together so that the output of one becomes the input of the next.
Matching operator and pattern matching
This is one of the most important features in the language because it lets us do much more than assign values:
x = 1
It also lets us destructure values:
{a, b, c} = {:hello, "world", 42}
From that expression we can infer the following bindings: a -> :hello, b -> "world", c -> 42.
The same idea can be used with arrays:
{a, b, c} = [:hello, "world", 42]
A very common case is a more advanced destructuring form:
[head | tail] = [1, 2, 3]
In that case, head -> 1 while tail -> [2, 3].
Using the same syntax we can also prepend content to a list:
list = [1, 2, 3]
new_list = [0 | list]
The result would be [0, 1, 2, 3].
Another interesting case is the _ operator. It is reserved for destructuring and can never be read:
[head | _] = [1, 2, 3]
Here head -> 1, while _ cannot be read. It is useful when the rest of the data is irrelevant to the code.
Pipe operator |>
This operator works as a composition mechanism. For example, if we wanted to define a chain of calls in a classic style:
defmodule Socracan do
def reversed_digits(number) do
Integer.digits(Enum.reverse(number))
end
end
We receive a number, reverse it, and split it into digits. Even though the code fits on one line, it is not very readable. Using |> makes the flow much clearer:
defmodule Socracan do
def reversed_digits(number) do
number
|> Integer.digits()
|> Enum.reverse()
end
end
In this version, number is passed into Integer.digits, and then that result is passed into Enum.reverse.
Another thing I found curious in Elixir is that, besides regular unit tests:
defmodule ExampleTest do
use ExUnit.Case
doctest Example
test "greets the world" do
assert Example.hello() == :world
end
end
We also have documentation tests, or doctests. These tests define a simple input/output example directly above the method definition as part of the documentation. That gives us living documentation that also verifies itself.
defmodule Example do
@moduledoc """
Documentation for Example.
"""
@doc """
Hello world.
## Examples
iex> Example.hello()
:world
"""
def hello do
:world
end
end
If you want to get started with Elixir and do not know where to begin, there is a very good repository with koans to practice the language. It guides you from the basics to more advanced cases using failing tests that you complete by replacing the three underscores ___.