Erlang has a reputation of featuring a kind of syntax that begs for sugar so desperately, that it led to importing goodness from Ruby, and eventually inventing the Elixir language. However, for someone coming from ML languages, like OCaml or Haskell, it may even feel familiar.
In this article I peek into what this language is about, what’s the buzz around it, and how it tries to deceive the unsuspecting developers sometimes.
From the Lab into your Palm
The research for a programming language particularly suitable for implementing telecommunication solutions is started some 40 years ago at the Ericsson Computer Science Laboratory. Prolog — the logic programming language — served as direct predecessor for it, but later turned out to be too slow for implementing core features, then those parts were eventually rewritten in C. During development, the team was focusing on inventing a minimal language suitable for the needs of telecommunication, that is also capable of running massively in parallel with high performance while offering maximum uptime. These key requirements determined the features of the language and the related ecosystem, and make the language applicable of writing servers performing at near real-time speed using a set of Erlang libraries called the Open Telecom Platform (OTP).
This performance attracted some companies targeting to implement new instant messaging solutions for mobile devices. One of those companies was WhatsApp, and their success also drove some attention towards Erlang and its platform.
What Kind of Animal is Erlang?
The requirements to make Erlang suitable for implementing telecom solutions also determined what features the language will have. Erlang is a functional programming language. It runs in a virtual machine to which it needs to be compiled, but it’s also capable of running interpreted in its REPL (read-eval-print loop) called erl. Erlang’s virtual machine is called BEAM (Bogdan/Björn’s Erlang Abstract Machine), and as such is similar to that of Java or .NET. BEAM comes with a runtime and library called OTP, already mentioned. Together they make possible to write applications that are:
- distributed — can be run on several machines in parallel while still working as a whole
- fault-tolerant — gracefully embraces errors, and able to restart parts of itself as necessary
- soft real-time — its response time is almost as low as that of those programs used to drive pacemakers or rockets
- highly available — the system remains always online mostly because of its fault-tolerance and also because it’s
- hot swapping — meaning it’s possible to reload, replace or restart parts of the application without stopping the system or the application itself
The language implements the following functional programming concepts (for an extent):
- higher-order functions — functions can accept other functions as arguments
- first-class functions — functions can be defined anonymously
- immutability — the value of variables cannot change after the first assignment
- pattern matching — the values stored in compound data types can be extracted in parameters, guards and cases; aka destructuring
- eager evaluation — the expressions are evaluated as soon as they are assigned to a variable as opposed to lazy evaluation where they only evaluated the time they are actually used
- single assignment — variables cannot be reassigned to new values after they were initialized first
- list comprehension — generating lists from existing lists
It also features dynamic typing, meaning the type of the variables are not forced or assured at compile time, but rather at runtime (although static type checking can be used with the optional tool called Dialyzer).
Erlang applications usually run as masses of lightweight processes, where Erlang processes are ‘much lighter than an operating system thread’.
The memory allocated for Erlang variables at runtime are freed dynamically and automatically by a Garbage Collector running separately.
What Kind of Animal is not?
For performance and other practical reasons Erlang had to make some sacrifices that distinguishes it from other FP languages. Erlang is not a purely functional language, meaning it’s not free from side effects, in fact it heavily relies on them whenever it’s necessary out of practical reasons. It’s also not statically typed, although types can be checked at compile time with an optional tool. The Erlang runtime —despite being soft real-time — is not applicable for implementing high performance applications where performance is also affected by constant-factors, for example in image and signal processing and large volume data sorting, and where low-level access is a must, like writing operating system drivers.
Say Hello to Erlang
Here we define a module (FP encapsulation container) that exports only one function which has nothing to do with division by zero, but accepts no arguments (hence the 0), and prints a string on the standard output. The number of percentage signs can escalate quickly in Erlang, if we want to document our code (here I was reserved using only two). Also new lines are not tolerated to be marked in strings as usual
~n is used instead, which adds up to the weird syntax of the language.
We can declare a value like this in Erlang:
Value = 5, but this won’t work on the module level, only within a function. That’s confusing, because we are allowed to use them freely in the REPL. However it’s totally valid to define a function which just returns the value we’d assign, and later can be called at any time:
days() -> [ mon, tue, wed, thu, fri, sat, sun ].
months() -> [ jan, feb, mar, apr, may, jun, jul, aug, sep, oct, nov, dec ].
These lists contain only atoms, which are self-contained values similar to enums, but without having to worry about their value.
Note that instructions end with period in Erlang, but that does not mean comma and semicolon will remain jobless.
Let’s say we want to write some calendar functions because we could not find the already implemented ones in Erlang’s documentation, or because we’re not happy with the results it gave to us (calendar:date_to_gregorian_days/3 returns the days between year 0 and the date provided, however there’s no such thing as year 0 in Gregorian calendar).
We could do something like this:
Now let me mention some of the Erlang peculiarities we can catch red handed in this snippet.
- Line 1: for immediate execution use
- Lines 29–33: guards with
- 36: logical equality with
- 37: we’re not over yet with
;— the Erlang way of writing alternative implementations, here
- 39: if I had an else…
- 42, 47: polymorphic functions — we define separate logic for February
- 53: list comprehension generates a list for the days of the months
- 55: pattern matching for getting head and tail of a list
- 58: anonymous functions are
- 58: logical inequality with even weirder
- 70–76: function overloading
- 75: to confuse the unsuspecting:
- 81: tuples with curly braces
- 81,82: doing things consecutively with
A honorable mention not included in the code, but can cause a lot of headaches:
Y = 1.
M = jan.
Y =:= 1 and M =:= jan.
this will give you:
* 1: syntax error before: '=:='
which is not very helpful, but this happens with error messages in Erlang. For those who are curious, the problem is not with the equality operator, but with the
and which enjoys priority, and therefore
1 and M steals the show, leaving the interpreter clueless about the what
Y =:= might mean. To fix this, use parentheses:
(Y =:= 1) and (M =:= jan).
true. The cosmic balance is restored.
Somehow the documentation on the Erlang website faithfully reflects the weirdness of the language, and makes it easy to get lost. Some functions are not where they might seem reasonable at first (
tl — head and tail of lists, but not in the
lists module), and some categories (like
erts) simply make no sense for the newcomers.
We can interpret this as an attempt to let the unfamiliar developers walk through the entire documentation before granting them the piece of information they are looking for. This can have an effect of making us feel helpless sometimes instead of getting help. However the content itself is clear and straightforward enough to be usable, so the time spent in the documentation is not wasted.
Who am I to judge such a powerful and great language as Erlang? I haven’t even scratched the surface here! I should have talked about Erlang’s way of ruling concurrency, the OTP libraries, its implementation of the Actor model and so on, but as the title suggest, my goal here was only to get a taste of the language itself. Talking of which, I can tell that Erlang is great fun!
Despite its quirks, its famous performance and (otherwise) clean style lures to learn more. After all it’s not very different from other FP languages, especially from those of the ML domain. It’s less strict though, and that makes diving in relatively easy. Great books are also available to get started, and installing the tool set is also a piece of cake.
I would say, for those who are interested in message passing and similar features, and are not purists, it’s definitely worth a try and play around to get familiar with. This would also mean a huge benefit for those who otherwise intend to use its spinoff, Elixir instead. Erlang has a lot to offer and does its job in its own domain extremely well justifying the attention it recently gets.