106
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

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

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 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

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 |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

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 [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

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("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

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 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

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 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

An Introduction to Erlang

Corrado Santoro

Dipartimento di Matematica e Informatica

Universita’ di Catania

Master Cloud P.A.

Corrado Santoro An Introduction to Erlang