Daniel Azuma

Family Ties part 1: Comparing Erlang and Elixir


I should learn Erlang

The importance of Erlang

For those of us who have been using/learning Elixir, there comes a time when we start to think, “I really should learn more Erlang”.

I started to feel this pretty strongly after about six months of experimenting with Elixir. By then, I understood that Elixir’s power comes from its roots in the Erlang language and the BEAM virtual machine. I knew that many of Elixir’s features, such as pattern matching and processes, are shared with Erlang/OTP. I had learned that I needed to interface often with Erlang libraries. But, since I had spent much of my career using modern mainstream languages such as Ruby, Erlang still felt like the eccentric old patriarch of the family. We respect him for building the family fortune, but now no one seems to know quite what to do with him.

Determined to turn that awkwardness around and become more familiar with Erlang, I embarked on a personal project: I began working on an Erlang-to-Elixir transpiler, a tool that takes Erlang source code and generates Elixir source code with equivalent functionality. Such a tool could conceivably be used as a first step toward porting an Erlang code base to Elixir. But to me, it was mostly a means to learn more Erlang and explore how the two languages related to each other.

Exploring the family ties

I began with the hypothesis that Elixir was extremely similar to Erlang, little more than syntactic veneer. If this was true, a transpiler should be quite simple to write. To a large extent, this is indeed true. Both languages are structured the same with modules containing functions, both employ immutable data with the same structures—atoms, lists, tuples, maps—both use pattern matching heavily in lieu of assignment and conditional logic, and both boast lightweight processes with actor-like message passing. Quite a bit of code would look very similar in the two languages. Take, for instance, a simple function that counts the items in a list.

In Erlang:

-spec count(list(any())) -> non_neg_integer().
count([]) -> 0;
count([_Head | Tail]) -> 1 + count(Tail).

In Elixir:

@spec count(list(any)) :: non_neg_integer
def count([]), do: 0
def count([_head | tail]), do: 1 + count(tail)

As we can see, the way code is written and structured (beyond some variations in syntax) is remarkably similar. So, setting aside the few extra Elixir features (such as protocols) that have no direct Erlang counterpart, perhaps Elixir could be thought of as just a dialect of Erlang, a bit of a cockney accent on top of otherwise nearly identical diction.

Over the course of the project, I came to understand that, while the languages are indeed very similar, there are a number of subtle differences that make direct translation more challenging than one might first expect. True to my goals, I did learn quite a bit, not only about Erlang, but also about the idiosyncracies of Elixir. Those idiosyncracies, the similarities and differences between the two languages, and their relationship with each other, are the topics of this series of articles.

I currently have around a dozen articles mapped out, covering such topics as variable scoping, naming, guards and comprehensions, records and types, and the Erlang preprocessor. For example, we’ll see how the two languages differ in their naming rules and conventions, and we’ll learn techniques for breaking some of those rules should you ever need to. We’ll also learn more than you ever want to know about Erlang’s preprocessor (including why preprocessors are evil), and how you can do similar things with Elixir macros. We’ll discover whether there are things Erlang can do that Elixir can’t, and what pitfalls you should be aware of when interfacing between Elixir and Erlang code. Overall, we’ll discover that each language has its own personality and quirks, but that nonetheless, the family ties run deep.

Where to go from here

My transpiler project, Erl2ex, is on Github and can be downloaded from Hex. Feel free to experiment with it. It is usable for many cases already, and indeed, its end-to-end tests actually involve downloading common Erlang libraries from Github, converting them to Elixir, and running their own test suites on the output. However, as of this writing, there are still a number of known issues and edge cases that I’m still working on.

Otherwise, feel free to browse the index of articles in this series, and stay tuned for more on Erlang and Elixir’s family ties.

Dialogue & Discussion