0% found this document useful (0 votes)
8 views31 pages

Solutions

The document outlines various topics related to Erlang programming, including system startup, basic syntax, sequential and concurrent programming, process design patterns, error handling, and advanced topics. It includes specific examples such as temperature conversion, list manipulation, and database handling. Each section provides code snippets and specifications for functions, demonstrating practical applications of Erlang concepts.

Uploaded by

toniooo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
8 views31 pages

Solutions

The document outlines various topics related to Erlang programming, including system startup, basic syntax, sequential and concurrent programming, process design patterns, error handling, and advanced topics. It includes specific examples such as temperature conversion, list manipulation, and database handling. Each section provides code snippets and specifications for functions, demonstrating practical applications of Erlang concepts.

Uploaded by

toniooo
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd

Contents

1 Starting the System and Basic Erlang 2


1.4 Temperature Conversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.5 Simple Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2

2 Sequential Programming 4
2.1 Evaluating Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.2 Creating Lists . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
2.3 Side Effects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.4 Database Handling Using Lists . . . . . . . . . . . . . . . . . . . . . . . . . 5
2.5 ADVANCED: Manipulating Lists . . . . . . . . . . . . . . . . . . . . . . . . 6
2.6 ADVANCED: Implement Quicksort and Merge Sort . . . . . . . . . . . . . 7
2.7 ADVANCED: Database Handling using Trees . . . . . . . . . . . . . . . . . 8

3 Concurrent Programming 10
3.1 An Echo Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.2 The Process Ring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
3.3 The Process Crossring . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

4 Process Design Patterns 14


4.1 A Database Server . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
4.2 A Mutex Semaphore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15
4.3 ADVANCED: A Database Server with transactions . . . . . . . . . . . . . . 16

5 Process Error Handling 20


5.1 The Linked Ping Pong Server . . . . . . . . . . . . . . . . . . . . . . . . . . 20
5.2 Trapping Exits . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
5.3 A Reliable Mutex Semaphore . . . . . . . . . . . . . . . . . . . . . . . . . . 22
5.5 ADVANCED: A Supervisor Process . . . . . . . . . . . . . . . . . . . . . . . 23

6 Records and Funs 25


6.1 Database Handling using Records . . . . . . . . . . . . . . . . . . . . . . . . 25
6.2 Higher Order Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26

7 Advanced Topics 27
7.1 ADVANCED: List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . 27
7.2 Distribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
7.3 Distributed, Replicated Associative Store . . . . . . . . . . . . . . . . . . . 29

c 2018 Erlang Solutions Ltd. 1


1 STARTING THE SYSTEM AND BASIC ERLANG

1 Starting the System and Basic Erlang


1.4 Temperature Conversion
temp.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: temp.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2012 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(temp).
8
9 -export([f2c/1,c2f/1,convert/1]).
10
11 -spec f2c(number()) -> number().
12
13 f2c(F) ->
14 5 * (F - 32) / 9.
15
16 -spec c2f(number()) -> number().
17
18 c2f(C) ->
19 9 * C / 5 + 32.
20
21 -spec convert({’c’,number()}) -> {’f’,number()};
22 ({’f’,number()}) -> {’c’,number()}.
23
24 convert({c,C}) ->
25 {f,c2f(C)};
26 convert({f,F}) ->
27 {c,f2c(F)}.

1.5 Simple Pattern Matching


boolean.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: boolean.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(boolean).
8 -export([b_not/1, b_and/2, b_or/2, b_or2/2]).
9
10 -spec b_not(boolean()) -> boolean().
11 b_not(false)-> true;
12 b_not(true )-> false.
13
14 -spec b_and(boolean(), boolean()) -> boolean().
15 b_and(true, true )-> true ;
16 b_and(_Bool1, _Bool2)-> false.
17
18 -spec b_or(boolean(), boolean()) -> boolean().
19 b_or(true, _Bool)-> true;
20 b_or(_Bool, true )-> true;
21 b_or(false, false)-> false.
22
23 % other solution

c 2018 Erlang Solutions Ltd. 2


1 STARTING THE SYSTEM AND BASIC ERLANG

24 -spec b_or2(boolean(), boolean()) -> boolean().


25 b_or2(false, false) -> false;
26 b_or2(_, _) -> true.

c 2018 Erlang Solutions Ltd. 3


2 SEQUENTIAL PROGRAMMING

2 Sequential Programming
2.1 Evaluating Expressions
sums.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: sums.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(sums).
8 -export([sum/1, sum_interval/2]).
9
10 %% @doc Adds the integers between 1 and N.
11 -spec sum(non_neg_integer()) -> integer().
12 sum(0) ->
13 0;
14 sum(N) ->
15 N + sum(N-1).
16
17 %% @doc Adds the integers between N and M
18 -spec sum_interval(integer(), integer()) -> integer().
19 sum_interval(Max, Max) ->
20 Max;
21 sum_interval(Min, Max) when Min =< Max ->
22 Min + sum_interval(Min + 1, Max).

2.2 Creating Lists


create.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: create.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(create).
8 -export([create/1, reverse_create/1]).
9
10 %% @doc Creates a list with integers [1,..,N]
11 -spec create(integer()) -> [integer(), ...].
12 create(N) ->
13 create(1, N).
14
15 create(M,M) ->
16 [M];
17 create(M,N) ->
18 [M | create(M+1, N)].
19
20 %% @doc Creates a list with integers [N,..,1]
21 -spec reverse_create(non_neg_integer()) -> [non_neg_integer(), ...].
22 reverse_create(1) ->
23 [1];
24 reverse_create(N) ->
25 [N | reverse_create(N-1)].

c 2018 Erlang Solutions Ltd. 4


2 SEQUENTIAL PROGRAMMING

2.3 Side Effects


effects.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: effects.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(effects).
8 -export([print/1, even_print/1]).
9
10 %% @doc Prints the integers between 1 and N.
11 -spec print(non_neg_integer()) -> ok.
12 print(0) ->
13 ok;
14 print(N) ->
15 print(N-1),
16 io:format("Number:~p~n",[N]).
17
18 %% @doc Prints the even integers between 1 and N.
19 -spec even_print(non_neg_integer()) -> ok.
20 even_print(0) ->
21 ok;
22 even_print(N) when N rem 2 == 0 ->
23 even_print(N-1),
24 io:format("Number:~p~n",[N]);
25 even_print(N) ->
26 even_print(N-1).

2.4 Database Handling Using Lists


db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(db).
8 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
9 -export_type([db/0]).
10 -type db() :: list().
11
12 %% @doc Create a new database
13 -spec new() -> db().
14 new() ->
15 [].
16
17 %% @doc Insert a new element in the database
18 -spec write(Key::term(), Val::term(), db()) -> db().
19 write(Key, Element, []) ->
20 [{Key, Element}];
21 write(Key, Element, [{Key, _} | Db]) ->
22 [{Key, Element}|Db];
23 write(Key, Element, [Current | Db]) ->
24 [Current | write(Key, Element, Db)].
25
26 %% @doc Remove an element from the database
27 -spec delete(Key::term(), db()) -> db().

