What is it?
Elixir is an impure functional programming language created by José Valim as an alternative to Erlang. The language is very powerful and featured with some solid frameworks. There are also machine learning (NX) and embedded systems (Nerves) frameworks. Elixir also has interoperability into Elrang, and can directly call Erlang functions. Its syntax is similar to Ruby, but the language itself differs quite a bit.
What is Similar to Other Languages?
On the surface, Elixir looks pretty familiar. Variables can be declared and redeclared. There are modules and functions. The usual conditionals exist such as if
and for
. Elixir also comes with a standard set of tools and frameworks one would expect to find in a modern language. It has a very capable REPL. ExUnit is a straightforward unit testing suite. Phoenix (web) and Ecto (data mapper) comprise a solid MVC framework. There is also FFI-like interoperability with compiled code. Working in Elixir feels familiar to a developer that has worked in other popular languages. With a few exceptions.
What is Different than Other Languages?
As mentioned, Elixir is an impure functional language. That means that while it has some imperative qualities (such as if
constructs, or being able to reassign variables), it still has a funcitonal base. Some of the typical functional elements exist, like tail call recursion and the usual data processing funtions (map, reduce, foldr, etc). The biggest difference is immutability. This means that no variables can be mutated, which makes for some interesting differences.
Instead of this:
foo = 1
if foo == 1
foo = 2
end
You do this:
foo = 1
foo = if foo == 1 do
2
end
Wait a minute, that looks kind of ugly, actually that looks really ugly. That’s because once foo is assigned to 1, you cannot change that variable. I said that you can reassign variables, and one of the biggest things to trip beginners up is reassignment (note: binding if to a variable is not common or recommended in real world Elixir, the block would likely be its own function). The official Elixir documentation goes into great detail about this, but the gist is, when you write foo = 1
you are not necessarily creating a foo variable then setting the value to 1, you are making an assertion that foo is 1. If you want to change foo you can reassign it: foo = foo + 1
means add foo + 1
then assign a new variable also named foo
and give it the value of 2. You may wonder why this matters but it does, because it affects scope greatly. Variables are not mutated, they are shadowed. And this makes Elixir different than Erlang which does not have shadowing.
It took me a while to really wrap my head around this but when I did the rest of the language clicked. All of the other differences were not that complicated. A few other differences of the language:
- There are no classes, only modules
-
There is also no method chaining, as there are no mutable objects, instead functions may be piped into other functions, this is similar to pipes in Bash,
redirect(put_flash(conn, "Success!"), to: "/")
can be instead written toconn |> put_flash("Success!") |> redirect(to: "/")
, this give the same flow as method chaining but makes the logic easier to reason about -
Atoms (constants where the value is the name of the atom) are really important, in fact even module names are atoms, i.e.
Ecto.Query
-
In addition to lists and maps, Elixir also has keyword lists (and a matching
Keyword
module) which are lists of two value tuples, as well as some sugar to make[{foo, bar}, {faz, baz}]
into[foo: bar, faz: baz]
, also duplicate keys are allowed -
do
blocks are actually values of keyword lists, and are usually evaluated conditionally due to macros, so an if conditional can be written asif(foo, [do: bar])
, speaking of macros: -
Elixir has hygenic macros, when you write
if(foo, [do: bar])
this expands to{:if, [context: Elixir, import: Kernel], [{:foo, [], Elixir}, [do: {:bar, [], Elixir}]]}
, this is because Elixir under the hood has an intermediary LISP like language
What is REALLY Different? OTP:
Okay so I lied when I said nothing is mutable, because there are some features of the language that do provide mutable structures. Mainly two in memory databases and being able to recursively loop processes. In fact, there are a lot of these extras scattered about the language. This is what makes Erlang the language it is, and by extension, Elixir. These exensions, along with the BEAM virtual machine and the language runtime itself comprise the Open Telecom Platform, or OTP. You will see Erlang versions written as Erlang/OTP, because they are joined at the hip. One way of looking at it is OTP includes an enormous standard library on top of the runtime. Honestly a language is a language is a language, we all have our gripes about them but this extension is what really makes Erlang, and thus Elixir, shine. Some of the main features of OTP include:
- ETS/DETS - an in memory key-value store, and in the case of DETS, also disk backed
- Mnesia - a RDBMS-like in memory (also disk backed) database, this is what powers RabbitMQ
- Processes - Erlang uses the actor model of concurrency, and thanks to its immutability, green threads are highly performant with negligible overhead (millions of concurrent threads are possible)
- GenServer - Erlang has abstracted processes into something called a Generic Server, or GenServer, which is essentially a tail recursive process that passes its state to each call, these servers are incredibly powerful and can receive synchronous or asynchronous calls
- Hot code reloading - due to processes being first class citizens, code can be reloaded live while the application is running, this is not dissimilar to modern container platforms, where a new process is spun up for each existing process and all old processes exit gracefully when they finish serving requests
- Nodes - multiple BEAM instances can be linked together and state shared, this all happens seamlessly under the hood, if you ever saw an “Erlang cookie”, this is exactly what uses it, OTP essentially provides clustering for free
- Dialyzer - a static code analysis tool, this compensates for Erlang and Elixir being dynamic languages
- Everything else - I’ve found SSH modules, parsing libraries, and all sorts of other miscellaneous functions that would normally be third party libraries, the benefit with OTP is that these are battle tested libraries dating back decades and are not at risk of atrophy or abandonment
Learning to leverage these tools will vastly increase productivity.
Summary
Elixir is a fantastic language with a lot of useful features. It takes a while to absorb it all in, along with having to reprogram your brain to deal with immutability, but it is an incredibly productive language. For example, I wrote this blog in 12 hours, and implemented a from scratch RSS reader in an hour. It’s not just comfort level, the langauge really stays out of the way, and is very expressive. I hope that you will consider giving it a try, and while it isn’t a top Tiobe language, it has been picking up steam, mainly for web services.