Part 3: Node vs Elixir
Of the 3 languages I will use to compare with NodeJS, I will spend the most time on my favorite, Elixir. It also happens to be the most similar:
- Both are dynamic languages and support type annotations (i.e. Typescript & Dialyzer)
- Both languages run on a VM (Erlang VM & V8)
- In my opinion, good JS apps adopt functional programming, including immutability. Elixir is purely functional and immutable by default and mixing paradigms is not possible.
- GenServer is a pattern that should be fairly familiar to any JS developers that have worked with Redux. Although not exactly the same it should be an easy transition for anyone working with Redux and actions.
At first glance, it may also seem that Elixir is similar in the sense that it’s a language that is ‘transpiled’ into Erlang, much like Babel is used to ‘transpile’ JS code into more standard code that can run anywhere. This is far from true, however. Elixir is actually more of a macro language than syntactic sugar, which is far more powerful and is probably a feature that JS needed from the very get-go to scale the language without all of the crazy transpilers and plugins that are needed today.
Macro Programming & Extensibility
I like to think about what Javascript could be today if it had macro support from the start. I have no idea what that would even take, that's beyond my pay grade, but I know most, if not all, of the craziness I mentioned in the first part of this series could've been solved or worked around with macros, eliminating the need for at least Babel transformations and the whole import/require module format debacle.
Javascript wasn't built to be an extensible language. Flexible and dynamic, yes, but extensible no. Elixir, however, is basically a macro language for Erlang, enabling engineers to write abstractions at an even higher level than non-macro languages. Macros, along with the flexible module system, makes it easy to modify the language itself and do things that could otherwise be tedious and time-consuming.
Maturity
Although Elixir is relatively new, it is built on an extremely mature foundation being Erlang, which has been around since the late 70’s and was brilliantly designed to solve many of the problems we struggle with today. In fact, Erlang was built specifically for telecomm and to this day still runs a large share of telecomm companies), and was famously the ‘secret sauce’ WhatsApp used to scale to a ridiculous amount of users with an absurdly low staff & server count.
To reiterate this further, if you had no previous context and I asked you whether you’d prefer a language that would run on a VM designed to handle massive concurrency, is highly fault-tolerant and built to handle the world’s telecomm or a VM that is meant to run a web browser, which would make more sense?
Async
The real genius behind Elixir/Erlang is its concurrency model. Erlang uses isolated processes called 'green threads' along with message passing in what is called the Actor Model. It is an advanced and powerful way of handling concurrency that doesn't have the same limitations a single-threaded I/O model like Javascript's event loop does.
In fact, Elixir probably has the best async model of the newer generation of languages as well, though Rust & Tokio have an interesting approach that is lower-level and swappable, even if not quite as expressive to work with. Go's goroutines could be thought of as a type of 'green thread', though there are some very specific, and complicated, differences. Suffice it to say, Elixir, Go and Rust all have more powerful async solutions than NodeJS, which is constrained by its single-threaded design.
Language Design & Consistency
Because Erlang is over 30 years old and used heavily in Telecomm still to this day, it's fair to say it's pretty mature and consistent. If just looking at age itself it's going to beat a lot of the competition out there, but add in the rock-solid, battle-tested design and you have a real contender.
The problem with Erlang was its departure from the increasingly popular object-oriented approach to programming, and it's fairly radical approach to building software. It was also solving a problem that wasn't mainstream at the time (high-concurrency, fault-tolerant, distributed systems) but has since proved to be exactly what is needed some 30 years later.
What Elixir brings is a macro language for Erlang with a modern toolset that is easy, fun, well-documented and well thought out. The community is strong, and should you need something that isn't already provided by Elixir and the rich Erlang ecosystem, you can likely find Elixir packages, (or even Erlang packages) to get the job done.
Summary
These are just a few of the advantages Elixir brings to the table, but they really just scratch the surface of what is available. In my experience, Elixir is the gift that just keeps on giving, and I always find myself pleasantly surprised at the simple, idiomatic ways of doing things, even if they aren't obvious at first.