Video

Want to see the full-length video right now for free?

Sign In with GitHub for Free Access

Notes

Introduction

Elixir is a functional programming language built to run on the Erlang Virtual Machine. Breaking this down, Erlang is a language built by Ericsson in the 80's to run their telecom platform. As a result, it was designed to be highly concurrent and fault tolerant in order to support the high throughput and non-stop nature of telecom traffic.

Elixir builds on this Erlang foundation, but provides a nicer syntax to work in day to day. In contrast to a language like CoffeeScript which is "transpiled" into the intermediate language (JavaScript), Elixir is compiled directly to Erlang byte-code.

Elixir's syntax is very reminiscent of Ruby's with def and do and many other syntactic similarities. This is unsurprising as the creator of Elixir, José Valim, is also a Rails core team member and co-founder of Plataformatec. That said, Elixir's functional nature, amongst other features, mean that it is also quite different from Ruby in practice.

Sample Elixir Code

Recently we've been working on an internal communication tool written in Phoenix, a Rails-like web framework for Elixir. The following is a sample controller with a create action from that project:

defmodule Constable.Api.CommentController do
  use Constable.Web, :controller

  alias Constable.Api.CommentView
  alias Constable.Services.CommentCreator

  plug :scrub_params, "comment" when action in [:create]

  def create(conn, %{"comment" => params}) do
    current_user = current_user(conn)
    params = Map.put(params, "user_id", current_user.id)

    case CommentCreator.create(params) do
      {:ok, comment} ->
        Constable.Endpoint.broadcast!(
          "update",
          "comment:add",
          CommentView.render("show.json", %{comment: comment})
        )
        conn |> put_status(201) |> render("show.json", comment: comment)
      {:error, changeset} ->
        conn
        |> put_status(422)
        |> render(Constable.ChangesetView, "error.json", changeset: changeset)
    end
  end
end

Pattern Matching

One feature that stands out in the above code sample is the use of functional pattern matching via the case control-flow structure. While at times these sort of constructs can be a smell in Ruby land, they're a welcome and regular part of the toolset in functional languages like Elixir.

Pipe Operator

In addition, we can see repeated use of the pipe operator, |>, which provides a clear syntax for streaming a value through multiple functions, passing the output of each function as the first argument to the next function.

Speed Matters

Thanks to its functional nature and immutable data types, Elixir is able to easily take advantage of concurrency and the many cores on modern hardware in a way that Ruby simply can't. This can allow for overall better response speed, and perhaps more importantly much greater support for concurrent requests.

In our simple test we ran 100 concurrent requests against each of a Rails server and a Phoenix server, using the siege benchmarking tool, Phoenix solidly outperformed Rails:

Rails Phoenix
30 trans/sec* 183 trans/sec*

Note: transactions / second here essentially maps to how many request / response cycles each server was able to perform per second.

Channels and Websockets

Since Elixir can easily spin up additional lightweight processes and keep them running with little overhead, it's particularl well-suited to handling Websockets or other bidirectional (rather than request, response style) communication between a server and client.

Phoenix provides a mechanism known as a channel to handle this Websocket dance for us. Per the Phoenix docs:

Channels broker websocket connections and integrate with the PubSub layer for message broadcasting. You can think of channels as controllers, with two differences: they are bidirectional and the connection stays alive after a reply.

Developing With Elixir and Phoenix

All of the thoughtbotters who've worked on Phoenix applications have had very positive things to say about working with them. Specifically:

  • Channels - ease of working with Websockets a big plus
  • Speed - always matters
  • Syntax / Functional Nature - The structure that the functional nature of Elixir brought was a welcome change.

Also, Elixir and Phoenix run comfortably on Heroku with just a handful of steps, detailed in the Heroku guide in the Phoenix docs.

Fault Tolerance

One of the great features Elixir inherits and embraces from Erlang is fault-tolerance and a "crash early" approach to processes. Elixir uses Supervisors which watch over processes and manage restarting that child process using one of a handful of strategies such as restarting just that process, killing all sibling processes and restarting all together, and a few others.

Check out the talk, Idioms for building distributed fault-tolerant applications with Elixir by Elixir's creator, José Valim, for a deeper dive into this topic.

Build Tools & Testing

Despite Elixir's relatively young age (~3 years old for public releases) it has an impressive community and solid tooling. Two standouts are:

  1. Mix, the build & task running tool that ships with Elixir.
  2. ExUnit, the testing library that comes with Elixir.

Getting Started with Elixir

Now that we're fully convinced of the excellence of Elixir, we can continue our adventure with the following resources:

  1. The Getting Started guide on the Elixir-Lang Website provides a great introduction.
  2. Programming Elixir is a solid, deeper dive into Elixir by the Pragmatic Programmers.