Upload
corrado-santoro
View
284
Download
1
Embed Size (px)
Citation preview
An Introduction to Erlang
Corrado Santoro
Dipartimento di Matematica e Informatica
Universita’ di Catania
Master Cloud P.A.
Corrado Santoro An Introduction to Erlang
Overview
1 Erlang Basics:
Function programming principlesBasic syntax
Basic recursion and list usage
2 Data types, Matching and Advanced Recursion:
Variables and assignment principlesErlang data types
Using tail-recursion
Corrado Santoro An Introduction to Erlang
The Erlang Language
Definition
Erlang is a functional language developed by Ericsson for
provide a flexible and safe support to program
telecommunication equipments.
Characteristics
Functional Semantics
Strong usage of lists
Concurrent programming model based on isolated
processes exchanging messages
Runtime support for the creation of distributed and highly
fault-tolerant applications.
It can be downloaded from http://www.erlang.org
Corrado Santoro An Introduction to Erlang
Erlang and Functional Programming
Functional Programming
A program is a set of functions, similar to algebraic
functions, which behave by working only on input values and
giving output results.
Strict analogy with algebra, this we don’t have:
global variables
for and while constructs, we use recursion
if and case constructs, we use multiple function clause
Corrado Santoro An Introduction to Erlang
Our first function: factorial
The mathematical formulation
0! = 1
n! = n · (n − 1)! , ∀n > 0
This definition does not contain either cycles for or if
statements.
Can we implement it exactly “as is”?
Corrado Santoro An Introduction to Erlang
Our first function: factorial
Solutions in C:
✞int fact (int n) {
if (n == 0)
return 1;
else
return n * fact (n - 1);
}
✡✝ ✆
✞int fact (int n) {
int res = 1;
while (n > 1) {
res = res * n;
n --;
}
return res;
}
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Our first function: factorial
Solutions in C:
✞int fact (int n) {
if (n == 0)
return 1;
else
return n * fact (n - 1);
}
✡✝ ✆
✞int fact (int n) {
int res = 1;
while (n > 1) {
res = res * n;
n --;
}
return res;
}
✡✝ ✆
Solution in Erlang:
✞fact(0) -> 1;
fact(N) -> N * fact(N - 1).
✡✝ ✆
Mathematical formulation
0! = 1
n! = n · (n − 1)! , ∀n > 0
Corrado Santoro An Introduction to Erlang
Erlang: first syntactical elements
✞fact(0) -> 1;
fact(N) -> N * fact(N - 1).✡✝ ✆
fact Function name; it is defined using two clauses
fact(0) -> 1; First clause executed when the
argument is “0”.
The definition is terminated with symbol “;”, it means that
other clauses will follow.
fact(N) -> N * fact(N - 1). Second clause
executed when the condition on the first clause is not met
(that is the argument is not “0”).
The definition ends with symbol “.” meaning that this is the
last clause of the function.
Corrado Santoro An Introduction to Erlang
Erlang: first syntactical elements
✞fact(0) -> 1;
fact(N) -> N * fact(N - 1).✡✝ ✆
Function names and language keywords are literal
symbols starting with lowercase.
Variables and Parameter are literal symbols starting with
uppercase.
Symbol “->” separates function definition from function
body (implementation).
Function/clause body contains a set of expressions,
comma-separated, which are evaluated in sequence.
The value of the last expression is the return value of the
function (or clause).
Corrado Santoro An Introduction to Erlang
Guards
✞fact(0) -> 1;
fact(N) -> N * fact(N - 1).✡✝ ✆
What does it happen if we invoke fact(-3)?
✞fact(-3) = -3 * fact (-4)
fact(-3) = -3 * (-4 * fact (-5))
fact(-3) = -3 * (-4 * (-5 * fact (-6)))
...✡✝ ✆
The execution does not terminate!
Indeed, the program has an error since the value of factorial is
not defined when n < 0.
This check is not present in our program!
Corrado Santoro An Introduction to Erlang
Guards
Let’s take a look at the mathematical formulation
0! = 1
n! = n · (n − 1)! , ∀n > 0
We must add the condition N > 0.
✞fact(0) -> 1;
fact(N) when N > 0 -> N * fact(N - 1).✡✝ ✆
The statement “when + boolean expression” after function (or
clause) declaration is called guard.
A guard activates the clause if and only if the boolean
expression evaluates true.
Corrado Santoro An Introduction to Erlang
Guards
✞fact(0) -> 1;
fact(N) when N > 0 -> N * fact(N - 1).✡✝ ✆
First clause is activated when argument is 0.
Second clause is true when the argument is > 0.
What about fact(-3)?
No clause is met and a run-time error (exception) is raised
“undefined function clause”.
That’s really our aim! A program crash is expected since
the function is not defined for negative arguments.
Corrado Santoro An Introduction to Erlang
An Exercise
Sum of the first N even numbers
sumpair(0) = 0
sumpair(n) = n + sumpair(n − 2) , ∀n > 0 , even
sumpair(n) = sumpair(n − 1) , ∀n > 0 , odd
✞sumpair(0) -> 0;
sumpair(N) when ((N rem 2) == 0) and (N > 0) ->
N + sumpair(N - 2);
sumpair(N) when N > 0 -> sumpair(N - 1).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Lists
An Erlang list is an ordered set of any Erlang term.
It is syntactical expressed as a comma-separated set of
elements, delimited with “[” and “]”.
Example: [7, 9, 15, 44].
“Roughly speaking”, a list is a C array.
However it’s not possible to directly get the i-th element, but we
can separate the first element from the tail of the list.
Using only this operation, the complete manipulation of a list is
possible!
Corrado Santoro An Introduction to Erlang
Lists: separation operator
[ First | Rest ] = List.
First will contain the first element of the list.
Rest will contain the sublist of the element starting from the
second till the end.
Corrado Santoro An Introduction to Erlang
Lists: Examples
[ First | Rest ] = [7, 9, 15, 44].
First = 7
Rest = [9, 15, 44]
[ Head | Tail ] = [16].
Head = 16
Tail = []
[ First | Tail ] = [].
runtime error! It is not possible to get the first element
from an empty list.
Corrado Santoro An Introduction to Erlang
Example: sum of all elements of a list
Mathematical formulation
sum([x1, . . . , xn]) =
n∑
i=1
xi
C Implementation:
✞int sum (int * x, int n) {
int i, res = 0;
for (i = 0;i < n;i++)
res += x[i];
return res;
}✡✝ ✆
Erlang does not have the construct to get the i-th element, nor
the for. We must use recursion!
Corrado Santoro An Introduction to Erlang
Example: sum of all elements of a list
Mathematical formulation with recursion
sum([]) = 0
sum([x1, x2, . . . , xn]) = x1 + sum([x2, . . . , xn])
Let’s do it in Erlang:
✞sum([]) -> 0;
sum([ Head | Tail ]) -> Head + sum(Tail).
%% sum(L) -> [ Head | Tail ] = L, Head + sum(Tail).✡✝ ✆
C solution uses 6 lines of code and 2 local variables.
Erlang solution uses only 2 lines of code!
Corrado Santoro An Introduction to Erlang
List Construction
Operator “|” is also used to build a list.
List = [ FirstElement | TailOfTheList ].
It builds a list by chaining the element FirstElement at with
the list TailOfTheList.
Corrado Santoro An Introduction to Erlang
List Construction: Examples
X = [7 | Y].
Y = [9, 15, 44]
X = [7, 9, 15, 44]
X = [Y | Z].
X = 16 e Z = []
X = [16]
X = [Y | [1, 2, 3]].
X = 16
X = [16, 1, 2, 3]
Corrado Santoro An Introduction to Erlang
Example: double each element of a list
C solution:✞void doubling (int * x, int n) {
int i;
for (i = 0;i < n;i++) x[i] *= 2;
}
✡✝ ✆
Erlang solution:✞doubling([]) -> [];
doubling([ Head | Tail ]) -> [Head * 2 | doubling (Tail)].
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Example: filtering a list
Let’s write a function that, given a list L, returns a new list
containing only the elements of L greater than 10.
Solution 1:✞filter([]) -> [];
filter([ Head | Tail ]) when Head > 10 ->
[Head | filter (Tail)];
filter([ Head | Tail ]) -> filter (Tail).✡✝ ✆
Corrado Santoro An Introduction to Erlang
Filtering an array in C
Can we do the same thing in C?
Yes but... a couple of problems:
We have to return a new array.
We must allocate the new array before filling it.
But we don’t know in advance the size of this resulting
array.
What do we have to do?
Corrado Santoro An Introduction to Erlang
Array filtering in C
✞int * filter (int * x, int n, int * newN) {
int i, j, newSize = 0;
int * result;
/* first, let’s compute the size of the resulting array */
for (i = 0;i < n;i++)
if (x[i] > 10) newSize ++;
/* then let’s allocate the resulting array */
result = malloc (newSize * sizeof (int));
/* finally let’s fill the array */
for (i = 0, j = 0; i < n;i++) {
if (x[i] > 10) {
result[j] = x[i];
j++;
}
}
*newN = newSize;
return result;
}
✡✝ ✆
“Some more lines” w.r.t. Erlang solution.
Corrado Santoro An Introduction to Erlang
Quicksort
The algorithm is quite simple:
1 Starting from a list, we get a cutting element.
2 Two sublists are built: the first contains all elements less
than the cutting element; the second list contains the
elements greater than the cutting element.
3 Steps 1 and 2 are recursively applied to the two sublists.
4 Sublist 1, cutting element and sublist 2 are chained
together.
Corrado Santoro An Introduction to Erlang
Quicksort: a numerical example
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
Corrado Santoro An Introduction to Erlang
Quicksort: a numerical example
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
1 2 4 21 43 9 33 10 8
Corrado Santoro An Introduction to Erlang
Quicksort: a numerical example
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
1 2 4 21 43 9 33 10 8
1 2 4 9 10 8 21 43 33
Corrado Santoro An Introduction to Erlang
Quicksort: a numerical example
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
1 2 4 21 43 9 33 10 8
1 2 4 9 10 8 21 43 33
1 2 4 8 9 10 21 43 33
Corrado Santoro An Introduction to Erlang
Quicksort: a numerical example
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
1 2 4 21 43 9 33 10 8
1 2 4 9 10 8 21 43 33
1 2 4 8 9 10 21 43 33
1 2 4 8 9 10 21 33 43
Corrado Santoro An Introduction to Erlang
Quicksort: implementation
Mathematical formulation
quicksort([ ]) = [ ]
quicksort([x1, x2, . . . , xn]) = quicksort([xi : xi ∈ [x2, . . . , xn], xi < x1])⊕
⊕ [x1]⊕
⊕ quicksort([xi : xi ∈ [x2, . . . , xn], xi >= x1])
4 21 43 2 1 9 33 10 8
2 1 4 21 43 9 33 10 8
1 2 4 21 43 9 33 10 8
1 2 4 9 10 8 21 43 33
Corrado Santoro An Introduction to Erlang
Quicksort: implementation
Mathematical formulation
quicksort([ ]) = [ ]
quicksort([x1, x2, . . . , xn]) = quicksort([xi : xi ∈ [x2, . . . , xn], xi < x1])⊕
⊕ [x1]⊕
⊕ quicksort([xi : xi ∈ [x2, . . . , xn], xi >= x1])
✞quicksort([]) -> [];
quicksort([X1 | L]) ->
quicksort([X || X <- L, X < X1]) ++ [X1] ++
quicksort([X || X <- L, X >= X1]).✡✝ ✆
Here we used the Erlang operator “++” to concatenate two lists.
Corrado Santoro An Introduction to Erlang
Assignment
1 As in classical programming languages, Erlang supports
variables
2 They can “assigned” by using the symbol “=” which
however has the meaning of match
3 As in mathematics, the “=” symbol does not really mean
“assignment” but that the LHS and RHS are the same
(equal)!4 Thus the sequence has the following meaning:
A = 3, variable A has not assigned before (it is unbound),the statement succeeds by assigning 3 to A
A = A + 1, since variable A is now bound, this statement
has the meaning 3 = 3 + 1, which is false!The statement above thus generates a bad matchexception.
Corrado Santoro An Introduction to Erlang
Assignment
A bound variable cannot be thus reused as in classical
imperative language
This seems weird but has some precise reasons:1 The symbol “=” has its own mathematical meaning: equal!2 This semantics makes pattern matching very easy
Example: Ensuring that the first two elements of a list are the
same:✞[H, H | T ] = MyList
✡✝ ✆
Corrado Santoro An Introduction to Erlang
The “list member” function
Matching is very useful in function clause heads
Let’s implement a function which checks if an element
belongs to the list
✞member(X, []) -> false;
member(X, [X | T]) -> true;
member(X, [H | T]) -> member(X, T).
✡✝ ✆
The code above may be rewritten in a more elegant way by
using the “ ” symbol which means “don’t care”
✞member(_, []) -> false;
member(X, [X | _]) -> true;
member(X, [_ | T]) -> member(X, T).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
A step behind: overview of Erlang data types
Integers, the are “big-int”, i.e. integers with no limit
Reals, usually represented in floating-point
atoms, Symbolic constants made of literals starting withlowercase. Example: hello, ciao, thisIsAnAtom.
They are atomic entities; even if they are literals, they
cannot be treated as “classical” C or Java strings. Instead,
they play the role of symbolic constants (like C #define
or Java final).
tuples, ordered sequences of elements representing, in
general, a structured data.
Examples: {item, 10}, {15, 20.3, 33},{x, y, 33, [1, 2, 3]}.
The number of elements is fixed at creation time andcannot be changed. They are something like C structs.
lists
Corrado Santoro An Introduction to Erlang
Optimising Erlang: tail recursion
Let’s go back to the factorial:✞fact(0) -> 1;
fact(N) -> N * fact(N - 1).
✡✝ ✆
It works but has a big problem: recursive function calls create,
each time, an activation frame in the stack, thus causing its
growth.
In order to avoid it, tail recursion and accumulators can be
used:✞fact(N) -> fact(N, 1).
fact(0, Acc) -> Acc;
fact(N, Acc) -> Acc1 = Acc * N, fact(N - 1, Acc1).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Optimising Erlang: tail recursion
✞fact(N) -> fact(N, 1).
fact(0, Acc) -> Acc;
fact(N, Acc) -> Acc1 = Acc * N, fact(N - 1, Acc1).
✡✝ ✆
If the last statement of a clause is a recursive call; and
The recursive call is not included in a mathematical
expression; then
The function is tail recursive and recursion is not
performed using a subroutine call but a go to (i.e. it always
uses the same activation frame)
Corrado Santoro An Introduction to Erlang
Summing list elements with tail recursion
Not tail recursive:✞sum([]) -> 0;
sum([ Head | Tail ]) -> Head + sum(Tail).
✡✝ ✆
Tail recursive:✞sum(L) -> sum(L, 0).
sum([], Acc) -> Acc;
sum([ Head | Tail ], Acc) -> sum(Tail, Head + Acc).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Part III
A Sample Module: Property Lists (or Dictionaries)
Corrado Santoro An Introduction to Erlang
Dictionaries
Let us implement a module which handles a dictionary with data inthe form:
[{key1, value1}, {key2, value2}, ... ]
Exports:
new()→[]
insert(Dict, Key, Value)→NewDict
delete(Dict, Key)→NewDict
member(Dict, Key)→bool
find(Dict, Key)→{ok, Value} | error
Corrado Santoro An Introduction to Erlang
Dictionary
✞-module(dictionary).
-export([new/0, insert/3, delete/2, member/2, find/2]).
new() -> [].
member([], _Key) -> false;
member([{Key, _Value} | _Tail], Key) -> true;
member([ _ | Tail], Key) -> member(Tail, Key).
insert(Dict, Key, Value) ->
M = member(Dict, Key),
if
M -> % The key exists
Dict;
true -> % The key does not exist
[{Key, Value} | Dict]
end.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
The “if” construct
The source code above uses the “if” erlang statement, which has a
semantics and a syntactical construction quite different than that of
imperative languages:if
cond1− >expr1 ;
cond2− >expr2 ;
....
condn− >exprn ;
true− >expr true
end
Conditions may be different and not related to the same
variables;
However conditions can use only variables and booleanexpression but not function invocation;
Corrado Santoro An Introduction to Erlang
The “case” construct
The “case” erlang statement does not suffer of the limitations of the“if” and has a semantics and a syntactical construction quite similar to
that of imperative languages:
case expr1 of
val1− >expr1 ;
val2− >expr2 ;
....
valn− >exprn ;
− >expr default
end
Conditions may be different and not related to the samevariables;
It is more like a case statement.
Corrado Santoro An Introduction to Erlang
Dictionary
✞-module(dictionary).
-export([new/0, insert/3, delete/2, member/2, find/2]).
new() -> [].
member([], _Key) -> false;
member([{Key, _Value} | _Tail], Key) -> true;
member([ _ | Tail], Key) -> member(Tail, Key).
insert(Dict, Key, Value) ->
case member(Dict, Key) of
true -> % The key exists
Dict;
false -> % The key does not exist
[{Key, Value} | Dict]
end.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Dictionary (Part II)
✞-module(dictionary).
-export([new/0, insert/3, delete/2, member/2, find/2]).
...
delete([], _Key) -> [];
delete([{Key, _Value} | Tail], Key) -> delete(Tail, Key);
delete([ Head | Tail], Key) -> [Head | delete(Tail, Key) ].
find([], _Key) -> error;
find([{Key, Value} | _Tail], Key) -> {ok, Value};
find([ _ | Tail], Key) -> find(Tail, Key).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Strings
An Erlang string is a list in which each element is the ASCII code of
the n-th character:The string Hello can be expressed in erlang as:
"Hello"
[72,101,108,108,111]
[$H,$e,$l,$l,$o]
The literal $car is a short-cut for “ASCII code of character car”.
A string can be manipulated as a list:
[Head | Tail] = "Hello"
Head = 72
Tail = "ello"
Corrado Santoro An Introduction to Erlang
Exercise on strings and lists
Write a function:
split(String, SepChar)→ [Token1, Token2, ...]
which separates a string into tokens (returns a list of string) on the
basis of the separation char SepChar.
Corrado Santoro An Introduction to Erlang
Exercise on Strings and Lists✞-module(exercise).
-export([split/2, reverse/1]).
split(String, Sep) -> split(String, Sep, [], []).
split([], _Sep, [], ListAccumulator) ->
reverse(ListAccumulator);
split([], Sep, TokenAccumulator, ListAccumulator) ->
split([], Sep, [], [reverse(TokenAccumulator) | ListAccumulator]);
split([Sep | T], Sep, [], ListAccumulator) ->
split(T, Sep, [], ListAccumulator);
split([Sep | T], Sep, TokenAccumulator, ListAccumulator) ->
split(T, Sep, [], [reverse(TokenAccumulator) | ListAccumulator]);
split([H | T], Sep, TokenAccumulator, ListAccumulator) ->
split(T, Sep, [H | TokenAccumulator], ListAccumulator).
reverse(List) -> reverse(List, []).
reverse([], Acc) -> Acc;
reverse([H | T], Acc) -> reverse(T, [H | Acc]).
✡✝ ✆Corrado Santoro An Introduction to Erlang
Concurrency in Erlang
Traditional approach: pthread
Different execution units sharing data and exploiting
synchronization mechanisms for concurrency control
(semaphores, mutexes, condition variables).
Erlang approach: message passing
Concurrent processes totally separated which share nothing
and interact only with message passing.
Corrado Santoro An Introduction to Erlang
COPL: Concurrency-Oriented Programming
Languages
According to Joe Armstrong’s definition, the COPLs avoid the
semantic gap between a concurrent problem and its
implementation.
Given a problem:
1 identify the concurrent activities.
2 identify the information flow among concurrent activities.
3 implement activities with processes and information with
messages.
No data sharing: no need for (complex) synchronization
constructs, the model features transparency w.r.t. location, in
a distributed environment.
Corrado Santoro An Introduction to Erlang
Erlang Statements for Concurrency
Process creation:
spawn (ModuleName, FunctionName, ListOfParameters).
It returns an Erlang Pid identifying the process.
Sending a message (bang operator):
PID ! DataToSend
{NetworkNode,PID} ! DataToSend
Message reception:
Data = receive X -> X end
Corrado Santoro An Introduction to Erlang
A very simple concurrent program
Let’s write a function that spawns a process, sends a data and
gets back a reply.✞-module(ping_pong).
-export([caller/0, responder/0]).
caller() ->
Pid = spawn(ping_pong, responder, []),
Pid ! {self(), 100},
receive
Reply -> ok
end.
responder() ->
receive
{From, Data} ->
From ! Data + 1
end.✡✝ ✆
Corrado Santoro An Introduction to Erlang
A more complex concurrent program
Parallel Factorial (version for one master + two workers):
let’s compute N!
First worker computes products from 2 to |N2 | and sends back
result to master.
Second worker computes products from |N2 |+ 1 to N and sends
back result to master.
Master multiples the two partial results.
Corrado Santoro An Introduction to Erlang
Parallel Factorial with two workers✞-module(fact_parallel).
-export([fact_parallel/1, fact_worker/3]).
fact_part(To, To) -> To;
fact_part(X, To) -> X * fact_part(X + 1, To).
fact_worker(Master, From, To) ->
PartialResult = fact_part(From, To),
Master ! PartialResult.
fact_parallel(N) ->
Middle = trunc(N / 2),
ProcOneArguments = [self(), 2, Middle],
ProcTwoArguments = [self(), Middle + 1, N],
spawn(fact_parallel, fact_worker, ProcOneArguments),
spawn(fact_parallel, fact_worker, ProcTwoArguments),
receive Result1 -> ok end,
receive Result2 -> ok end,
Result1 * Result2.✡✝ ✆
Corrado Santoro An Introduction to Erlang
Exercise: Parallel Factorial with “m” workers
Let’s compute N!
The interval [2,N] is subdivided into “m” subintervals.
Each worker computes the sub-product of its sub-interval and
sends back result to master.
Master multiples all the partial results.
1 Compute each subinterval [Ai ,Bi ];
2 Spawn the m processes and gather the PIDs into a list;
3 Scan the list and wait, for each PID, the result; gather the
result into a list;
4 Multiply all the results together and return the final number.
Corrado Santoro An Introduction to Erlang
Processes with States
Let us suppose we need, in a Erlang program, a memory
database storing tuples in the form {Key, Value}.
Such a DB must be live and shared by any Erlang process,
so cannot propagate the DB itself in a varaible.
We must use a process holding the DB and handling a
proper set of messsage for interaction with the world.
Messages will correspond to functionalities such as:
Add a new tupleGet all tuples
Search for a tuple given the keyDelete a tuple given the key
...
Each message will be handled in a client/server fashion.
Corrado Santoro An Introduction to Erlang
The DB (Part 1)✞-module(db).
-export([start/0, db_loop/1, add/3, getall/1, get/2]).
start() ->
spawn(db, db_loop, [ [] ]).
db_loop(TheDB) ->
receive
{From, add, Key, Value} ->
case lists:keymember(Key, 1, TheDB) of
true -> % the key exists returns an error
From ! duplicate_key,
db_loop(TheDB);
false -> % the key does not exist, add it
From ! ok,
db_loop( [ {Key, Value} | TheDB ])
end;
{From, getall} ->
From ! TheDB,
db_loop(TheDB);
{From, get, Key} ->
case lists:keyfind(Key, 1, TheDB) of
false -> % key not found
From ! not_found;
{K, V} ->
From ! {K, V}
end,
db_loop(TheDB)
end.
...
✡✝ ✆Corrado Santoro An Introduction to Erlang
The DB (Part 1)
✞...
%% -------------------------------------------------------------------------------
%% API
%% -------------------------------------------------------------------------------
add(Pid, K, V) ->
Pid ! {self(), add, K, V},
receive
Reply -> Reply
end.
getall(Pid) ->
Pid ! {self(), getall},
receive
Reply -> Reply
end.
get(Pid, Key) ->
Pid ! {self(), get, Key},
receive
Reply -> Reply
end.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Distributed Erlang Systems
An Erlang distributed system is composed of a set of erlang
nodes.
An Erlang Node is characterised by:
A live erlang virtual machine
A fully qualified node name, made of a user-defined part
and the IP address/name of the host, in the form
’name@IP’ (it is an atom)
A cookie, i.e. a specific data which must be the same in all
nodes of the system and is used for security reasons.
Corrado Santoro An Introduction to Erlang
Distributed Ping-pong
Let’s write a ping-pong program working in distributed Erlang.✞-module(d_ping_pong).
-export([start_responder/0, responder/0, ping/2, stop/1]).
start_responder() ->
Pid = spawn(d_ping_pong, responder, []),
register(responder, Pid).
responder() ->
receive
{From, Data} ->
From ! Data + 1,
responder();
bye ->
ok
end.
ping(Node, Data) ->
{responder, Node} ! {self(), Data},
receive
Reply -> Reply
end.
stop(Node) ->
{responder, Node} ! bye.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Testing the example
✞$ erl -name [email protected] -setcookie mycookie
Erlang R16B03 (erts-5.10.4) [source] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.4 (abort with ˆG)
([email protected])1> net_adm:ping(’[email protected]’).
pong
([email protected])2> d_ping_pong:start_responder().
true
✡✝ ✆
✞$ erl -name [email protected] -setcookie mycookie
Erlang R16B03 (erts-5.10.4) [source] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.4 (abort with ˆG)
([email protected])1> d_ping_pong:ping(’[email protected]’, 10).
11
([email protected])2> d_ping_pong:ping(’[email protected]’, 44).
45
([email protected])3> d_ping_pong:stop(’[email protected]’).
bye
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Linked Processes
If a process is created using the spawn link function, an
internal link is created between the creating and the
created processes.
Links have a specific role during process termination and
are used in fault handling
Links also work in distributed Erlang and can be also made
between processes belonging to different nodes.
The link function can be used to make a link to an
already existing process.
Corrado Santoro An Introduction to Erlang
Linked Processes: Normal Termination
If a process normally terminates, nothing happens to
linked processes
Corrado Santoro An Introduction to Erlang
Linked Processes: Termination with Errors
If a process terminates with an error, but ...
... linked processes do not trap exit signal (normal
behaviour), then
linked processes will terminate as well (cascade
termination).
Corrado Santoro An Introduction to Erlang
Linked Processes: Termination with Errors
If a process terminates with an error, and ...
... linked processes (proc2) issued a call to
process flag(trap exit, true), then
linked processes will receive an EXIT message than can
be handled properly.
Corrado Santoro An Introduction to Erlang
Linked Processes: an Example
✞-module(test_link).
-export([start/0, one/0, two/0, stop/0]).
start() ->
Pid = spawn(test_link, one, []),
register(one, Pid).
one() ->
Pid = spawn_link(test_link, two, []),
register(two, Pid),
one_loop().
one_loop() ->
io:format("One\n"),
receive
bye ->
ok % exit(normal) or exit(error)
after 1000 ->
one_loop()
end.
...
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Linked Processes: an Example (2)
✞...
two() ->
%process_flag(trap_exit,true),
two_loop().
two_loop() ->
io:format("Two\n"),
receive
Msg ->
io:format("Received ˜p\n", [Msg])
after 1000 ->
two_loop()
end.
stop() ->
one ! bye.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
An Example: A supervisor
Let us write a simple supervisor
It manages a list of processes to be controlled, given in the
form: {Pid, Module, Function, Arguments}
Each time a processes crashes, the supervisor performs a
restart
Two API functions are given:
add(M, F, A), adds a new process given the module, the
function and the arguments;
get proc list(), returns the list of running processesmanaged by the supervisor.
Corrado Santoro An Introduction to Erlang
Supervisor (Part 1)
✞%%
%% sup.erl
%%
-module(sup).
-export([start/0, sup_starter/0, add/3, get_proc_list/0]).
start() ->
Pid = spawn(sup, sup_starter, []),
register(sup, Pid).
sup_starter() ->
process_flag(trap_exit, true),
sup_loop([]).
...
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisor (Part 2)
✞...
sup_starter() ->
process_flag(trap_exit, true),
sup_loop([]).
sup_loop(ListOfProcesses) ->
receive
{From, proc_list} ->
From ! ListOfProcesses,
sup_loop(ListOfProcesses);
{From, add_proc, Module, Function, Arguments} ->
Pid = spawn_link(Module, Function, Arguments),
sup_loop( [ {Pid, Module, Function, Arguments} | ListOfProcesses]);
{’EXIT’, Pid, Reason} ->
io:format("PID ˜p Terminated with ˜p\n", [Pid, Reason]),
case find_process(Pid, ListOfProcesses) of
{ok, M, F, A} ->
io:format("Restarting ˜p:˜p\n", [M, F]),
NewPid = spawn_link(M, F, A),
sup_loop(pid_replace(Pid, NewPid, ListOfProcesses));
error ->
io:format("PID not found in list!\n"),
sup_loop(ListOfProcesses)
end
end.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisor (Part 3)
✞%% -------------------------------------------------------------------------------
%% API
%% -------------------------------------------------------------------------------
add(M, F, A) ->
sup ! {self(), add_proc, M, F, A},
ok.
get_proc_list() ->
sup ! {self(), proc_list},
receive
ProcList -> ProcList
end.
%% --------------------------------------------------------------------------------
%% Internal functions
%% -------------------------------------------------------------------------------
find_process(_Pid, []) -> error;
find_process(Pid, [{Pid, M, F, A} | _Tail]) -> {ok, M, F, A};
find_process(Pid, [{_OtherPid, _M, _F, _A} | Tail]) -> find_process(Pid, Tail).
pid_replace(_Pid, _NewPid, []) -> [];
pid_replace(Pid, NewPid, [{Pid, M, F, A} | Tail]) -> [ {NewPid, M, F, A} | Tail];
pid_replace(Pid, NewPid, [{OtherPid, M, F, A} | Tail]) ->
[ {OtherPid, M, F, A} | pid_replace(Pid, NewPid, Tail) ].
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisor (Part 4)
✞-module(p).
-export([p1/0, p2/1]).
p1() ->
io:format("One!\n"),
timer:sleep(1000),
p1().
p2(X) when X > 10 ->
B = 0, X / B;
%% provoke a ’badartih’ error
p2(X) ->
io:format("Two: ˜p\n", [X]),
timer:sleep(1000),
p2(X+1).
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisor (Part 5)✞corrado@Corrado-1215P:˜/didattica/MasterCloud/erlang/software$ erl
Erlang R16B03 (erts-5.10.4) [source] [smp:4:4] [async-threads:10] [kernel-poll:false]
Eshell V5.10.4 (abort with ˆG)
1> sup:start().
true
2> sup:add(p, p1, []).
ok
One!
3> sup:add(p, p2, [ 5 ]).
Two: 5
ok
One!
Two: 6
One!
Two: 7
One!
Two: 8
One!
Two: 9
One!
Two: 10
One!
PID <0.39.0> Terminated with {badarith,[{p,p2,1,[{file,"p.erl"},{line,11}]}]}
Restarting p:p2
Two: 5
4>
=ERROR REPORT==== 24-Mar-2015::10:39:55 ===
Error in process <0.39.0> with exit value: {badarith,[{p,p2,1,[{file,"p.erl"},{line,11}]}]}
One!
Two: 6
One!
Two: 7
One!
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Erlang Common Process Models
An Erlang system is a set of interacting processes
The way in which such processes interact is often conformto few precise models:
client/server: the process waits for a request message,handles it and sends a reply;
one way: the process waits for a request message,
handles it but does not send any reply;finite-state-machine: the process evolves according to a
finite-state machine in which events are the reception of
specific messages and/or timeouts;
Corrado Santoro An Introduction to Erlang
Erlang Common Process Models
In general, the code of an Erlang module handling aprocess is composed of the following parts:
A “start” function which spawns the process by running
its intialization function;An initialization function which perform process startup
operation and then runs the main message reception loop;
A message handler, it is in general the same process loopfunction, which waits for a message, recognises it, executes
the proper operations, sends the reply (if needed), andre-enters the loop;
A process state, made of a piece of information which is
passed through the main loop function (with updates ifneeded);
An API, made of some functions which interacts with the
process by sending the proper messages.
Corrado Santoro An Introduction to Erlang
The Message Handler of a Process
A message handler
waits for a message;
parses the message (a message is in general composed bya From field and some specific parts which depend by the
functionality carried by the message);handles the message by executing the proper code;
sends a reply (if needed)
Parts in blue are generic and common to all source codes
for Erlang processes
Parts in red are specific for each kind of Erlang process
The Erlang/OTP platform provides ready-to-use
behaviours, which are library modules implementing the
generic part, and they are designed for implementing
well-defined process patterns
Corrado Santoro An Introduction to Erlang
gen server
gen server is a module for the implementation of
processes behaving according to the client/server pattern
The programmer has to write API function and callback
functions to handle the messages (specific parts)
The part regarding message reception, parsing and reply
sending is managed directly by the module
The module also supports the protocol for the
interoperability with supervisors, for the implementation of
fault-tolerant systems.
Corrado Santoro An Introduction to Erlang
Starting a gen server
A gen server is started through a call to one of the functions:
start(Module, Args, Options) − > Result
start link(Module, Args, Options) − > Result
start(ServerName, Module, Args, Options) − >
Result
start link(ServerName, Module, Args,
Options) − > Result
Here:
Module is the name of the callback moduleArgs is the list of arguments to be passed to the init
callback function
ServerName is the name of the process, if it has to beregistered
Options are some process-specific options
The reply is {ok, Pid} or {error, ErrorReason}
Corrado Santoro An Introduction to Erlang
Starting a gen server
As a result, the callback function Module:init(Args) iscalled, whose result can be
{ok, State}{ok, State, Timeout}{stop, Reason}
Corrado Santoro An Introduction to Erlang
The Database Example with gen server
✞-module(database).
%% this module implements a gen_server behaviour
-behaviour(gen_server).
-export([start/0, init/1, .... ]).
start() ->
%% Module is ’database’ (the same),
%% No arguments
%% No options
gen_server:start_link(database, [], []).
init(Args) ->
{ok, []}. %% start with an empty database
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Managing calls in a gen server
To call a gen server “service” one of the following functions can
be used:
call(Pid, Request) − > Reply
call(Pid, Request, Timeout) − > Reply
Here:
Pid is the process pid or name
Request is an Erlang term which represents the requestTimeout is an optional timeout value, in milliseconds
Corrado Santoro An Introduction to Erlang
Managing calls in a gen server
As a result, the callback function
Module:handle call(Request, From, State) is called,where
Request is the request
From is the sender process
State is the server process state
The result of the callback can be:
{reply,Reply,NewState}, a reply is sent and a new
process state is set
{stop,Reason,Reply, NewState}, a reply is sent, butthe server is going to be stopped
Corrado Santoro An Introduction to Erlang
The Database Example with gen server
✞-module(database).
-behaviour(gen_server).
-export([start/0, add/3, getall/1, get/2, init/1, handle_call/3]).
start() ->
gen_server:start_link(database, [], []).
%% -------------------------------------------------------------------------------
%% API
%% -------------------------------------------------------------------------------
add(Pid, K, V) -> gen_server:call(Pid, {add, K, V}).
getall(Pid) -> gen_server:call(Pid, {getall}).
get(Pid, Key) -> gen_server:call(Pid, {get, Key}).
...
✡✝ ✆
Corrado Santoro An Introduction to Erlang
The Database Example with gen server
✞...
%% -------------------------------------------------------------------------------
%% CALLBACKS
%% -------------------------------------------------------------------------------
init(Args) -> {ok, []}. %% start with an empty database
handle_call({add, Key, Value}, _From, TheDB) ->
case lists:keymember(Key, 1, TheDB) of
true -> % the key exists returns an error
{reply, duplicate_key, TheDB};
false -> % the key does not exist, add it
{reply, ok, [ {Key, Value} | TheDB ]}
end;
handle_call({getall}, _From, TheDB) -> {reply, TheDB, TheDB};
handle_call({get, Key},_From, TheDB) ->
case lists:keyfind(Key, 1, TheDB) of
false -> % key not found
{reply, not_found, TheDB};
{K, V} ->
{reply, {K, V}, TheDB}
end.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
A “Registered” Database (with “stop” API)
✞-module(reg_database).
-behaviour(gen_server).
-export([start/0, add/2, getall/0, get/1, stop/0, init/1, handle_call/3, terminate/2]).
-define(SERVER_NAME, mydb).
start() ->
gen_server:start_link({local, ?SERVER_NAME}, ?MODULE, [], []).
%% -------------------------------------------------------------------------------
%% API
%% -------------------------------------------------------------------------------
add(K, V) -> gen_server:call(?SERVER_NAME, {add, K, V}).
getall() -> gen_server:call(?SERVER_NAME, {getall}).
get(Key) -> gen_server:call(?SERVER_NAME, {get, Key}).
stop() -> gen_server:call(?SERVER_NAME, {stop}).
%% -------------------------------------------------------------------------------
%% CALLBACKS
%% -------------------------------------------------------------------------------
...
handle_call({stop}, _From, TheDB) -> {stop, normal, ok, TheDB};
...
terminate(Reason,State) -> io:format("DB is terminating\n").
✡✝ ✆
Corrado Santoro An Introduction to Erlang
gen event
gen server is a module for the implementation of
processes behaving as a finite-state machine
Example, a fixed-phone line fsm (from Cesarini,
Thompson, “Erlang Programming”):
Corrado Santoro An Introduction to Erlang
Starting a gen fsm
A gen fsm is started through a call to one of the functions:
start(Module, Args, Options) − > Result
start link(Module, Args, Options) − > Result
start(ServerName, Module, Args, Options) − >
Result
start link(ServerName, Module, Args,
Options) − > Result
Here:
Module is the name of the callback moduleArgs is the list of arguments to be passed to the init
callback function
ServerName is the name of the process, if it has to beregistered
Options are some process-specific options
The reply is {ok, Pid} or {error, ErrorReason}
Corrado Santoro An Introduction to Erlang
Starting a gen fsm
As a result, the callback function Module:init(Args) iscalled, whose result can be
{ok, StateName, StateData}{ok, StateName, StateData, Timeout}{stop, Reason}
StateName is an atom which represents the state of the fsm
StateData is the process data
Corrado Santoro An Introduction to Erlang
Handling Events a gen fsm
To generate an event, the following function is used:
send event(Pid, Event) − > ok
Here:
Pid is the process pid or name
Event is an atom which represents the event
As a result, the callback function Module:StateName(Event,
StateData) is called, where
StateName is the state handling the event
Event is the event to be handledStateData is the server process state
The result of the callback can be:
{next state, NextStateName, NextStateData}{next state, NextStateName, NextStateData,
TimeoutVal}
Corrado Santoro An Introduction to Erlang
The gen fsm of the fixed-line phone
✞-module(phone).
-behaviour(gen_fsm).
-export([start/0,
incoming/0, off_hook/0, on_hook/0, other_on_hook/0, connect/0,
init/1,
idle/2, ringing/2, connected/2, dial/2]).
-define(SERVER_NAME, phone).
start() ->
gen_fsm:start_link({local, ?SERVER_NAME}, ?MODULE, [], []).
%% -------------------------------------------------------------------------------
%% API
%% -------------------------------------------------------------------------------
incoming() -> gen_fsm:send_event(?SERVER_NAME, incoming).
off_hook() -> gen_fsm:send_event(?SERVER_NAME, off_hook).
on_hook() -> gen_fsm:send_event(?SERVER_NAME, on_hook).
other_on_hook() -> gen_fsm:send_event(?SERVER_NAME, other_on_hook).
connect() -> gen_fsm:send_event(?SERVER_NAME, connect).
...
✡✝ ✆
Corrado Santoro An Introduction to Erlang
The gen fsm of the fixed-line phone
✞...
%% -------------------------------------------------------------------------------
%% CALLBACKS
%% -------------------------------------------------------------------------------
init(Args) -> {ok, idle, []}.
idle(incoming, StateData) ->
io:format("Incoming call\n"),
{next_state, ringing, StateData};
idle(off_hook, StateData) ->
io:format("The user is making a call\n"),
{next_state, dialing, StateData}.
ringing(other_on_hook, StateData) ->
io:format("The peer closed the call\n"),
{next_state, idle, StateData};
ringing(off_hook, StateData) ->
io:format("We answered the call\n"),
{next_state, connected, StateData}.
connected(on_hook, StateData) ->
io:format("The call terminated\n"),
{next_state, idle, StateData}.
dial(on_hook, StateData) ->
io:format("The call terminated\n"),
{next_state, idle, StateData};
dial(connect, StateData) ->
io:format("The peer answered the call\n"),
{next_state, connected, StateData}.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisors
The Erlang library provides a library module for theimplementation of supervisors
A supervisor can monitor processes compliant to OTP and thusimplemented as gen server, gen fsm or gen event.
A supervisor needs a callback module which has the task of:
Performing initialization tasks, if neededSpecifying which are the children processes and their
restart policy
Corrado Santoro An Introduction to Erlang
Starting a supervisor
A supervisor is started through a call to one of the functions:
start link(Module, Args, Options) − > Result
start link(ServerName, Module, Args,
Options) − > Result
Here:
Module is the name of the callback module
Args is the list of arguments to be passed to the init
callback functionServerName is the name of the process, if it has to be
registeredOptions are some process-specific options
The reply is {ok, Pid} or {error, ErrorReason}
Corrado Santoro An Introduction to Erlang
Starting a supervisor
As a result, the callback function Module:init(Args) iscalled, whose result can be
{ok, SupervisorSpecification,
ChildSpecificationList}
SupervisorSpecification is a tuple specifying restart
policy
ChildSpecificationList is the list of children processes
Corrado Santoro An Introduction to Erlang
Starting a supervisor
SupervisorSpecification = {RestartStrategy,AllowedRestarts, MaxSeconds}
RestartStrategy:
one for one, the crashed child is restartedone for all, if a child crahses, all children are terminated
and restartedrest for one, if a child X crahses, all children are started
after X will be terminated and restarted
AllowedRestarts is the maximum number of abnormal
termination allowed in MaxSeconds seconds; these two
parameters specify the maximum abnormal termination/restartfrequency.
Corrado Santoro An Introduction to Erlang
Starting a supervisor
ChildSpecificationList = [{Id, {Mod, Fun, Args},Restart, Shutdown, Type, ModuleList}]
Id, an id (atom) assigned to the child
{Mod, Fun, Args}, the specification of starting function of the
child module, with its args
Restart is the restart policy which can be transient,
temporary or permanent
Shutdown is the maximum time allowed between a terminationcommand and the execution of terminate function
Type is the process type, it can be worker or supervisor
ModuleList is the list of modules implementing the process;
this information is used during a software upgrade
Corrado Santoro An Introduction to Erlang
The supervisor for mydb and phone
✞-module(mysup).
-behaviour(supervisor).
-export([start/0, init/1]).
start() ->
supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init(_Args) ->
SupSpecs = {one_for_one, 10, 1},
Child1 = {one, {reg_database, start, []}, permanent, 1000, worker, [reg_database]},
Child2 = {two, {phone, start, []}, permanent, 1000, worker, [phone] },
Children = [ Child1, Child2 ],
{ok, {SupSpecs, Children}}.
✡✝ ✆
Corrado Santoro An Introduction to Erlang