erlangbasics
Erlang Basics
Erlang Mantra
Origin (PDF): Making Reliable Distributed Systems in the Presence of Software Errors
- Everything is a process.
- Processes are strongly isolated.
- Process creation and destruction is a lightweight operation.
- Message passing is the only way for processes to interact.
- Processes have unique names.
- If you know the name of a process you can send it a message.
- Processes share no resources.
- Error handling is non-local.
- Processes do what they are supposed to do or fail.
...The statement about error handling is perhaps less obvious. When we make a fault-tolerant system we need at least two physically separated computers. Using a single computer will not work, if it crashes, all is lost. The simplest fault-tolerant system we can imagine has exactly two computers, if one computer crashes, then the other computer should take over what the first computer was doing. In this simple situation even the software for fault-recovery must be non-local; the error occurs on the first machine, but is corrected by software running on the second machine.
Common Concurrency Patterns
Cast (send msg)
A ! B
Event (get msg)
receive A -> A end
Call (RPC)
A ! {self(), B},
receive
{A, Reply} ->
Reply
end
Callback
receive
{From, A} ->
From ! F(A)
end
Some trickery follows ;).
Here something cool – Parallel RPC:
%% Usage: par_rpc([A, B, C], M)
par_rpc(Ps, M) ->
Self = self(),
Tags = map(fun(I) ->
Tag = make_ref(),
spawn(fun() ->
Val = rpc(I, M),
Self ! {Tag, Val}
end),
Tag
end, Ps),
yield(Tags).
yield([]) -> [];
yield([H|T]) ->
Val1 = receive {H, Val} -> Val end,
[Val1|yield(T)].
Parallel map implementation in Erlang:
%% Map function F over list L in parallel.
par_map(F, L) ->
Parent = self(),
[ receive
{Pid, Result} -> Result end ||
Pid <- [ spawn( fun() -> Parent ! {self(), F(X)} end) || X <- L ]
].
Behaviours
based on Joe Armstrong post to the ML
gen_server
Here is a mini gen_server:
rpc(Pid, Q) ->
Pid ! {self(), Q},
receive
{Pid, Reply} -> Reply
end.
loop(State, Fun) ->
receive
{swapCode, Fun1} ->
loop(State, Fun1);
{From, Q} ->
{Reply, State1} = Fun(Q, State),
From ! {self(), Reply},
loop(State1, Fun)
end.
Here’s one that crashes the client if the query in the server results
in a crash:
rpc(Pid, Q) ->
Pid ! {self(), Q},
receive
{ok, Pid, Reply} -> Reply;
{oops, Pid, Why} -> exit(Why)
end.
loop(State, Fun) ->
receive
{swapCode, Fun1} ->
loop(State, Fun1);
{From, Q} ->
case (catch Fun(Q, State)) of
{'EXIT', Why} ->
From ! {oops, self(), Why},
loop(State, Fun);
{Reply, State1} ->
From ! {ok, self(), Reply},
loop(State1, Fun)
end
end.
gen_event
send_event(Pid, Event) ->
Pid ! {event, Event}.
register(Pid, F) ->
Pid ! {register, F}.
loop(Funs) ->
receive
{register, Fun} ->
loop([Fun | Funs]);
{event, Event} ->
lists:foreach(fun(F) -> F(Event) end, Funs),
loop(Funs)
end.
See also
- Erlang Study – Sequential Programming (part1)
- Erlang Study – Concurrent Programming (part2)
- Erlang Study – Robustness (part3)
- Erlang Study – System Principles (part4)