Want to see the full-length video right now for free?
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.
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
[Phoenix]: http://www.phoenixframework.org/
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.
[case control-flow structure]: http://elixir-lang.org/getting-started/case-cond-and-if.html#case
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.
[the pipe operator]: http://elixir-lang.org/getting-started/enumerables-and-streams.html#the-pipe-operator
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.
[siege benchmarking tool]: https://www.joedog.org/siege-home/
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.
[channel]: http://www.phoenixframework.org/v0.7.2/docs/channels#section-channels
All of the thoughtbotters who've worked on Phoenix applications have had very positive things to say about working with them. Specifically:
Also, Elixir and Phoenix run comfortably on Heroku with just a handful of steps, detailed in the [Heroku guide in the Phoenix docs][].
[Heroku guide in the Phoenix docs]: http://www.phoenixframework.org/docs/heroku
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.
[Supervisors]: http://elixir-lang.org/docs/v1.0/elixir/Supervisor.html [Idioms for building distributed fault-tolerant applications with Elixir]: https://www.youtube.com/watch?v=B4rOG9Bc65Q
Despite Elixir's relatively young age (~3 years old for public releases) it has an impressive community and solid tooling. Two standouts are:
[Mix]: http://elixir-lang.org/getting-started/mix-otp/introduction-to-mix.html [ExUnit]: http://elixir-lang.org/docs/v1.0/ex_unit/ExUnit.html
Now that we're fully convinced of the excellence of Elixir, we can continue our adventure with the following resources:
[Getting Started guide]: http://elixir-lang.org/getting-started/introduction.html [Programming Elixir]: https://pragprog.com/book/elixir/programming-elixir