JUST ANOTHER SYNTAX FOR ERLANG?
Old Programming language (1986), built by Ericsson with the aim to improve the development of telephony applications.
Major characteristics:
New Programming language (2012), built by José Valim with the aim to improve some of the Erlang flaws.
Major characteristics:
-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
1. Encoding
Strings and UTF-8 encoding are the default whereas Erlang default is charlists
"hello" #=> ascii in Erlang
"hello" #=> UTF-8 in Elixir
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
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
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
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,...
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.
7. GenBroker + GenStage
Current GenEvent implementation can be a problem because:
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.
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.