Max Out Your Programming HP and MP With Elixir

Hello, and welcome back to the language of the month!. For April, I decided to try my hand at a language that's been very popular with the Ruby community, and is causing quite a stir in the Elm community. It's called Elixir!

Why Learn Elixir?

Elixir brings some interesting features to the table; it's built on top of Erlang's BEAM virtual machine, which means that spawning processes is extremely lightweight, and programs written in Elixir are encouraged to make use of many task-focused processes. Like Erlang, it's ok (and expected!) for processes to fail; unlike Erlang, however, Elixir favors a more Ruby-like syntax as opposed to Erlang's Prolog-inspired one. Elixir was on my list of languages to learn because I figured it would be a good way to learn how to organize a program around many small processes. Let's dig in!

Learning the Language

The getting started guide on the official website is really good; it's a quick but very through introduction to the language, and provides a good overview of mix, Elixir's build tool and package manager, as well.

Distinctive Features

Elixir is full of features that work together to make a programmer happy!

Pattern Matching

Like many functional programming languages, Elixir supports pattern matching; not pattern matching as in matching a string against a regular expression, but rather comparing the structure of some data against a pattern. If the pattern matches, any variables in the pattern are bound to the part of the structure that matched it. For example:

def handle({:ok, result}) do
  IO.puts "Operation succeeded: " <> to_string(result)
end

def handle({:error, msg}) do
  IO.puts "A problem occurred: " <> to_string(msg)
end

Elixir allows us to define multiple variants of the handle function, and it will execute the version whose pattern matches. This allows the programmer to write very focused functions that handle a single situation and keeps the code nice and organized.

Put a Pin in It

Elixir has some interesting behavior when you try to pattern match with an existing variable in the pattern:

{username, password} = get_login_details()
hash = hash_password(password)
{:exists, hash} = get_password_hash_for_user(username)

In other languages I've used, such as Erlang and Prolog, if the hash returned by get_password_hash_for_user is different from the one returned by hash_password, the pattern match will fail. In Elixir, however, the pattern does match, and will bind hash to the one returned by get_password_hash_for_user. Fortunately, Elixir provides a way to express the other meaning via the pin operator:

{:exists, ^hash} = get_password_hash_for_user(username)

It's nice that we can express this in Elixir, but this is my first wart in the language. I try not to get hung up on syntax, but to me, it's too easy to mistakenly and silently blow away a variable's value and make a match succeed in ways you didn't expect it to. In my opinion, the pin operator's semantics should be reversed; that is, you should have to explicitly specify when you want to rebind a variable.

Spawn

The most important feature of Elixir - at least to me, whose goal is to learn concurrent programming - is spawn. spawn is Elixir's function for creating new processes; you give it a function, and in return, it gives you a process identifier, or pid for short. Each process in Elixir has a mailbox that we can send messages to via this pid; this the only way for Elixir processes to communicate. The advantage of this method of communication is that if a process crashes, you don't need to worry about dragging down some undefined shared state. To receive messages, you call the receive form, which we'll see a bit later.

Low Level of Abstraction over Erlang

Another nice feature of Elixir is that although the syntax is different from Erlang, a lot of the semantics are very close, and sometimes identical. This should make picking up Elixir as an Erlang developer very easy, and makes interoperability between Erlang and Elixir pretty simple.

Very Clean and Consistent Syntax

One thing that I noticed while going through the tutorial is how clean and regular Elixir's syntax is. Like I said before, I try not to get caught up on syntax, but having cleaner syntax with simpler rules never hurts! One example of this are do blocks, like we use with the def form:

def greeting(name) do
  "Hello, " <> name
end

do blocks are just syntactic sugar for a do: keyword argument with a block, so the above desugars to this:

def greeting(name), do: "Hello, " <> name

So do blocks aren't special; they're just a keyword argument (keyword arguments, in turn, are just an argument that happens to be a list of 2-tuples). Having regular and simple syntax is handy when you're developing tooling, or when your language supports…

Macros!

Like last month's language, Elixir has support for macros, which come in handy for reducing boilerplate and creating expressive code. One thing I found myself repeating a lot when writing Elixir was something along these lines:

def my_process() do
  receive do
    # handle message
  end

  my_process
end

So I ended up making a receive_forever macro to eliminate a lot of the boilerplate:

defmacro receive_forever(block) do
  quote do
    f <- fn(f)
        receive do
          unquote(block)
          f.(f)
        end
    end
    f.(f)
  end
end

def my_process() do
  receive_forever do
    # handle message
  end
end

Pretty cool, huh? One quick caveat, though; this code won't actually work! Since macros are modifying the compilation process, you need to define a macro in a separate module to be able to use it in code. That was something that was very surprising and confusing when working on my macro, but makes a certain amount of sense.

One thing that concerned me when writing this macro was my f variable - what would happen if the user happened to use an f variable in the enclosing code and in the block? Well, never fear! Elixir's macros are what's called hygenic macros; this essentially means that variables introduced in the macro don't contaminate the generated code.

Moving Forward

I have a fun idea for the program I'm going to implement in Elixir; I feel like the two are a really natural fit. Digging into Elixir has inspired me to check out the Erlang OTP, which allows for building extremely robust and fault-tolerant systems. A lot of people are very excited about the Phoenix web framework for Elixir, so I might end up building something with that if the right idea comes my way. It seems to be a natural pairing to using Elm on the frontend.

From my experience with it so far, Elixir is a fun language to work with, and I highly recommend trying it out!