c 2018 Erlang Solutions Ltd. 5


2 SEQUENTIAL PROGRAMMING

28 delete(Key, [{Key, _Element}|Db]) ->


29 Db;
30 delete(Key, [Tuple|Db]) ->
31 [Tuple|delete(Key, Db)];
32 delete(_Key, []) ->
33 [].
34
35 %% @doc Retrieve the first element in the database with a matching key
36 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
37 read(Key, [{Key, Element}|_Db]) ->
38 {ok, Element};
39 read(Key, [_Tuple|Db]) ->
40 read(Key, Db);
41 read(_Key, []) ->
42 {error, instance}.
43
44 %% @doc Return all the keys whose values match the given element.
45 -spec match(Val::term(), db()) -> [term()].
46 match(Element, [{Key, Element}|Db]) ->
47 [Key|match(Element, Db)];
48 match(Element, [_Tuple|Db]) ->
49 match(Element, Db);
50 match(_Key, []) ->
51 [].
52
53 %% @doc Deletes the database.
54 -spec destroy(db()) -> ok.
55 destroy(_Db) ->
56 ok.

2.5 ADVANCED: Manipulating Lists


manipulating.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: manipulating.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(manipulating).
8 -export([filter/2, concatenate/1, reverse/1, flatten/1]).
9
10 %% @doc Given an integer list, returns a new list where the elements
11 %% come from List and are smaller than the Key.
12 -spec filter([integer()], integer()) -> [integer()].
13 filter([H|T], Key) when H =< Key ->
14 [H|filter(T, Key)];
15 filter([_|T], Key) ->
16 filter(T, Key);
17 filter([], _Key) ->
18 [].
19
20 %% @doc Given a list of lists, returns a new list containing the
21 %% elements of the lists.
22 -spec concatenate([list()]) -> list().
23 concatenate([]) -> [];
24 concatenate([H|T]) ->
25 concatenate1(H, T).
26
27 concatenate1([H|T], Lists) ->

c 2018 Erlang Solutions Ltd. 6


2 SEQUENTIAL PROGRAMMING

28 [H|concatenate1(T, Lists)];
29 concatenate1([], Lists) ->
30 concatenate(Lists).
31
32 %% @doc Will reverse the list order.
33 -spec reverse(list()) -> list().
34 reverse(List) ->
35 reverse(List, []).
36 reverse([], Buffer) ->
37 Buffer;
38 reverse([H|T], Buffer) ->
39 reverse(T, [H|Buffer]).
40
41 %% @doc Takes a list and recursively flattens it.
42 -spec flatten(list()) -> list().
43 flatten([H|T]) when is_list(H) ->
44 concatenate([flatten(H), flatten(T)]);
45 flatten([H|T]) ->
46 [H|flatten(T)];
47 flatten([]) ->
48 [].

2.6 ADVANCED: Implement Quicksort and Merge Sort


sorting.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: sorting.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(sorting).
8 -export([quicksort/1, mergesort/1]).
9
10 %% @doc Sorts a list using the quicksort algorithm
11 -spec quicksort(list()) -> list().
12 quicksort([]) -> [];
13 quicksort([Pivot|Rest]) ->
14 {Smaller, Larger} = partition(Pivot, Rest),
15 quicksort(Smaller) ++ [Pivot] ++ quicksort(Larger).
16
17 %% Partition breaks the list into elements smaller or larger
18 %% Than the pivot
19 partition(Pivot, List) -> partition(Pivot, List, {[],[]}).
20
21 partition(_Pivot, [], Acc) ->
22 Acc;
23 partition(Pivot, [Smaller|Rest], {S,L}) when Smaller =< Pivot ->
24 partition(Pivot, Rest, {[Smaller|S], L});
25 partition(Pivot, [Larger|Rest], {S,L}) ->
26 partition(Pivot, Rest, {S, [Larger|L]}).
27
28
29 %% @doc Sorts a list with the mergesort algorithm
30 -spec mergesort(list()) -> list().
31 mergesort([]) -> [];
32 mergesort([X]) -> [X];
33 mergesort(L) when is_list(L) ->
34 {Left, Right} = split(length(L) div 2, L),
35 merge(mergesort(Left), mergesort(Right)).

c 2018 Erlang Solutions Ltd. 7


2 SEQUENTIAL PROGRAMMING

36
37 %% Splits a list at the point N and returns both parts
38 split(N, List) -> split(N, List, []).
39 split(0, List, Acc) -> {Acc, List};
40 split(N, [H|T], Acc) -> split(N-1, T, [H|Acc]).
41
42 %% Merges two sorted lists in a single one, still sorted.
43 merge([], Right) -> Right;
44 merge(Left, []) -> Left;
45 merge(Left = [L|Ls], Right = [R|Rs]) ->
46 if L =< R -> [L | merge(Ls, Right)];
47 L > R -> [R | merge(Left, Rs)]
48 end.

2.7 ADVANCED: Database Handling using Trees


db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2014 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(db).
8 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
9 -export_type([db/0]).
10 -type db() :: {term(),term(),term(),term()} | ’empty’.
11
12 %% @doc Create a new database
13 -spec new() -> db().
14 new() ->
15 empty.
16
17 %% @doc Insert a new element in the database
18 -spec write(Key::term(), Val::term(), db()) -> db().
19 write(Key, Val, {Key1, Val1, Left, Right}) when Key < Key1 ->
20 {Key1, Val1, write(Key, Val, Left), Right};
21 write(Key, Val, {Key1, Val1, Left, Right}) when Key > Key1 ->
22 {Key1, Val1, Left, write(Key, Val, Right)};
23 write(Key, Val, {_Key1, _Val1, Left, Right}) -> %Key == Key1
24 {Key, Val, Left, Right};
25 write(Key, Value, empty) ->
26 {Key, Value, empty, empty}.
27
28 %% @doc Remove an element from the database
29 -spec delete(Key::term(), db()) -> db().
30 delete(Key, {Key1, Val1, Left, Right}) when Key < Key1 ->
31 {Key1, Val1, delete(Key, Left), Right};
32 delete(Key, {Key1, Val1, Left, Right}) when Key > Key1 ->
33 {Key1, Val1, Left, delete(Key, Right)};
34 delete(_Key, {_Key1, _Val1, Left, Right}) -> %Key == Key1
35 merge(Left, Right);
36 delete(_Key, empty) ->
37 empty.
38
39 %% @doc Merge together two trees adding the left into the right.
40 merge(empty, Right) ->
41 Right;
42 merge(Left, empty) ->
43 Left;

