The problems of Erlang the language
I've been using Erlang in production for more than three years, to build a long-polling based HTTP push server that in peak time serves over 2000 reqs/sec (non-cached). Never before have I written anything of this scale nor have I ever developed something this stable. The service just runs happily, without me thinking about it. This was actually my first Erlang code, initially burdened with anti-patterns, and bad approaches. And still, EVM proved to be very resilient, and the server ran efficiently from the very beginning. Most importantly, it was fairly straightforward for me to work on such complex problem, in large part owing to Erlang concurrency mechanism.
What Elixir is (not)
Ability to organize the code and to flush out duplication and boilerplate is in my opinion an important role of every programming language. If developers have to constantly duplicate some code structure (such as this one), without being able to eliminate this duplication reasonably easy, then the language doesn't succeed when it comes to code organization. It is in this department where Elixir improves on Erlang, by providing additional tools to organize the code, making developers more efficient in writing production-ready, maintainable code.
On a more specific level, metaprogramming helps us remove structural duplication - a situation where two pieces of code share the same abstract pattern, but differ in various details. For example, a following snippet presents a sketch of a module that models a User record:
defmodule User do #initializer def new(data) do ... end # getters def name(user) do ... end def age(user) do ... end # setters def name(value, user) do ... end def age(value, user) do ... end end
defrecord User, name: nil, age: 0
State1 = trans_1(State), State2 = trans_2(State1), State3 = trans_3(State2), ...
trans_3( trans_2( trans_1(State) ) )
state |> trans_1 |> trans_2 |> trans_3
list |> Enum.filter(&(&1 > 0)) # take positive numbers |> Enum.map(&(&1 * &1)) # square each one |> Enum.reduce(0, &(&1 + &2)) # calculate sum
Polymorphism via protocols
Protocols allow developers to create a generic logic that can be used with any type of data, assuming that some contract is implemented for the given data. An excellent example is the Enum module, that provides many useful functions for manipulating anything enumerable. For example, this is how we iterate an enumerable:
Enum.each(enumerable, fn -> ... end)
1 2 3 4
list |> Stream.filter(&(&1 > 0)) |> Stream.map(&(&1 * &1)) |> Enum.reduce(0, &(&1 + &2)) # Entire iteration happens here in a single pass
It's worth mentioning that laziness is in no special way supported by Elixir compiler. The general protocol support, and first-class functions are all it takes to implement lazy enumerables.