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 principles
Basic syntax
Basic recursion and list usage
2 Data types, Matching and Advanced Recursion:
Variables and assignment principles
Erlang 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
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
Part II
Data types, Matching and Advanced Recursion
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 match
exception.
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 with
lowercase. 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 and
cannot 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 in
the 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 boolean
expression 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 same
variables;
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
Part IV
Concurrent Programming in Erlang
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 |N
2 | and sends back
result to master.
Second worker computes products from |N
2 | + 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 tuple
Get all tuples
Search for a tuple given the key
Delete 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
Part V
Distributed Programming in Erlang
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 pluto@127.0.0.1 -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)
(pluto@127.0.0.1)1> net_adm:ping(’pippo@127.0.0.1’).
pong
(pluto@127.0.0.1)2> d_ping_pong:start_responder().
true
✡✝ ✆
✞
$ erl -name pippo@127.0.0.1 -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)
(pippo@127.0.0.1)1> d_ping_pong:ping(’pluto@127.0.0.1’, 10).
11
(pippo@127.0.0.1)2> d_ping_pong:ping(’pluto@127.0.0.1’, 44).
45
(pippo@127.0.0.1)3> d_ping_pong:stop(’pluto@127.0.0.1’).
bye
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Part VI
Linked Processes
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("Onen"),
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("Twon"),
receive
Msg ->
io:format("Received ˜pn", [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 processes
managed 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 ˜pn", [Pid, Reason]),
case find_process(Pid, ListOfProcesses) of
{ok, M, F, A} ->
io:format("Restarting ˜p:˜pn", [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: ˜pn", [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 Corrado Santoro An Introduction to Erlang
Part VII
Erlang Design Patterns
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 conform
to 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 a
process 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 loop
function, which waits for a message, recognises it, executes
the proper operations, sends the reply (if needed), and
re-enters the loop;
A process state, made of a piece of information which is
passed through the main loop function (with updates if
needed);
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 by
a 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
gen server
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 module
Args is the list of arguments to be passed to the init
callback function
ServerName is the name of the process, if it has to be
registered
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) is
called, 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 request
Timeout 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, but
the 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 terminatingn").
✡✝ ✆
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 module
Args is the list of arguments to be passed to the init
callback function
ServerName is the name of the process, if it has to be
registered
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) is
called, 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 handled
StateData 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 calln"),
{next_state, ringing, StateData};
idle(off_hook, StateData) ->
io:format("The user is making a calln"),
{next_state, dialing, StateData}.
ringing(other_on_hook, StateData) ->
io:format("The peer closed the calln"),
{next_state, idle, StateData};
ringing(off_hook, StateData) ->
io:format("We answered the calln"),
{next_state, connected, StateData}.
connected(on_hook, StateData) ->
io:format("The call terminatedn"),
{next_state, idle, StateData}.
dial(on_hook, StateData) ->
io:format("The call terminatedn"),
{next_state, idle, StateData};
dial(connect, StateData) ->
io:format("The peer answered the calln"),
{next_state, connected, StateData}.
✡✝ ✆
Corrado Santoro An Introduction to Erlang
Supervisors
The Erlang library provides a library module for the
implementation of supervisors
A supervisor can monitor processes compliant to OTP and thus
implemented as gen server, gen fsm or gen event.
A supervisor needs a callback module which has the task of:
Performing initialization tasks, if needed
Specifying 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 function
ServerName is the name of the process, if it has to be
registered
Options 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) is
called, 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 restarted
one for all, if a child crahses, all children are terminated
and restarted
rest 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/restart
frequency.
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 termination
command 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
An Introduction to Erlang
Corrado Santoro
Dipartimento di Matematica e Informatica
Universita’ di Catania
Master Cloud P.A.
Corrado Santoro An Introduction to Erlang

Introduction to Erlang

  • 1.
    An Introduction toErlang Corrado Santoro Dipartimento di Matematica e Informatica Universita’ di Catania Master Cloud P.A. Corrado Santoro An Introduction to Erlang
  • 2.
    Overview 1 Erlang Basics: Functionprogramming principles Basic syntax Basic recursion and list usage 2 Data types, Matching and Advanced Recursion: Variables and assignment principles Erlang data types Using tail-recursion Corrado Santoro An Introduction to Erlang
  • 3.
    The Erlang Language Definition Erlangis 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
  • 4.
    Erlang and FunctionalProgramming 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
  • 5.
    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
  • 6.
    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
  • 7.
    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
  • 8.
    Erlang: first syntacticalelements ✞ 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
  • 9.
    Erlang: first syntacticalelements ✞ 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
  • 10.
    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
  • 11.
    Guards Let’s take alook 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
  • 12.
    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
  • 13.
    An Exercise Sum ofthe 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
  • 14.
    Lists An Erlang listis 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
  • 15.
    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
  • 16.
    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
  • 17.
    Example: sum ofall 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
  • 18.
    Example: sum ofall 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
  • 19.
    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
  • 20.
    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
  • 21.
    Example: double eachelement 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
  • 22.
    Example: filtering alist 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
  • 23.
    Filtering an arrayin 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
  • 24.
    Array filtering inC ✞ 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
  • 25.
    Quicksort The algorithm isquite 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
  • 26.
    Quicksort: a numericalexample 4 21 43 2 1 9 33 10 8 Corrado Santoro An Introduction to Erlang
  • 27.
    Quicksort: a numericalexample 4 21 43 2 1 9 33 10 8 2 1 4 21 43 9 33 10 8 Corrado Santoro An Introduction to Erlang
  • 28.
    Quicksort: a numericalexample 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
  • 29.
    Quicksort: a numericalexample 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
  • 30.
    Quicksort: a numericalexample 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
  • 31.
    Quicksort: a numericalexample 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
  • 32.
    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
  • 33.
    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
  • 34.
    Part II Data types,Matching and Advanced Recursion Corrado Santoro An Introduction to Erlang
  • 35.
    Assignment 1 As inclassical 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 match exception. Corrado Santoro An Introduction to Erlang
  • 36.
    Assignment A bound variablecannot 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
  • 37.
    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
  • 38.
    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 with lowercase. 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 and cannot be changed. They are something like C structs. lists Corrado Santoro An Introduction to Erlang
  • 39.
    Optimising Erlang: tailrecursion 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
  • 40.
    Optimising Erlang: tailrecursion ✞ 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
  • 41.
    Summing list elementswith 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
  • 42.
    Part III A SampleModule: Property Lists (or Dictionaries) Corrado Santoro An Introduction to Erlang
  • 43.
    Dictionaries Let us implementa module which handles a dictionary with data in the 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
  • 44.
    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
  • 45.
    The “if” construct Thesource 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 boolean expression but not function invocation; Corrado Santoro An Introduction to Erlang
  • 46.
    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 same variables; It is more like a case statement. Corrado Santoro An Introduction to Erlang
  • 47.
    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
  • 48.
    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
  • 49.
    Strings An Erlang stringis 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
  • 50.
    Exercise on stringsand 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
  • 51.
    Exercise on Stringsand 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
  • 52.
    Part IV Concurrent Programmingin Erlang Corrado Santoro An Introduction to Erlang
  • 53.
    Concurrency in Erlang Traditionalapproach: 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
  • 54.
    COPL: Concurrency-Oriented Programming Languages Accordingto 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
  • 55.
    Erlang Statements forConcurrency 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
  • 56.
    A very simpleconcurrent 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
  • 57.
    A more complexconcurrent program Parallel Factorial (version for one master + two workers): let’s compute N! First worker computes products from 2 to |N 2 | and sends back result to master. Second worker computes products from |N 2 | + 1 to N and sends back result to master. Master multiples the two partial results. Corrado Santoro An Introduction to Erlang
  • 58.
    Parallel Factorial withtwo 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
  • 59.
    Exercise: Parallel Factorialwith “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
  • 60.
    Processes with States Letus 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 tuple Get all tuples Search for a tuple given the key Delete a tuple given the key ... Each message will be handled in a client/server fashion. Corrado Santoro An Introduction to Erlang
  • 61.
    The DB (Part1) ✞ -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
  • 62.
    The DB (Part1) ✞ ... %% ------------------------------------------------------------------------------- %% 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
  • 63.
    Part V Distributed Programmingin Erlang Corrado Santoro An Introduction to Erlang
  • 64.
    Distributed Erlang Systems AnErlang 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
  • 65.
    Distributed Ping-pong Let’s writea 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
  • 66.
    Testing the example ✞ $erl -name pluto@127.0.0.1 -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) (pluto@127.0.0.1)1> net_adm:ping(’pippo@127.0.0.1’). pong (pluto@127.0.0.1)2> d_ping_pong:start_responder(). true ✡✝ ✆ ✞ $ erl -name pippo@127.0.0.1 -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) (pippo@127.0.0.1)1> d_ping_pong:ping(’pluto@127.0.0.1’, 10). 11 (pippo@127.0.0.1)2> d_ping_pong:ping(’pluto@127.0.0.1’, 44). 45 (pippo@127.0.0.1)3> d_ping_pong:stop(’pluto@127.0.0.1’). bye ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 67.
    Part VI Linked Processes CorradoSantoro An Introduction to Erlang
  • 68.
    Linked Processes If aprocess 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
  • 69.
    Linked Processes: NormalTermination If a process normally terminates, nothing happens to linked processes Corrado Santoro An Introduction to Erlang
  • 70.
    Linked Processes: Terminationwith 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
  • 71.
    Linked Processes: Terminationwith 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
  • 72.
    Linked Processes: anExample ✞ -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("Onen"), receive bye -> ok % exit(normal) or exit(error) after 1000 -> one_loop() end. ... ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 73.
    Linked Processes: anExample (2) ✞ ... two() -> %process_flag(trap_exit,true), two_loop(). two_loop() -> io:format("Twon"), receive Msg -> io:format("Received ˜pn", [Msg]) after 1000 -> two_loop() end. stop() -> one ! bye. ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 74.
    An Example: Asupervisor 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 processes managed by the supervisor. Corrado Santoro An Introduction to Erlang
  • 75.
    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
  • 76.
    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 ˜pn", [Pid, Reason]), case find_process(Pid, ListOfProcesses) of {ok, M, F, A} -> io:format("Restarting ˜p:˜pn", [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
  • 77.
    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
  • 78.
    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: ˜pn", [X]), timer:sleep(1000), p2(X+1). ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 79.
    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 Corrado Santoro An Introduction to Erlang
  • 80.
    Part VII Erlang DesignPatterns Corrado Santoro An Introduction to Erlang
  • 81.
    Erlang Common ProcessModels An Erlang system is a set of interacting processes The way in which such processes interact is often conform to 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
  • 82.
    Erlang Common ProcessModels In general, the code of an Erlang module handling a process 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 loop function, which waits for a message, recognises it, executes the proper operations, sends the reply (if needed), and re-enters the loop; A process state, made of a piece of information which is passed through the main loop function (with updates if needed); An API, made of some functions which interacts with the process by sending the proper messages. Corrado Santoro An Introduction to Erlang
  • 83.
    The Message Handlerof a Process A message handler waits for a message; parses the message (a message is in general composed by a 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
  • 84.
    gen server gen serveris 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
  • 85.
    gen server Corrado SantoroAn Introduction to Erlang
  • 86.
    Starting a genserver 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 module Args is the list of arguments to be passed to the init callback function ServerName is the name of the process, if it has to be registered Options are some process-specific options The reply is {ok, Pid} or {error, ErrorReason} Corrado Santoro An Introduction to Erlang
  • 87.
    Starting a genserver As a result, the callback function Module:init(Args) is called, whose result can be {ok, State} {ok, State, Timeout} {stop, Reason} Corrado Santoro An Introduction to Erlang
  • 88.
    The Database Examplewith 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
  • 89.
    Managing calls ina 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 request Timeout is an optional timeout value, in milliseconds Corrado Santoro An Introduction to Erlang
  • 90.
    Managing calls ina 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, but the server is going to be stopped Corrado Santoro An Introduction to Erlang
  • 91.
    The Database Examplewith 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
  • 92.
    The Database Examplewith 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
  • 93.
    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 terminatingn"). ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 94.
    gen event gen serveris 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
  • 95.
    Starting a genfsm 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 module Args is the list of arguments to be passed to the init callback function ServerName is the name of the process, if it has to be registered Options are some process-specific options The reply is {ok, Pid} or {error, ErrorReason} Corrado Santoro An Introduction to Erlang
  • 96.
    Starting a genfsm As a result, the callback function Module:init(Args) is called, 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
  • 97.
    Handling Events agen 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 handled StateData 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
  • 98.
    The gen fsmof 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
  • 99.
    The gen fsmof the fixed-line phone ✞ ... %% ------------------------------------------------------------------------------- %% CALLBACKS %% ------------------------------------------------------------------------------- init(Args) -> {ok, idle, []}. idle(incoming, StateData) -> io:format("Incoming calln"), {next_state, ringing, StateData}; idle(off_hook, StateData) -> io:format("The user is making a calln"), {next_state, dialing, StateData}. ringing(other_on_hook, StateData) -> io:format("The peer closed the calln"), {next_state, idle, StateData}; ringing(off_hook, StateData) -> io:format("We answered the calln"), {next_state, connected, StateData}. connected(on_hook, StateData) -> io:format("The call terminatedn"), {next_state, idle, StateData}. dial(on_hook, StateData) -> io:format("The call terminatedn"), {next_state, idle, StateData}; dial(connect, StateData) -> io:format("The peer answered the calln"), {next_state, connected, StateData}. ✡✝ ✆ Corrado Santoro An Introduction to Erlang
  • 100.
    Supervisors The Erlang libraryprovides a library module for the implementation of supervisors A supervisor can monitor processes compliant to OTP and thus implemented as gen server, gen fsm or gen event. A supervisor needs a callback module which has the task of: Performing initialization tasks, if needed Specifying which are the children processes and their restart policy Corrado Santoro An Introduction to Erlang
  • 101.
    Starting a supervisor Asupervisor 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 function ServerName is the name of the process, if it has to be registered Options are some process-specific options The reply is {ok, Pid} or {error, ErrorReason} Corrado Santoro An Introduction to Erlang
  • 102.
    Starting a supervisor Asa result, the callback function Module:init(Args) is called, 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
  • 103.
    Starting a supervisor SupervisorSpecification= {RestartStrategy, AllowedRestarts, MaxSeconds} RestartStrategy: one for one, the crashed child is restarted one for all, if a child crahses, all children are terminated and restarted rest 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/restart frequency. Corrado Santoro An Introduction to Erlang
  • 104.
    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 termination command 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
  • 105.
    The supervisor formydb 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
  • 106.
    An Introduction toErlang Corrado Santoro Dipartimento di Matematica e Informatica Universita’ di Catania Master Cloud P.A. Corrado Santoro An Introduction to Erlang