c 2018 Erlang Solutions Ltd. 8


2 SEQUENTIAL PROGRAMMING

44 merge({KeyL, ValL, LeftL, RightL}, Right) ->


45 {KeyL, ValL, LeftL, merge(RightL, Right)}.
46
47
48 %% @doc Retrieve the first element in the database with a matching key
49 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
50 read(Key, {Key1, _Val, Left, _Right}) when Key < Key1 ->
51 read(Key, Left);
52 read(Key, {Key1, _Val, _Left, Right}) when Key > Key1 ->
53 read(Key, Right);
54 read(_Key, {_Key1, Val, _Left, _Right}) -> %Key == Key1
55 {ok,Val};
56 read(_Key, empty) ->
57 {error, instance}.
58
59 %% @doc Return all the keys whose values match the given element.
60 -spec match(Val::term(), db()) -> [term()].
61 match(Val, {Key, Val, Left, Right}) ->
62 match(Val, Left) ++ [Key] ++ match(Val, Right);
63 match(Val, {_Key, _Val, Left, Right}) ->
64 match(Val, Left) ++ match(Val, Right);
65 match(_Val, empty) ->
66 [].
67
68 %% @doc An alternate algorithm generating a list of keys.
69 %% match(Val, Db) ->
70 %% match(Val, Db, []).
71
72 %% match(Val, {Key, Val, Left, Right}, Tail) ->
73 %% match(Val, Left, [Key|match(Val, Right, Tail)]);
74 %% match(Val, {_Key, _Val, Left, Right}, Tail) ->
75 %% match(Val, Left, match(Val, Right, Tail));
76 %% match(_Val, empty, Tail) ->
77 %% Tail.
78
79 %% @doc Deletes the database.
80 -spec destroy(db()) -> ok.
81 destroy(_Db) ->
82 ok.

c 2018 Erlang Solutions Ltd. 9


3 CONCURRENT PROGRAMMING

3 Concurrent Programming
3.1 An Echo Server
echo.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : echo.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(echo).
8 -export([start/0, stop/0, listen/0, print/1]).
9
10 -spec start() -> ok.
11 start()->
12 register(echo, spawn(echo, listen, [])),
13 ok.
14
15 %% Prints a term passed as an argument.
16 -spec print(term()) -> ok.
17 print(Message)->
18 echo ! {print, Message},
19 ok.
20
21 %% Stops the echo server.
22 -spec stop() -> ok.
23 stop()->
24 echo ! stop,
25 ok.
26
27 %% The echo server loop
28 -spec listen() -> true.
29 listen()->
30 receive
31 {print, Message} ->
32 io:format("~p~n",[Message]),
33 listen();
34 stop ->
35 true
36 end.

3.2 The Process Ring


ring.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: ring.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(ring).
8 %% Client Functions
9 -export([start/3]).
10
11 %% Internal Exports
12 -export([master/3, loop/2]).
13
14 %% @doc Starts the master process which in turn spawns off the

c 2018 Erlang Solutions Ltd. 10


3 CONCURRENT PROGRAMMING

15 %% individual processes which will receive a message.


16 -spec start(non_neg_integer(), non_neg_integer(), term()) -> pid().
17 start(ProcNum, MsgNum, Message)->
18 spawn(ring, master, [ProcNum, MsgNum, Message]).
19
20 %% @private This function starts the slave pids and then gets into
21 %% the loop which will send the Message MsgNum times to
22 %% the slaves.
23 -spec master(non_neg_integer(), non_neg_integer(), term()) -> stop | no_return
().
24 master(ProcNum, MsgNum, Message)->
25 Pid = start_slaves(ProcNum,self()),
26 master_loop(MsgNum, Message, Pid).
27
28
29 %% Will start ProcNum slave processes
30 -spec start_slaves(non_neg_integer(), pid()) -> pid().
31 start_slaves(1, Pid)->
32 Pid;
33 start_slaves(ProcNum, Pid)->
34 NewPid = spawn(ring, loop, [ProcNum, Pid]),
35 start_slaves(ProcNum - 1, NewPid).
36
37 %% The master loop will loop MsgNum times sending a message to
38 %% Pid. It will iterate every time it receives the Message it is
39 %% sent to the next process in the ring.
40 -spec master_loop(non_neg_integer(), term(), pid()) -> stop | no_return().
41 master_loop(0, _Message, Pid)->
42 io:format("Process:1 terminating~n"),
43 Pid ! stop;
44 master_loop(MsgNum, Message, Pid) ->
45 Pid ! Message,
46 receive
47 Message ->
48 io:format("Process:1 received:~p~n",[Message]),
49 master_loop(MsgNum - 1, Message, Pid)
50 end.
51
52 %% @private This is the slave loop, where upon receiving a message, the
53 %% process forwards it to the next process in the ring. Upon
54 %% receiving stop, it sends the stop message on and terminates.
55 -spec loop(non_neg_integer(), pid()) -> stop | no_return().
56 loop(ProcNum, Pid)->
57 receive
58 stop ->
59 io:format("Process:~p terminating~n",[ProcNum]),
60 Pid ! stop;
61 Message ->
62 io:format("Process:~p received: ~p~n", [ProcNum, Message]),
63 Pid!Message,
64 loop(ProcNum, Pid)
65 end.

3.3 The Process Crossring


crossring.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: crossring.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.

c 2018 Erlang Solutions Ltd. 11


3 CONCURRENT PROGRAMMING

