Playing with Elixir and Erlang
I’ve always been interested in playing with functional programming in my spare time. I’ve tried out a few different flavors such as Clojure, Erlang, Haskell, and OCaml but never really found myself sticking to one or really getting to know any of them really well. Lately I’ve been playing with Elixir and Erlang together and have fallen in love with them.
Mixing (and Matching)
One of my favorite things about this combination is the ability to mix Erlang and Elixir. Even though I’ve written a decent amount of Clojure I was never really interested in much Java interop.
Example time, I can write a fib
function in Erlang and then load it directly into iex
(An Elixir REPL) and it will compile and load it with no problem. Assume the following is saved in test.erl
.
-module(test).
-export([fib/1]).
% Very slow fib
fib(0) -> 1;
fib(1) -> 1;
fib(N) -> fib(N - 1) + fib(N - 2).
The code is pretty straight forward, define the module test, export our fib function that takes 1 argument, then we define our fib with pattern matching. Pattern matching alone is a magical thing that deserves its own discussion so I’ll skip explaining that for now.
Then we can run iex
and tell it to compile and load our module with the c
helper method. Since we’re loading Erlang instead of Elixir, our module will be an atom. Atoms are like symbols in Ruby and evaluate to themselves. So to access our Erlang test module we can use :test
and to call functions we still use Elixir syntax with a .
instead of Erlang’s :
.
iex(1)> c "test.erl"
[:test]
iex(2)> :test.fib(4)
5
iex(3)>
And It works! We’re using Erlang code inside of Elixir without any hassle at all.
We can also write the exact same thing in Elixir too in test.ex
.
defmodule Test do
def fib(0), do: 1
def fib(1), do: 1
def fib(n) do
fib(n-1) + fib(n-2)
end
end
Now we can use our Elixir test module natively.
iex(1)> c("test.ex")
[Test]
iex(2)> Test.fib(4)
5
If we want to use our Elixir module in Erlang we have to compile it first. Just run elixirc test.ex
and it will compile it to Elixir.Test.beam
. Elixir always prefixes Elixir modules with Elixir
and they’re called from Erlang the same way but use a :
to call the function.
1> l('Elixir.Test').
{module,'Elixir.Test'}
2> 'Elixir.Test':fib(4).
5
We’ve seen how we can use Elixir in Erlang and Erlang in Elixir. This means we can use any code written in either Elixir or Erlang in one another with ease.
Pattern Matching
Pattern matching is one of the major features that make Erlang and Elixir attractive. Instead of having to take optional parameters or split your function into several differently named functions we can use a single name with multiple functions that attempt to match the passed in parameters with the arguments it takes.
We’ve already seen this with Erlang and Elixir with our fib
function. It checks for 0, 1, or any other number and calls the appropriate function for each.
We’re using the Fibonacci sequence on purpose so it can be expanded upon into a slightly more complicated and more efficient version of our fib
function. Right now if you run the fib
function with larger inputs it can be extraordinarily slow. Our solution is elegant but it calls the same code too many times over. We can fix this by holding on to what number we’re currently on, the previous value, and the two previous values.
In Erlang our code will look like this.
-module(test).
-export([fib/1]).
fib(0) -> 0;
fib(1) -> 1;
fib(2) -> 1;
fib(N) -> fib(N, 1, 1).
fib(3, Previous, Current) -> Current + Previous;
fib(N, Previous, Current) ->
fib(N - 1, Current, Previous + Current).
Here we are doing a bit more. We only export fib/1
since that’s all we want to expose for people to use. We then match 0, 1, and 2 and return their respective values. Now when we match any other number we call fib
with 3 arguments, the number we want to get the value of, the previous value, and the previous previous value. Since the Fibonacci sequence starts with 1 and 1 that’s what we pass instead of calling our fibs again.
When our 3 arity fib
function is called we recursively call itself with our current value minus 1, current becomes previous, and our new current value is our previous value added to our current value. Once N is 3 the first version of fib/3
matches and we return the Current
and Previous
values added which happens to be the nth number of the sequence where n
is what fib/1
was originally passed.
No we can load this into erl
and try it out.
1> c('test.erl').
{ok,test}
2> test:fib(50).
12586269025
The same code in Elixir would look pretty much the same but with a hint of Ruby.
defmodule Test do
def fib(0), do: 0
def fib(1), do: 1
def fib(2), do: 1
def fib(n) do
fib(n, 1, 1)
end
defp fib(3, previous, current) do
current + previous
end
defp fib(n, previous, current) do
fib(n - 1, current, previous + current)
end
end
Since Elixir doesn’t have an export call we can use defp
to define a method private to the module. Besides that difference everything is almost identical.
Now if we load this up into iex
we can play this version as well.
iex(1)> c "test.ex"
iex(2)> Test.fib(50)
12586269025
Everything so far is pretty trivial but Elixir has quite a few other features that make it awesome such as macros and other metaprogramming goodies. If you want to learn more check out the Getting Started section of the Elixir Lang site. I’ve also just started reading the excellent (beta version) of Programming Elixir by Dave Thomas.
If you’re more interested in the Erlang side check out Learn You Some Erlang For Great Good and Programming Erlang 2nd Edition.