The Elixir Language: a Features Summary
Introduction
Elixir is a general-purpose functional programming language with built-in support for concurrency and fault tolerance that shares many abstractions, libraries and tools with Erlang, a language created by Ericsson and used initially by telecom companies. It runs on BEAM, the Erlang Virtual Machine.
The first stable version was released on September 2014.
You may have heard of Elixir as being the programming language used by Phoenix, a rising web framework very loved by developers as demonstrated by many surveys such as the 2022 Stack Overflow Developer Survey, but what are the main features of this language ? and what is it used for ?
Elixir language features
Atoms: similar to symbols in Javascript. They are frequently used in Elixir with pattern matching as a way to handle function results (with the
:ok
and:error
values for example).Tuples: data structures that can be accessed by index (fast operation), for example
{:ok, "hello"}
. They are immutable by default as operations on them return new tuples.Lists defined with square brackets are actually linked lists, for example:
[1, 2, 3]
. The++
and--
operators are used to concatenate and subtract lists.Keyword lists are defined like lists, but are primarily used to provide options to functions. The keys must be atoms and can be duplicate. When a keywords list is the last argument of a function, the brackets become optional:
String.split("1 2 3", " ", trim: true)
Maps are key-value stores without any restrictions on keys. Example:
map = %{:x => 1, 5 => :y}
Structs are special maps that provide type safety and default values:
%User{name: "John", age: 30}
Pattern matching: can be used for destructuring complex data types. Works with tuples and lists. For example, after evaluating
{:ok, result} = {:ok, 13}
, theresult
variable will hold the value13
. You can use the pin operator to prevent a left side variable from being reassigned during pattern matching, likex
in this example:[^x, 2, 3] = [1, 2, 3]
.The
case
statement is similar toswitch
in other languages. You can pattern match values and execute the instructions of the matched pattern in the same control flow structure:The
cond
statement is similar toif / else if
in other languages. If atrue
condition is not added and none of the conditions are matched, an error is raised:Elixir also provides
if
,unless
andelse
to check only one condition.You can use guards in function definitions and
case
statements to define a condition for the execution of the function or thecase
pattern.Enumerables and ranges are provided by Elixir along with functions to manipulate them, for example:
Enum.map(1..3, fn x -> x * 2 end)
. Operations performed on enumerables using theEnum
module are eager.The pipe operator
|>
in Elixir is similar to the Unix|
operator. It is used to simplify function calls by taking the output from the expression on its left side and passing it as the first argument to the function call on its right side:1..100_000 |> Enum.map(&(&1 * 3)) |> Enum.filter(odd?) |> Enum.sum()
Streams are special enumerables that are lazy and composable:
Protocols provide mechanisms to allow implementing additional behaviors for data types. They are similar to extension functions in other languages. They are defined with one protocol definition and multiple implementations for each data type.
Sigils are another way for developers to provide custom extensions to the Elixir language. They are defined with the tilde (~) character. Many predefined sigils are also provided such as
Date
:Messaging in Elixir is based loosely on the Actor model. The Elixir processes are the main actors that communicate via message passing by using the
send
andreceive
constructs.
Elixir language uses
Distributed and scalable systems
Elixir does allow horizontal scaling with the lightweight threads, aka Elixir processes, that can run in the same machine or other machines of the same network.
Theses processes can communicate easily with each other, even if they are running on different machines on the same network.
Elixir will also try to maximize the CPU cores usage on the same machine, and thus offer better vertical scaling.
These properties makes it very convenient to build highly scalable distributed systems that require less resources compared to other languages.
High-availability applications
By default, Elixir processes are isolated. If one process fails, it will not affect other processes unless they are explicitly linked. This is why the most common way to handle failures in Elixir is by adopting the "fail fast" approach. Elixir provides mechanisms, called supervisors, that will instruct the system what to do when a process has failed. The try, catch, and rescue statements are less frequently used.
This provides the foundation to build fault tolerant systems that are highly available.
Web development
The Phoenix Framework allows building feature-rich and production ready web apps based on the Elixir language with less boilerplate code. One of the main advantages of using this framework include productivity thanks to the project generator and tasks tools.
It's follows an Model-View-Controller (MVC) architecture and comes with real time capabilities out-of-the-box.
You can build with Phoenix LiveView feature rich web apps with server-rendered views based on a declarative model.
Conclusion
The Elixir language provides many constructs that makes it easy to build fault-tolerant apps that scale.
It also allows building maintainable complex distributed systems with high productivity compared to other languages.
If you are interested in learning more about Elixir, you can follow the getting started guide.
You can also follow this installation guide if you want to try out the Phoenix Framework.
Soufiane Sakhi is an AWS Certified Solutions Architect – Associate and a professional full stack developer.