5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(crossring).
8
9 %% Client Functions
10 -export([start/3]).
11 %% Internal Exports
12 -export([master/3, loop/2]).
13
14 %% @doc Starts the master process which in turn spawns off the
15 %% individual processes which will receive a message.
16 -spec start(pos_integer(), non_neg_integer(), term()) -> pid().
17 start(ProcNum, MsgNum, Message)->
18 spawn(crossring, master, [ProcNum, MsgNum, Message]).
19
20 %% @private This function starts the slave pids and then gets into
21 %% the loop which will send the Message MsgNum times to
22 %% the slaves.
23 -spec master(pos_integer(), non_neg_integer(), term()) -> stop | no_return().
24 master(ProcNum, MsgNum, Message)->
25 ProcLim = round(ProcNum / 2),
26 {MidPid, FirstPid} = start_slaves(ProcNum, ProcLim, self()),
27 master_loop(MsgNum, {first_half, Message}, FirstPid, MidPid).
28
29
30 %% Will start ProcNum slave processes
31 -spec start_slaves(pos_integer(), non_neg_integer(), pid()) -> pid() | {pid(),
pid()}.
32 start_slaves(1, _, Pid)->
33 Pid;
34 %% We cross when we’re on the midpoint process + 1.
35 start_slaves(ProcNum, ProcLim, Pid) when ProcNum =:= ProcLim + 1->
36 %% We spawn the process the first one will send messages to
37 MidPid = spawn(crossring, loop, [ProcNum, Pid]),
38 %% We return it in a tuple, and keep starting the other processes
39 %% after the first (middle) one. The Last spawned Pid (or the second
40 %% element of the crossring) is returned as the second tuple element
41 {MidPid, start_slaves(ProcNum - 1, ProcLim, self())};
42 start_slaves(ProcNum, ProcLim, Pid)->
43 NewPid = spawn(crossring, loop, [ProcNum, Pid]),
44 start_slaves(ProcNum - 1, ProcLim, NewPid).
45
46 %% The master loop will loop MsgNum times sending a message to
47 %% Pid. It will iterate every time it receives the Message it is
48 %% sent to the next process in the ring.
49 -spec master_loop(non_neg_integer(), term(), pid(), pid()) -> stop | no_return
().
50 master_loop(0, _Message, FirstPid, MidPid)->
51 io:format("Process: 1 terminating~n"),
52 MidPid ! FirstPid ! stop;
53 %% Handling the messages on the first half of the crossring
54 master_loop(MsgNum, {first_half, Message}, FirstPid, MidPid) ->
55 FirstPid ! {first_half, Message},
56 receive
57 {first_half, Message} ->
58 io:format("Process: 1 received: ~p halfway through~n",[Message]),
59 master_loop(MsgNum, {second_half, Message}, FirstPid, MidPid)
60 end;
61 %% Handling the messages on the second half of the crossring
62 master_loop(MsgNum, {second_half, Message}, FirstPid, MidPid) ->
63 MidPid ! {second_half, Message},

c 2018 Erlang Solutions Ltd. 12


3 CONCURRENT PROGRAMMING

64 receive
65 {second_half, Message} ->
66 io:format("Process: 1 received: ~p~n",[Message]),
67 master_loop(MsgNum - 1, {first_half, Message}, FirstPid, MidPid)
68 end.
69
70 %% @private This is the slave loop, where upon receiving a message, the
71 %% process forwards it to the next process in the ring. Upon
72 %% receiving stop, it sends the stop message on and terminates.
73 -spec loop(pos_integer(), pid()) -> stop | no_return().
74 loop(ProcNum, Pid)->
75 receive
76 stop ->
77 io:format("Process: ~p terminating~n",[ProcNum]),
78 Pid ! stop;
79 {Part, Message} ->
80 io:format("Process: ~p received: ~p~n", [ProcNum, Message]),
81 Pid ! {Part, Message},
82 loop(ProcNum, Pid)
83 end.

c 2018 Erlang Solutions Ltd. 13


4 PROCESS DESIGN PATTERNS

4 Process Design Patterns


4.1 A Database Server
my_db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: my_db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2015 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(my_db).
8
9 %% Internal Exports
10 -export([init/0]).
11
12 %%External Exports
13 -export([start/0,stop/0, write/2, delete/1, read/1, match/1]).
14
15 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
16 %% Client Functions
17 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
18
19 %% @doc Starts the database server
20 -spec start() -> ok.
21 start() ->
22 register(db, spawn(my_db, init, [])),
23 ok.
24
25 %% @doc Stops the database server
26 -spec stop() -> ok.
27 stop() ->
28 async_call(stop).
29
30 %% @doc Inserts an element in the database server
31 -spec write(Key::term(), Val::term()) -> ok.
32 write(Key, Element) ->
33 async_call({write, Key, Element}).
34
35 %% @doc Removes an element from the database. Will succeed even
36 %% If the element does not exist.
37 -spec delete(Key::term()) -> ok.
38 delete(Key) ->
39 async_call({delete, Key}).
40
41 %% @doc Will retrieve an element from the database.
42 -spec read(Key::term()) -> {ok, term()} | {error, instance}.
43 read(Key) ->
44 sync_call({read, Key}).
45
46 %% @doc Will return a list of keys which match to the element.
47 -spec match(Val::term()) -> [Key::term()].
48 match(Element) ->
49 sync_call({match, Element}).
50
51 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
52 %% Communication Help Functions
53 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
54
55 %% Will make a synchronous call to the server and return the

c 2018 Erlang Solutions Ltd. 14


4 PROCESS DESIGN PATTERNS

56 %% reply received.
57 -spec sync_call(term()) -> term().
58 sync_call(Msg) ->
59 db ! {request, self(), Msg},
60 receive
61 {reply, Reply} -> Reply
62 end.
63
64 %% Will send an asynchronous reques to the server.
65 -spec async_call(term()) -> ok.
66 async_call(Msg) ->
67 db ! {request, Msg},
68 ok.
69
70 %% Sends a reply back to the client’s synchronous call.
71 -spec reply(pid(), term()) -> {reply, term()}.
72 reply(Pid, Reply) ->
73 Pid ! {reply, Reply}.
74
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
76 %% Database Server Loop
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78
79 %% @private Initialises the database and enters the server loop
80 -spec init() -> no_return().
81 init() ->
82 loop(db:new()).
83
84 %% loop(list())
85 %% The database server loop which will iterate and handle
86 %% all requests.
87 -spec loop(db:db()) -> no_return().
88 loop(Db) ->
89 receive
90 {request, {write, Key, Element}} ->
91 NewDb = db:write(Key, Element, Db),
92 loop(NewDb);
93 {request, {delete, Key}} ->
94 NewDb = db:delete(Key, Db),
95 loop(NewDb);
96 {request, Pid, {read, Key}} ->
97 reply(Pid, db:read(Key, Db)),
98 loop(Db);
99 {request, Pid, {match, Element}} ->
100 reply(Pid, db:match(Element, Db)),
101 loop(Db);
102 {request, stop} ->
103 db:destroy(Db)
104 end.

4.2 A Mutex Semaphore


mutex.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: mutex.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(mutex).

c 2018 Erlang Solutions Ltd. 15


4 PROCESS DESIGN PATTERNS

8 %% Client Exports
9 -export([start/0, signal/0, wait/0]).
10
11 %% Internal Exports
12 -export([free/0]).
13
14 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15 %% Client Functions
16 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17
18 %% @doc Will start the mutex semaphore
19 -spec start() -> true.
20 start() ->
21 register(mutex, spawn(mutex, free, [])).
22
23 %% @doc Will free the semaphore currently held by the process
24 -spec signal() -> ok.
25 signal() ->
26 mutex ! {signal, self()},
27 ok.
28
29 %% @doc Will keep the process busy until the semaphore is available.
30 -spec wait() -> ok.
31 wait() ->
32 mutex ! {wait, self()},
33 receive
34 ok -> ok
35 end.
36
37 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
38 %% Finite State Machine
39 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
40
41 %% @doc The state where the semaphore is available
42 -spec free() -> no_return().
43 free() ->
44 receive
45 {wait, Pid} ->
46 Pid ! ok,
47 busy(Pid)
48 end.
49
50 %% @doc The semaphore is taken by Pid. Pid is the only process which
51 %% may release it.
52 -spec busy(pid()) -> no_return().
53 busy(Pid) ->
54 receive
55 {signal, Pid} -> free()
56 end.

