Elixir is for programmersBy Geoffrey Grosenbach
The early 2000’s were an exciting time for dynamic programming languages.
Excitement was high for the upcoming Perl 6. It ran on a brand new VM built specifically for dynamic languages and included dozens of experimental syntax sugars such as the hyper operator, which could run a calculation on every element of an array (somewhat like
map). I attended the Seattle Perl User Group every month where you couldn’t ignore the anticipation!
It was during these years that I first heard about a weird language that used indentation to define scope (now known as Python). And on January 1, 2001, Dr. Dobb’s journal published an article by Dave Thomas introducing Ruby to the West.
That’s not even counting the other languages I tried out for a few weeks each such as REBOL, RealBasic, and AppleScript.
There must be something special about the start of a decade, because I’m feeling that same kind of excitement and seeing that same kind of experimentation again. At the beginning of May we filmed a 2 hour video with Elixir creator José Valim. Elixir is a language that runs on the Erlang VM but is inspired by the syntax and concepts of many other languages including Ruby, Python, and even Lisp.
It’s not just a transpiler like CoffeeScript; it makes real Erlang
.beam bytecode. According to some, parts of Elixir are even more optimized than Erlang itself. It benefits from all the concurrency and deployment options available to Erlang programs. Elixir can call Erlang functions and vice versa.
I don’t pretend to be an Elixir expert (that’s why we worked with José, who is), but I left the session with a lot of enthusiasm for Elixir’s design and features. Here are a few of them.
A programming language like Ruby loses a lot of data when you run it. It quickly loses access to the original source code. If you write a test with Ruby’s basic
assert, it can only tell you that the test passed or that it didn’t; it can’t tell you why. Projects like ruby2ruby have tried to bridge this gap but haven’t had support from the core team.
Elixir works directly with your source code to do smart things. Tests rarely need more than the built-in
assert, yet meaningful errors can be displayed. Take this Elixir code:
test "makes bacon" do assert Bacon.make_bacon() == "avocado" end
By reading it, we can see that it’s erroneously making
bacon but looking for
"avocado". We expect that it will fail. If this were a test in Ruby (or any other language), we would see an error such as
expected true, got false. Not too helpful.
In Elixir, we see this:
** (ExUnit.ExpectationError) expected: "bacon" to be equal to (==): "avocado" at test/bacon_test.exs:14
Elixir knows that
== is being used in the assertion, and shows you the values on either side of
== when the test fails. Now that’s a useful error!
Multi-block control flow
For years I’ve wanted to be able to write my own control flow structures, such as an
each...else that runs an
else block if the
each is empty (Handlebars templates have this).
The only way to do that in Ruby would be to pass several lambdas to a method, which would be ugly.
In Elixir, the relationship between single line functions and multi-line blocks is well thought out.
These two are equivalent:
# Single line if(condition, do: a, else: b) # Multiple lines if condition do a else b end
In the single line version,
if is a function that takes two arguments: the
condition and the
The multi-line version needs no explanation.
But the fact that these two are equivalent means you can write a macro that works just like a built-in. Because that’s how the built-ins are written, too! (source)
Consistent use of
Developers who love Ruby like the fact that they can override built-in operators and write DSLs that look like built-in Ruby syntax.
But many of Ruby’s syntactical elements are off limits. You can’t write Ruby code that works like a
class definition, or an
if, or a
class has an
end, but no
do. There’s no way to write a custom method that takes a block without
Elixir is consistent.
Need a module? It’s
defmodule...do. Need an
if? It also uses
do. Same with
defmodule MyModule do def my_function do end end
If you want to write a macro that works like the language does, you can. Because Elixir is implemented with the same tools available for you to use.
With Elixir, it’s built in. Use the
mix command to generate a
new app and you’re ready to go with a unit test. Run
mix test to run the test suite. Done.
It even has conveniences like a
test function that takes a quoted descriptive message as the name of the test.
test "extracts m3u8 from index file" do m3u8s = Streamers.extract_m3u8 index_file assert Enum.first(m3u8s) == Streamers.M3U8 [program_id: 1, bandwidth:
110000, path: m3u8_sample] assert length(m3u8s) == 5 end
And it can run your tests concurrently with a single
async option (docs).
One of the most ingenious techniques that José mentioned didn’t make it into the final cut of our video.
To capitalize a word, Elixir could implement a single
upcase function that keeps a list of Unicode letters in memory and figures out how to translate between them.
Instead, it generates a function definition for each letter. They look roughly like this (source):
def upcase("é" <> rest) do ["É"] ++ upcase(rest) end
A few things are going on here. Elixir can match functions on the number, type, and content of its arguments. So it looks for a letter such as
é. It knows the upper case version of the letter, then sends the rest of the string to the next letter’s
Elixir has many of the features that I look for in a programming language. Its authors have stolen useful features from other languages, it focuses on making it easier to write complex applications, and it has a healthy balance between performance and syntax.
Elixir isn’t the only way to write concurrent applications, but it’s definitely one I’ll be experimenting with for a few months. If you want to learn what it’s about, check out our fast-paced two hour video at PeepCode: