Elixir is for programmers
- select the contributor at the end of the page -
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.
Smart assert
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 lineif(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 clauses
. The clauses
are do
and else
.
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 do
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 case...when
. Why? class
has an end
, but no do
. There's no way to write a custom method that takes a block without do
.
Elixir is consistent.
Need a module? It's defmodule...do
. Need an if
? It also uses do
. Same with def
.
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.
Built-in TDD
I find it extremely difficult to learn a new language if I can't write unit tests. My confidence in writing JavaScript and many other languages went way up once I started using a decent test runner.
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).
Mind-blowing metaprogramming: upcase
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 upcase
function.
Pretty cool!
Conclusion
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:
Ready to test your skills in Ruby? See how they stack up with this assessment from Smarterer. Start this Ruby test now.