4.3 ADVANCED: A Database Server with transactions


my_db_trans.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: my_db_trans.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2015 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(my_db_trans).

c 2018 Erlang Solutions Ltd. 16


4 PROCESS DESIGN PATTERNS

8
9 %% Internal Exports
10 -export([init/0]).
11
12 %%External Exports
13 -export([start/0, stop/0]).
14 -export([write/2, delete/1, read/1, match/1, lock/0, unlock/0]).
15
16 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
17 %% Client Functions
18 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
19
20 %% @doc Starts the database server
21 -spec start() -> ok.
22 start() ->
23 register(db, spawn(my_db_trans, init, [])),
24 ok.
25
26 %% @doc Stops the database server
27 -spec stop() -> ok.
28 stop() ->
29 sync_call(stop).
30
31 %% @doc Inserts an element in the database server
32 -spec write(Key::term(), Val::term()) -> ok.
33 write(Key, Element) ->
34 sync_call({write, Key, Element}).
35
36 %% @doc Removes an element from the database. Will succeed even
37 %% If the element does not exist.
38 -spec delete(Key::term()) -> ok.
39 delete(Key) ->
40 sync_call({delete, Key}).
41
42 %% @doc Will retrieve an element from the database.
43 -spec read(Key::term()) -> {ok, term()} | {error, instance}.
44 read(Key) ->
45 sync_call({read, Key}).
46
47 %% @doc Will return a list of keys which match to the element.
48 -spec match(Val::term()) -> [Key::term()].
49 match(Element) ->
50 sync_call({match, Element}).
51
52 %% @doc Will lock the database for the current caller.
53 -spec lock() -> ok.
54 lock() ->
55 sync_call(lock).
56
57 %% @doc Will unlock the database.
58 -spec unlock() -> ok.
59 unlock() ->
60 sync_call(unlock).
61
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63 %% Communication Help Functions
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65
66 %% Will make a synchronous call to the server and return the
67 %% reply received.
68 -spec sync_call(term()) -> term().

c 2018 Erlang Solutions Ltd. 17


4 PROCESS DESIGN PATTERNS

69 sync_call(Msg) ->
70 db ! {request, self(), Msg},
71 receive
72 {reply, Reply} -> Reply
73 end.
74
75 %% Sends a reply back to the client’s synchronous call.
76 -spec reply(pid(), term()) -> {reply, term()}.
77 reply(Pid, Reply) ->
78 Pid ! {reply, Reply}.
79
80 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 %% Database Server Loop
82 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
83
84 %% @private Initialises the database and enters the server loop
85 -spec init() -> no_return().
86 init() ->
87 loop(db:new()).
88
89 %% loop(list())
90 %% The database server loop which will iterate and handle
91 %% all requests.
92 -spec loop(db:db()) -> no_return().
93 loop(Db) ->
94 receive
95 {request, Pid, {write, Key, Element}} ->
96 NewDb = db:write(Key, Element, Db),
97 reply(Pid, ok),
98 loop(NewDb);
99 {request, Pid, {delete, Key}} ->
100 NewDb = db:delete(Key, Db),
101 reply(Pid, ok),
102 loop(NewDb);
103 {request, Pid, {read, Key}} ->
104 reply(Pid, db:read(Key, Db)),
105 loop(Db);
106 {request, Pid, {match, Element}} ->
107 reply(Pid, db:match(Element, Db)),
108 loop(Db);
109 {request, Pid, stop} ->
110 reply(Pid, ok),
111 db:destroy(Db);
112 {request, Pid, lock} ->
113 reply(Pid, ok),
114 locked_loop(Db, Pid)
115 end.
116
117 %% locked_loop(list(), pid())
118 %% The database server loop which will iterate and handle
119 %% all requests from the locking client.
120 -spec locked_loop(db:db(), pid()) -> no_return().
121 locked_loop(Db, Locker) ->
122 receive
123 {request, Locker, {write, Key, Element}} ->
124 NewDb = db:write(Key, Element, Db),
125 reply(Locker, ok),
126 locked_loop(NewDb, Locker);
127 {request, Locker, {delete, Key}} ->
128 NewDb = db:delete(Key, Db),
129 reply(Locker, ok),

c 2018 Erlang Solutions Ltd. 18


4 PROCESS DESIGN PATTERNS

130 locked_loop(NewDb, Locker);


131 {request, Locker, {read, Key}} ->
132 reply(Locker, db:read(Key, Db)),
133 locked_loop(Db, Locker);
134 {request, Locker, {match, Element}} ->
135 reply(Locker, db:match(Element, Db)),
136 locked_loop(Db, Locker);
137 {request, Locker, stop} ->
138 reply(Locker, ok),
139 db:destroy(Db);
140 {request, Locker, unlock} ->
141 reply(Locker, ok),
142 loop(Db)
143 end.

c 2018 Erlang Solutions Ltd. 19


5 PROCESS ERROR HANDLING

5 Process Error Handling


5.1 The Linked Ping Pong Server
pingpong.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : pingpong.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(pingpong).
8
9 %% Interface
10 -export([start/0, stop/0, send/1]).
11 %% Internal Exports
12 -export([init_a/0, init_b/0]).
13
14 start() ->
15 register(a, spawn(pingpong, init_a, [])),
16 register(b, spawn(pingpong, init_b, [])),
17 ok.
18
19 stop() ->
20 exit(whereis(a), non_normal_exit).
21
22 send(N) ->
23 a ! {msg, message, N},
24 ok.
25
26 init_a() ->
27 loop_a().
28
29 init_b() ->
30 link(whereis(a)),
31 loop_b().
32
33 loop_a() ->
34 receive
35 {msg, _Msg, 0} ->
36 loop_a();
37 {msg, Msg, N} ->
38 io:format("ping...~n"),
39 timer:sleep(500),
40 b ! {msg, Msg, N -1},
41 loop_a()
42 after
43 15000 ->
44 io:format("Ping got bored, exiting.~n"),
45 exit(timeout)
46 end.
47
48 loop_b() ->
49 receive
50 {msg, _Msg, 0} ->
51 loop_b();
52 {msg, Msg, N} ->
53 io:format("pong!~n"),
54 timer:sleep(500),
55 a ! {msg, Msg, N -1},

c 2018 Erlang Solutions Ltd. 20


5 PROCESS ERROR HANDLING

56 loop_b()
57 after
58 15000 ->
59 io:format("Pong got bored, exiting.~n"),
60 exit(timeout)
61 end.

5.2 Trapping Exits


