/mili

Elixir

It was almost a year ago that I got introduced to Elixir. I remember my first impression was yet another language on top of another one. In addition running a software on Erlang/OTP, it’s totally different beast and I was a bit skeptical about it. However I gave it a chance and after a year, I’m totally happy with the time I spend and I believe that everyone should take advantage of the language and the platform that is running on.

Why Elixir?

Eloquent & Expressive

1
2
urls = ["http://google.com", "http://trello.com"]
random_url = urls |> Enum.shuffle |> List.first

Do I need to explain the code? Don’t think so! And if you have a bit of unix background this code should make sense to you.

Yes, there is an easy way to get random item, the above code is just for making the point.

BTW did I tell you Elixir is a functional programming language? If you are not familiar with functional programming, it might take sometime to get to use to that, after you un-learn your OOP skills, you’ll find that every thing make sense in Elixir land.

Mature Environment

Elixir comes with tools that help you to develop and build your project:

  • Mix is a build tool to run your tasks.
  • Hex is a package manager that is done right.
  • ExUnit is your unit testing framework.
  • ExDoc generates documentation for your Elixir projects.

Also Elixir runs on Erlang Virtual Machine(BEAM) which has been around us for quite some time.

Metaprogramming

Whenever I’m learning a new language, after I get enough familiar with the language and it’s syntax the first thing I’m looking for is how can I write a code that writes code for me!

The way that elixir does that is quite interesting. Elixir’s AST can be represented by Elixir’s own data structures. Having the AST accessible by normal Elixir code lets you do very powerful things because you can operate at the level typically reserved only for compilers and language designers.

Let’s say you want to humanize a mathematical equation, the interface and output should be like this:

1
2
3
4
5
6
iex > Math.say 1 + 2
1 plus 2 is 3
3
iex > Math.say 5 + 7
5 plus 7 is 12
12

To achieve that we need to write a macro that matches with AST of 1 + 2. Thanks to Elixir’s quote function it’s easy to find out how it looks like:

1
2
iex > quote do: 1 + 2
{:+, [context: Elixir, import: Kernel], [1, 2]}

Now that we know how 1 + 2 is represented in Elixir’s format we can write a defmacro that matches with that data:

1
2
3
4
5
6
7
8
9
10
11
defmodule Math do
  defmacro say({:+, _, [lhs, rhs]}) do
    quote do
      lhs = unquote lhs
      rhs = unquote rhs
      result = lhs + rhs
      IO.puts "#{lhs} plus #{rhs} is #{result}"
      result
    end
  end
end

You can read more about macros in Metaprogramming Elixir book.

this example is from the same book

I have to mention that if and most of the Elixir’s keywords are implemented as a macro.

Concurrent

Having a concurrent system in Elixir is inevitable. In addition because the data are immutable you don’t need to worry about concurrent processes stepping on each others toes. Elixir achieves that by running on top of BEAM.

How does it work? Let’s say you want run a few queries and send the results back to the user

1
2
3
4
5
6
iex > run_query = fn (query) ->
  1000
  |> :random.uniform
  |> :timer.sleep
  IO.inspect "query #{query} executed"
end

Not all the queries finish in the same time, to simulate that we sleep random amount of time

It would be nice to run independent queries concurrently, let’s try it in iex:

1
2
3
4
5
6
iex > spawn(fn -> run_query.('select * from users') end)
#PID<0.132.0>
iex > spawn(fn -> run_query.('select * from reports') end)
#PID<0.134.0>
"query select * from users executed"
"query select * from reports executed"

As you can see both queries executed concurrently.

You might ask how can I get back the results and use them in the main process? The answer is Message Passing.

1
2
3
4
5
6
7
8
9
iex > caller = self
iex > spawn(fn -> send(caller, {:query_result, run_query.('select * from reports')}) end)
#PID<0.138.0>
"query select * from reports executed"
iex > receive do
    {:query_result, result} -> IO.inspect "result is #{result}"
    end
"result is query select * from reports executed"
"result is query select * from reports executed"

The processes in Elixir(it’s not same as unix processes) can talk to each other through message. Each process has it’s own mailbox and can process one item at a time.

Distributed

I save the best for last.

Erlang and its BEAM was implemented to be run in distributed environment. You can easily connect two Erlang nodes together:

Node1:

1
2
$ iex --sname node1
iex(node1@localhost)>

Node2:

1
2
3
4
$ iex --sname node2
iex(node2@localhost)> Node.connect(:"node1@localhost")
iex(node2@localhost)> Node.list
[:"node1@localhost"]

After the nodes recognize each other, you can run any piece of code in any node in the cluster.

Node2:

1
2
3
4
5
iex(node2@localhost)> Node.spawn(
  :"node1@localhost",
  fn -> IO.puts "This code ran from #{node}" end)
This code ran from node1@localhost
#PID<8771.79.0>

It looks simple, isn’t it? To be fair, building distributed application requires more thought but hey at least the language and its environment are in your side!

My experience

Well I said why I like Elixir but learning a new language is challenging and it’s get more complicated if it’s running under different environment(Erlang/OTP).

  • I did struggle with the idea of immutable data but after a while I got use to that.
  • Elixir has a few Hash/List like data structures and few times I got confused to choose between them. It’s getting better since Elixir is depreciating HashDict.
  • Building a concurrent, fault-tolerant system under BEAM requires knowledge about OTP framework(Elixir in Action can help you a lot).
  • Even though Elixir code runs under the VM, the development cycle is fast! Mix and Hex are doing great job, the dependencies are downloaded and compiled very fast. I can fire up iex console or run mix test and quickly get the result back.

Where to start

Are you interested? Spending most of my time reading Elixir/Erlang related book I can give you some suggestions

  • Programming Elixir This book helps you to get familiar with the language and master it.
  • Elixir in Action You’ve read Programming Elixir but still have lot of questions about using OTP? This book is for you.
  • Programming Phoenix Are you ready to write your first app in Elixir? If it’s a web app you can use Phoenix.
  • Metaprogramming Elixir You’ve built your first app in Phoenix but wondering how Phoenix does all of those tricks? It’s time to level up and become master in Elixir Metaprogramming by reading this book.