Elixir

JUST ANOTHER SYNTAX FOR ERLANG?

What is Erlang?

  • OTP
  • Functional
  • Distributed
  • Fault-tolerant
  • Hot-code loading

Old Programming language (1986), built by Ericsson with the aim to improve the development of telephony applications.

Major characteristics:

What is Elixir?

  • All Erlang/OTP characteristics (same VM)
  • Meta-programmation
  • Polymorphism

New Programming language (2012), built by José Valim with the aim to improve some of the Erlang flaws.

Major characteristics:

Erlang

Elixir

-module(bank_account).
-behaviour(gen_server).
-export([start_link/0, init/1, handle_call/3]).
-export([deposit/2, state/1, withdraw/2]).

start_link() ->
    gen_server:start_link(?MODULE, [], []).

init(_Arguments) ->
    {ok, 0}.

deposit(AccountPid, Amount) ->
    gen_server:call(AccountPid, {deposit, Amount}).

state(AccountPid) ->
    gen_server:call(AccountPid, state).

withdraw(AccountPid, Amount) ->
    gen_server:call(AccountPid, {withdraw, Amount}).

handle_call({deposit, Amount}, _From, AccountState)
when Amount < 1 ->
    {reply, {error, "Deposit only accept positive amount."}, AccountState};
handle_call({deposit, Amount}, _From, AccountState) ->
    NewAccountState = AccountState + Amount,
    {reply, {ok, NewAccountState}, NewAccountState};

handle_call(state, _From, AccountState) ->
    {reply, AccountState, AccountState};

handle_call({withdraw, Amount}, _From, AccountState)
when Amount < 1 ->
    {reply, {error, "Withdraw only accept positive amount."}, AccountState};
handle_call({withdraw, Amount}, _From, AccountState)
when Amount > AccountState ->
    {reply, {error, "Not enough money on the account."}, AccountState};
handle_call({withdraw, Amount}, _From, AccountState) ->
    NewAccountState = AccountState - Amount,
    {reply, {ok, NewAccountState}, NewAccountState}.
defmodule BankAccount do
  use GenServer

  def start_link do
    GenServer.start_link(__MODULE__, 0)
  end

  def deposit(account_pid, amount) do
    GenServer.call(account_pid, {:deposit, amount})
  end

  def state(account_pid) do
    GenServer.call(account_pid, :state)
  end

  def withdraw(account_pid, amount) do
    GenServer.call(account_pid, {:withdraw, amount})
  end

  def handle_call({:deposit, amount}, _from, account_state)
  when amount < 1 do
    {:reply, {:error, "Deposit only accept positive amount."}, account_state}
  end
  def handle_call({:deposit, amount}, _from, account_state) do
    new_account_state = account_state + amount
    {:reply, {:ok, new_account_state}, new_account_state}
  end

  def handle_call(:state, _from, account_state) do
    {:reply, account_state, account_state}
  end

  def handle_call({:withdraw, amount}, _from, account_state)
  when amount < 1 do
    {:reply, {:error, "Withdraw only accept positive amount."}, account_state}
  end
  def handle_call({:withdraw, amount}, _from, account_state)
  when amount > account_state do
    {:reply, {:error, "Not enough money on the account."}, account_state}
  end
  def handle_call({:withdraw, amount}, _from, account_state) do
    new_account_state = account_state - amount
    {:reply, {:ok, new_account_state}, new_account_state}
  end
end

Syntax DiffErence

  • Erlang modules are nothing more than symbols
  • Elixir can easily call erlang code by using symbols
    • :bank_account.start_link
  • Elixir modules are nothing more than symbols prefixed by Elixir
    • :"Elixir.BankAccount" == BankAccount
  • Erlang can call Elixir code by using symbols
    • 'Elixir.BankAccount':start_link()

Interoperability

Elixir additions

1.   Encoding

Strings and UTF-8 encoding are the default  whereas Erlang default is charlists

"hello" #=> ascii in Erlang
"hello" #=> UTF-8 in Elixir

Elixir additions

2.   Tasks

task = Task.async(fn -> do_some_hard_work() end)
# Do some stuff here 
result = Task.await(task)

Layer on top of GenServer, intended to execute a specific action asynchronously without storing state

Elixir additions

3.   Agents

{:ok, pid} = Agent.start_link(fn -> %{} end)
Agent.update(pid, fn state -> Map.put_new(state, :my_key, 42) end)
Agent.get(pid, fn state -> Map.get(state, :my_key) end)
#=> 42

Layer on top of GenServer, intended to store a mutable state without defining any specific action

Elixir additions

4.   Protocol

defprotocol Blank do
  @fallback_to_any true
  def blank?(data)
end

defimpl Blank, for: List do
  def blank?([]), do: true
  def blank?(_),  do: false
end

A bit like an Interface. You define the list of function which needs to be implemented by other modules

Elixir additions

5.   Lisp-ish macro

defmodule MyLogic do
  defmacro unless(expr, opts) do
    quote do
      if !unquote(expr), unquote(opts)
    end
  end
end

require MyLogic
MyLogic.unless false do
  IO.puts "It works"
end

Erlang has a preprocessor style macro whereas Elixir has a more advanced macro style: AST manipulation,...

Elixir additions

6.   Doctests

defmodule MyApp.Hello do
  @moduledoc """
  This is the Hello module.
  """

  @doc """
  Says hello to the given `name`.

  ## Examples

      iex> MyApp.Hello.world(:john)
      :ok

  """
  def world(name) do
    IO.puts "hello #{name}"
  end
end

Documentation is a first class citizen. To be sure code in documentation is not outdated, it's possible to make it part of the test suite.

Elixir future additions

7.   GenBroker + GenStage

Current GenEvent implementation can be a problem because:

 

  • event handlers are synchronous
  • event handlers live in the same process than the server so
    • hard to supervise
    • tricky to use in a fault tolerant way  

Elixir Tooling

1.   Mix

A task runner. It is used to start servers, install dependencies, run tests,...

2.   Hex

Package manager allowing to download Erlang and Elixir packages

3.   Guides and books

Good starter guide, tutorials and books explaining how the language work, how to use it.

Conclusion

The syntax is the most obvious difference between Erlang et Elixir but not the only difference.

 

Both languages shares a common ground: same VM, same OTP library, ... but Elixir also aims to improve some of the Erlang flaws.

 

I wish this talk helped you understand Elixir enthusiasts are not all foolish rubyists following blindly one of their old leader into a new language :)

 

Elixir is really awesome. Thanks to OTP.

Made with Slides.com