fussball.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

2 %%% File: fussball.erl


3 %%% @doc A simple game of Fussball.
4 %%% @author [email protected]
5 %%% @copyright 1999-2017 Erlang Solutions Ltd.
6 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

7
8 -module(fussball).
9
10 %% Interface
11 -export([start/2, init/2, stop/1, kickoff/1]).
12
13 start(MyCountry, OtherCountry) ->
14 spawn(?MODULE, init, [MyCountry, OtherCountry]),
15 ok.
16
17 stop(Country) ->
18 Country ! stop.
19
20 kickoff(Country) ->
21 Country ! kick,
22 ok.
23
24 init(MyCountry, OtherCountry) ->
25 process_flag(trap_exit, true),
26 register(MyCountry, self()),
27 catch link(whereis(OtherCountry)),
28 loop(MyCountry, OtherCountry).
29
30 loop(MyCountry, OtherCountry) ->
31 receive
32 {’EXIT’, _Pid, Reason} ->
33 io:format("Got exit signal ~p~n", [Reason]),
34 ok;
35 stop ->
36 ok;
37 save ->
38 io:format("~p just saved...~n", [OtherCountry]),
39 loop(MyCountry, OtherCountry);
40 score ->
41 io:format("Oh no! ~p just scored!!~n", [OtherCountry]),
42 loop(MyCountry, OtherCountry);
43 kick ->
44 timer:sleep(500),
45 case rand:uniform(1000) of
46 N when N > 950 ->
47 io:format("~p SAVES! And what a save!!~n", [MyCountry]),
48 OtherCountry ! save,

c 2018 Erlang Solutions Ltd. 21


5 PROCESS ERROR HANDLING

49 OtherCountry ! kick;
50 N when N > 800 ->
51 io:format("~p SCORES!!~n", [MyCountry]),
52 OtherCountry ! score;
53 _ ->
54 io:format("~p kicks the ball...~n", [MyCountry]),
55 OtherCountry ! kick
56 end,
57 loop(MyCountry, OtherCountry)
58 end.

5.3 A Reliable Mutex Semaphore


mutex.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: mutex.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(mutex).
8 %% Client Exports
9 -export([start/0, signal/0, wait/0]).
10 %% Internal Exports
11 -export([init/0]).
12 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
13 %% Client Functions
14 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
15
16 %% @doc Will start the mutex semaphore
17 -spec start() -> true.
18 start() ->
19 register(mutex, spawn(mutex, init, [])).
20
21 %% @doc Initializes the state machine.
22 -spec init() -> no_return().
23 init() ->
24 process_flag(trap_exit, true),
25 free().
26
27 %% @doc Will free the semaphore currently held by the process
28 -spec signal() -> ok.
29 signal() ->
30 mutex ! {signal, self()},
31 ok.
32
33 %% @doc Will keep the process busy until the semaphore is available.
34 -spec wait() -> ok.
35 wait() ->
36 mutex ! {wait, self()},
37 receive
38 ok -> ok
39 end.
40
41 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
42 %% Finite State Machine
43 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
44
45 %% @doc The state where the semaphore is available
46 -spec free() -> no_return().

c 2018 Erlang Solutions Ltd. 22


5 PROCESS ERROR HANDLING

47 free() ->
48 receive
49 {’EXIT’, Pid, _Reason} ->
50 free();
51 {wait, Pid} ->
52 link(Pid),
53 Pid ! ok,
54 busy(Pid)
55 end.
56
57 %% @doc The semaphore is taken by Pid. Pid is the only process which
58 %% may release it.
59 -spec busy(pid()) -> no_return().
60 busy(Pid) ->
61 receive
62 {’EXIT’, Pid, _Reason} ->
63 free();
64 {signal, Pid} ->
65 unlink(Pid),
66 free()
67 end.

5.5 ADVANCED: A Supervisor Process


sup.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: sup.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(sup).
8
9 %% Client Functions
10 -export([start/1, stop/1, start_child/4]).
11
12 %% Internal Exports
13 -export([init/0]).
14
15 %%% @doc Starts an Erlang Process Supervisor
16 -spec start(atom()) -> {ok, pid()}.
17 start(Name) ->
18 Pid = spawn(sup, init, []),
19 register(Name, Pid),
20 {ok, Pid}.
21
22 %%% @doc Stops an Erlang supervisor, killing all the monitored children
23 -spec stop(pid() | atom()) -> ok.
24 stop(Name) ->
25 Name ! stop,
26 ok.
27
28 %%% @doc Given a module, function and arguments, will start a child
29 %%% and monitor it. If it terminates abnormally, the child is
30 %%% restarted.
31 -spec start_child(atom(), atom(), atom(), [term()]) -> {ok, pid()}.
32 start_child(Name, Module, Function, Args) ->
33 Name ! {start_child, self(), Module, Function, Args},
34 receive
35 {ok, Pid} -> {ok, Pid}

c 2018 Erlang Solutions Ltd. 23


5 PROCESS ERROR HANDLING

36 end.
37
38 %%% @doc Initialises the supervisor state
39 -spec init() -> ok.
40 init() ->
41 process_flag(trap_exit, true),
42 loop([]).
43
44 %%% loop([child()]) -> ok.
45 %%% child() = {pid(), restar_count(), mod(), func(), [args()]}.
46 %%% restart_count() = integer(). number of times the child has restarted
47 %%% mod() = atom(). the module where the spawned function is located
48 %%% func() = atom(). the function spawned
49 %%% args() = term(). the arguments passed to the function
50 %%% The supervisor loop which handles the incoming client requests
51 %%% and EXIT signals from supervised children.
52 -type child() :: {pid(), non_neg_integer(), atom(), atom(), [term()]}.
53 -spec loop([child()]) -> ok.
54 loop(Children) ->
55 receive
56 {start_child, ClientPid, Mod, Func, Args} ->
57 Pid = spawn_link(Mod, Func, Args),
58 ClientPid ! {ok, Pid},
59 loop([{Pid, 1, Mod, Func, Args}|Children]);
60 {’EXIT’, Pid, normal} ->
61 NewChildren = lists:keydelete(Pid, 1, Children),
62 loop(NewChildren);
63 {’EXIT’, Pid, Reason} ->
64 NewChildren = lists:keydelete(Pid, 1, Children),
65 {value, Child} = lists:keysearch(Pid, 1, Children),
66 {Pid, Count, Mod, Func, Args} = Child,
67 error_message(Pid, Count, Reason, Mod, Func, Args),
68 NewPid = spawn_link(Mod, Func, Args),
69 loop([{NewPid, Count + 1, Mod, Func, Args}|NewChildren]);
70 stop ->
71 kill_children(Children)
72 end.
73
74 %%% Kills all the children in the supervision tree.
75 -spec kill_children([child()]) -> ok.
76 kill_children([{Pid, _Count, _Mod, _Func, _Args}|Children]) ->
77 exit(Pid, kill),
78 kill_children(Children);
79 kill_children([]) ->
80 ok.
81
82 %%% Prints an error message for the child which died.
83 -spec error_message(pid(), non_neg_integer(), term(), atom(), atom(), [term()])
84 -> ok.
85 error_message(Pid, Count, Reason, Mod, Func, Args) ->
86 io:format("~50c~n",[$-]),
87 io:format("Error: Process ~p Terminated ~p time(s)~n",[Pid, Count]),
88 io:format(" Reason for termination:~p~n",[Reason]),
89 io:format(" Restarting with ~p:~p/~p~n",[Mod,Func,length(Args)]),
90 io:format("~50c~n",[$-]).

c 2018 Erlang Solutions Ltd. 24


6 RECORDS AND FUNS

6 Records and Funs


6.1 Database Handling using Records
db.hrl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : db.hrl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -record(data, {key, value}).

db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(db).
8 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
9 -include("db.hrl").
10
11 -export_type([db/0]).
12 -type db() :: list().
13
14 %% @doc Create a new database
15 -spec new() -> db().
16 new() -> [].
17
18 %% @doc Insert a new element in the database
19 -spec write(Key::term(), Val::term(), db()) -> db().
20 write(Key, Element, Db) -> [#data{key = Key, value = Element}|Db].
21
22 %% @doc Remove an element from the database
23 -spec delete(Key::term(), db()) -> db().
24 delete(Key, [#data{key = Key}|Db]) ->
25 Db;
26 delete(Key, [Record|Db]) ->
27 [Record|delete(Key, Db)];
28 delete(_Key, []) ->
29 [].
30
31 %% @doc Retrieve the first element in the database with a matching key
32 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
33 read(Key, [#data{key = Key, value = Element}|_Db]) ->
34 {ok, Element};
35 read(Key, [_Record|Db]) ->
36 read(Key, Db);
37 read(_Key, []) ->
38 {error, instance}.
39
40 %% @doc Return all the keys whose values match the given element.
41 -spec match(Val::term(), db()) -> [term()].
42 match(Element, [#data{key = Key, value = Element}|Db]) ->
43 [Key|match(Element, Db)];
44 match(Element, [_Record|Db]) ->
45 match(Element, Db);

c 2018 Erlang Solutions Ltd. 25


6 RECORDS AND FUNS

46 match(_Key, []) ->


47 [].
48
49 %% @doc Deletes the database.
50 -spec destroy(db()) -> ok.
51 destroy(_Db) ->
52 ok.

6.2 Higher Order Functions


funs.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : funs.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(funs).
8 -export([print/1, smaller/2, print_even/1, concatenate/1, sum/1]).
9
10
11 print(N) ->
12 List = lists:seq(1,N),
13 Print = fun(X) -> io:format("~p~n",[X]) end,
14 lists:foreach(Print, List).
15
16 smaller(List, Size) ->
17 Filter = fun(X) when X > Size -> false;
18 (_X) -> true
19 end,
20 lists:filter(Filter, List).
21
22 print_even(N) ->
23 Filter = fun(X) -> X rem 2 == 0 end,
24 List = lists:seq(1,N),
25 FilteredList = lists:filter(Filter, List),
26 Print = fun(X) -> io:format("~p~n",[X]) end,
27 lists:foreach(Print, FilteredList).
28
29 concatenate(ListOfLists) ->
30 Concatenate = fun(X, Buffer) -> Buffer ++ X end,
31 lists:foldl(Concatenate, [], ListOfLists).
32
33 sum(ListOfInts) ->
34 Sum = fun(Integer, Buffer) -> Buffer + Integer end,
35 lists:foldl(Sum, 0, ListOfInts).

c 2018 Erlang Solutions Ltd. 26


7 ADVANCED TOPICS

7 Advanced Topics
7.1 ADVANCED: List Comprehensions
lc.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : lc.erl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(lc).
8
9 -export([three/0, filtersquare/1, intersection/2, disjunction/2]).
10
11 three() ->
12 [X || X <- lists:seq(1,10), X rem 3 == 0].
13
14 filtersquare(List) ->
15 [X*X || X <- List, is_integer(X)].
16
17 intersection(List1, List2) ->
18 [H || H <- List1, lists:member(H, List2)].
19
20 disjunction(List1, List2) ->
21 Intersection = intersection(List1, List2),
22 [H || H <- List1 ++ List2, not lists:member(H, Intersection)].

7.2 Distribution
db.hrl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File : db.hrl
3 %%% Author : [email protected]
4 %%% Copyright : 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -record(data, {key, value}).

db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 %%% NOTE: TO USE, CHANGE FILE NAME FROM db.erl.dist TO db.erl AND
8 %%% COMPILE
9
10 -module(db).
11 -define(NODE, ’a@localhost’).
12 -define(NAME, ?MODULE).
13 -define(SERVER, {?NAME, ?NODE}).
14
15 %% Client Function Exports
16 -export([new/0, write/3, delete/2, read/2, match/2, destroy/1]).
17 -include("db.hrl").
18 -export_type([db/0]).
19 -type db() :: ?SERVER.

c 2018 Erlang Solutions Ltd. 27


7 ADVANCED TOPICS

20
21 %% @doc Create a new database.
22 -spec new() -> db().
23 new() ->
24 Pid = spawn(?NODE, fun() -> init() end),
25 register(?NAME, Pid),
26 ?SERVER.
27
28 %% @doc Insert a new element in the database
29 -spec write(Key::term(), Val::term(), db()) -> db().
30 write(Key, Element, Db) ->
31 send(Db, {write, Key, Element}).
32
33 %% @doc Remove an element from the database
34 -spec delete(Key::term(), db()) -> db().
35 delete(Key, Db) ->
36 send(Db, {delete, Key}).
37
38 %% @doc Retrieve the first element in the database with a matching key
39 -spec read(Key::term(), db()) -> {ok, term()} | {error, instance}.
40 read(Key, Db) ->
41 send(Db, {read, Key}).
42
43 %% @doc Return all the keys whose values match the given element.
44 -spec match(Val::term(), db()) -> [term()].
45 match(Element, Db) ->
46 send(Db, {match, Element}).
47
48 %% @doc Deletes the database.
49 -spec destroy(db()) -> ok.
50 destroy(Db) ->
51 send(Db, destroy).
52
53 %% SERVER
54 send(To, Msg) ->
55 To ! {self(), Msg},
56 receive
57 done -> % Dropping the message to maintain the API
58 To;
59 Reply ->
60 Reply
61 after 5000 -> erlang:error(timeout)
62 end.
63
64 reply(To, Msg) ->
65 To ! Msg.
66
67 init() ->
68 loop(ets:new(db, [{keypos, #data.key}])).
69
70 loop(Db) ->
71 receive
72 {From, {write, Key, Element}} ->
73 ets:insert(Db, #data{key = Key, value = Element}),
74 reply(From, done),
75 loop(Db);
76 {From, {delete, Key}} ->
77 ets:delete(Db, Key),
78 reply(From, done),
79 loop(Db);
80 {From, {read, Key}} ->

c 2018 Erlang Solutions Ltd. 28


7 ADVANCED TOPICS

81 case ets:lookup(Db, Key) of


82 [#data{value = Element}] -> reply(From,{ok, Element});
83 [] -> reply(From,{error, instance})
84 end,
85 loop(Db);
86 {From, {match, Element}} ->
87 reply(From,
88 lists:flatten(
89 ets:match(Db, #data{key = ’$1’, value = Element}))),
90 loop(Db);
91 {From, destroy} ->
92 ets:delete(Db),
93 reply(From, ok);
94 Msg ->
95 io:format("Unknown Msg:~n\t~p~n", [Msg]),
96 loop(Db)
97 end.

7.3 Distributed, Replicated Associative Store


dist_db.erl
1 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
2 %%% File: dist_db.erl
3 %%% @author [email protected]
4 %%% @copyright 1999-2011 Erlang Solutions Ltd.
5 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6
7 -module(dist_db).
8 %% Assumes that there is one instance of dist_db running on each
9 %% node the VM is currently connected to.
10 %% This DB does full replication.
11
12 %%% public API
13 -export([start/0, stop/0, read/1, write/2, delete/1]).
14 %% Internal calls
15 -export([init/0]).
16 -define(NODES, [node() | nodes()]).
17
18 %%% Public API
19 %% @doc Starts a local instance of dist_db.
20 -spec start() -> ok.
21 start() ->
22 register(?MODULE, spawn(?MODULE, init, [])),
23 ok.
24
25 %% @doc Shuts down the local instance of dist_db
26 -spec stop() -> ok.
27 stop() -> request(stop).
28
29 %% @doc Asks the local dist_db instance for the value associated with Key
30 -spec read(Key::term()) -> {value, term()} | none.
31 read(Key) -> request({read, Key}).
32
33 %% @doc Inserts a new entry on all nodes if it doesn’t exist, updates it
34 %% otherwise.
35 -spec write(Key::term(), Val::term()) -> ok.
36 write(Key, Value) -> request({write, Key, Value}).
37
38 %% @doc Deletes an entry from all nodes.
39 -spec delete(Key::term()) -> ok.

c 2018 Erlang Solutions Ltd. 29


7 ADVANCED TOPICS

40 delete(Key) -> request({delete, Key}).


41
42 %%% helpers
43 %% Makes a synchronous request to the local dist_db server
44 %% and returns the reply.
45 -spec request(Msg::term()) -> Repky::term().
46 request(Msg) ->
47 Ref = make_ref(),
48 ?MODULE ! {self(), Ref, Msg},
49 receive
50 {Ref, Reply} -> Reply
51 after 5000 ->
52 erlang:error(timeout)
53 end.
54
55 %%% Server loop
56 %% @doc initializes the server’s main loop.
57 %% We chose a gb_tree for the backend of our storage
58 -spec init() -> term().
59 init() -> loop(gb_trees:empty(), 0).
60
61 %% The loop receives messages from two sides: the clients and other servers.
62 %% Messages coming from the client can be replied to directly. Messages
63 %% coming from other servers are usually requiring updates to the database.
64 %%
65 %% All updates must follow the virtual clock (VClock). On each destructive
66 %% request, an increasing VClock number (a timestamp) is given to the
67 %% request, which is then broadcasted to all instances of dist_db on the
68 %% currently connected nodes. The destructive update can only be
69 %% performed if the entry’s VClock is lower than the new timestamp.
70 %%
71 %% While the calls are performed, it’s possible that two values will
72 %% appear as different on all nodes due to message ordering, but the
73 %% db should reach eventual consistency, meaning that if we were to
74 %% stop all the queries and then looked them up, everything should look
75 %% consistent.
76 %%
77 %% It will happen under certain situations that the data becomes
78 %% inconsistent over all instances of the DB. This is because the
79 %% VClock scheme used here is only reducing the changes of conflicts,
80 %% not entirely eliminating them. A truly safe implementation would
81 %% require real Vector Clocks (or Lamport Clocks), or in the case of a
82 %% more consistent database (not just eventually consistent), use
83 %% transactions
84 -spec loop(gb_tree(), non_neg_integer()) -> term().
85 loop(Tree, VClock) ->
86 receive
87 {From, Ref, {read, Key}} -> % local read call
88 Res = case gb_trees:lookup(Key, Tree) of
89 {value, {Val, _Stamp}} -> {value, Val};
90 X -> X
91 end,
92 From ! {Ref, Res},
93 loop(Tree, VClock);
94 {From, Ref, {write, Key, Val}} -> % local write call. Broadcast to all.
95 Stamp = VClock+1,
96 broadcast({dist_write, Key, Val, Stamp}),
97 From ! {Ref, ok},
98 loop(Tree, Stamp);
99 {dist_write, Key, Val, Stamp} -> % broadcasted write call. Just Do It.
100 NewTree = case gb_trees:lookup(Key, Tree) of

c 2018 Erlang Solutions Ltd. 30


7 ADVANCED TOPICS

101 none ->


102 gb_trees:insert(Key, {Val, Stamp}, Tree);
103 {value, {_OldVal, OldStamp}} when OldStamp < Stamp ->
104 gb_trees:update(Key, {Val, Stamp}, Tree);
105 _ -> % we updated this one more recently
106 Tree
107 end,
108 loop(NewTree, 1 + max(VClock, Stamp));
109 {From, Ref, {delete, Key}} -> % local delete call. Broadcast to all.
110 Stamp = VClock+1,
111 broadcast({dist_del, Key, Stamp}),
112 From ! {Ref, ok},
113 loop(Tree, Stamp);
114 {dist_del, Key, Stamp} -> % distributed write call. Just Do It.
115 NewTree = case gb_trees:lookup(Key, Tree) of
116 none ->
117 Tree;
118 {value, {_OldVal, OldStamp}} when OldStamp < Stamp ->
119 gb_trees:delete(Key, Tree);
120 _ -> % we updated this one more recently
121 Tree
122 end,
123 loop(NewTree, 1 + max(VClock, Stamp));
124 {From, Ref, stop} ->
125 From ! {Ref, ok}
126 end.
127
128 %% Takes a message and forwards it to all the currently connected nodes
129 -spec broadcast(term()) -> ok.
130 broadcast(Msg) ->
131 [{?MODULE, Node} ! Msg || Node <- ?NODES],
132 ok.

c 2018 Erlang Solutions Ltd. 31

You might also like