A FEW ERLANG EXAMPLES %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -module(counter). -export([start/0,loop/1,increment/1,value/1,stop/1]). %% First the interface functions. start() -> spawn(counter, loop, [0]). increment(Counter) -> Counter ! increment. value(Counter) -> Counter ! {self(),value}, receive {Counter,Value} -> Value end. stop(Counter) -> Counter ! stop. %% The counter loop. loop(Val) -> receive increment -> loop(Val + 1); {From,value} -> From ! {self(),Val}, loop(Val); stop -> % No recursive call here true; Other -> % All other messages loop(Val) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %A Simple Client-Server system: -module(message). -export([loop/0,client/2]). loop() -> receive {From,{add, A, B}} -> From!A+B, loop(); {From,{mul, A, B}} -> From!A*B, loop(); {From,{minus, A, B}} -> From!A-B, loop(); {From,{division, A, B}} -> From!A/B, loop(); _Other -> loop() end. client(Pid,Request)-> Pid ! {self(), Request}, receive Response ->io:format("Response is ~p~n" ,[Response]) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% An Erlang program simulating a finite state machine recognizing the regular language represented by the regular expression abc*+c(bb)*. Each state has to be represented by a process. (by F.Barbanera) -module(regExpr). -export([manager/0,state/2,create/0]). % Module for automaton for a regular languages on {a,b,c}. % % Each state is a process. Many strings can be parsed in parallel. % % create : creates a particular automaton (the one corresponding to (ab)* in this simple example) % together with its manager. % % A parsing request can be sent to the manager by means of a triple {requestParsing,ID,List} % where ID is the PID of whom makes the request, and List is the string to be parsed. % % For sake of simplicity a string is represented by a list of atoms that represents characters. % % The manager sends the request to the initial state and receives the answer that is then % delivered to whom made the request. % The original string to be parsed is kept through all the parsing process and sent back with the % answer in order one can send onother request even before one gets the answer of the previous request. % % manager : It is the function corresponding to the manager of the automaton. % Once spawned by the create function, the manager waits for the address of the initial state, % that the create function itself sends to it (This procedures is a simple trick necessary since % the manager and the initial state need the PID of each other.) % % state : It is the function corresponding to a state. Its arguments are the PID of the manager % and one atom (final or nofinal) representing whether the spawned state is final or not. % Once spawned by the create function, a state waits for a triple of PIDs % corresponding, respectively, to the states it has to be connected to for the characters a, b, or c. % In case a state is not connected to another state for a particular character (and hence the % string has to be rejected), the corresponding element in the triple is the atom none instead of a PID. % (As for the manager, this trick is needed in order to have automata with cliques. %% abc*+c(bb)* % % a b c Final States = q2, q3 % init q1 q3 % q1 q2 % q2 q2 % q3 q4 % q4 q3 % create () -> %%we create the manager and the (process-)states. The PID of manager is returned. IDmanager = spawn(exprReg,manager,[]), IDinit = spawn(exprReg,state,[IDmanager,notfinal]), IDq1 = spawn(exprReg,state,[IDmanager,notfinal]), IDq2 = spawn(exprReg,state,[IDmanager,final]), IDq3 = spawn(exprReg,state,[IDmanager,final]), IDq4 = spawn(exprReg,state,[IDmanager,notfinal]), %%we connect the manager to qinit and the various states among themselves IDmanager!IDinit, IDinit!{IDq1,none,IDq3}, IDq1!{none,IDq2,none}, IDq2!{none,none,IDq2}, IDq3!{none,IDq4,none}, IDq4!{none,IDq3,none}, IDmanager. manager() -> receive IDinitial -> loopmanager(IDinitial) end. loopmanager(IDinitial) -> receive {requestParsing,ID,List} -> IDinitial ! {List,ID,List}; {accepted,ID,List} -> ID ! {accepted,List}; {rejected,ID,List} -> ID ! {rejected,List} end, loopmanager(IDinitial). state(IDmanager,Final) -> receive {IDA,IDB,IDC} -> loop(IDA,IDB,IDC,IDmanager,Final) end. loop(IDA,IDB,IDC,IDmanager,Final) -> receive {[],ID,List} -> if (Final == final) -> IDmanager ! {accepted,ID,List}; true -> IDmanager ! {rejected,ID,List} end; {[ Char | Cs ],ID,List} -> Next = (if (Char == a) -> IDA; (Char == b) -> IDB; (Char == c) -> IDC end), if (Next == none) -> IDmanager ! {rejected,ID,List}; true -> Next ! {Cs,ID,List} end end, loop(IDA,IDB,IDC,IDmanager,Final). % The above program can be modified in order the function create takes the description of % a finite automaton (its transition table) and returns the PID of the manager of the % corresponding system. Such a program is described in the solution of Exercise 14 in % http://www.dmi.unict.it/~barba/PROG-LANG/ESERCIZI/EserciziActorsErlang/esercizi.html %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %Token Ring % The following example has been taken from % http://trigonakis.com/blog/2011/05/26/introduction-to-erlang-message-passing/ %This application creates NumNodes processes and arranges them in a ring %(every process has one next process). %Then the coordinator “inserts” a token in the first node of the ring. %Every node (process) receiving the token increases its value by 1 and %sends it to the next node. The application stops when the token has value %greater than the MAXVAL. -module(ring). -export([create/1, node/2, connect/1]). -define(MAXVAL, 100000). %creates the ring's nodes, connects them in a ring, sends the token in the ring, and %collects the exit messages from the nodes create(NumNodes) when is_integer(NumNodes), NumNodes > 1 -> Nodes = [spawn(?MODULE, node, [ID, self()]) || ID <- lists:seq(1, NumNodes)], % notice that the above expression denotes a list using % the mechanism of list comprehension, similar to Haskell list comprehension % Remember that the function spawn returns a process identifier, % so to the Nodes variable we associate the list of the created process nodes. ring:connect(Nodes), hd(Nodes) ! {token, 0}, getexits(Nodes). %collects the exit messages from the nodes getexits([]) -> io:format("[Coord] Done.~n"), ok; getexits(Nodes) -> receive {Node, exit} -> case lists:member(Node, Nodes) of true -> getexits(lists:delete(Node, Nodes)); _ -> getexits(Nodes) end end. %little trick in order to connect the last with the first node %handle the [nd0, nd1, ..., ndN] list as [nd0, nd1, ..., ndN, nd0] % Notice the use of a particular sort of pattern matching, enabling % to associate the whole input to the variable N and the head of the % input list to the variable H. % This particular sort of pattern matching is present also in Haskell (as-pattern) % and PICT (layered pattern). % In Haskell and PICT the following notation is used: x@p (where x is a variable and p a pattern). connect(N = [H | _]) -> connect_(N ++ [H]). %connects the nodes to form a ring connect_([]) -> connected; connect_([_]) -> connected; connect_([N1, N2 | Nodes]) -> N1 ! {self(), connect, N2}, connect_([N2 | Nodes]). % The computation in each process node consists in the evaluation of % the node function below. % The node function initially waits for the next node's pid and then proceed % by evaluating the other node function (that can be recognized as different from % the first one, since it has three arguments instead of two. node(ID, CrdId) -> receive {CrdId, connect, NxtNdId} -> io:format("[~p:~p] got my next ~p~n", [ID, self(), NxtNdId]), node(ID, CrdId, NxtNdId) end. %the main functionality of a node; receive the token, increase its value and send %it to the next node on the ring node(ID, CrdId, NxtNdId) -> receive {token, Val} -> if Val < MAXVAL -> NxtNdId ! {token, Val + 1}, node(ID, CrdId, NxtNdId); true -> io:format("[~p:~p] token value ~p~n", [ID, self(), Val]), case erlang:is_process_alive(NxtNdId) of true -> NxtNdId ! {token, Val + 1}; _ -> ok end, CrdId ! {self(), exit}, done end %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% % The (sequential) quicksort in Erlang. This example (but the Haskell version of % the partition function) is partially taken from slides % by David Chisnall for the talk % Introduction to Erlang A Language For Message-Passing Concurrency -module (qs). -export ([qs /1]). qs([]) -> [] ; qs([X]) -> [X] ; qs( List ) -> [ Pivot | Sublist ] = List , {Less , Greater } = partition ( Sublist , Pivot ,[],[]), qs( Less ) ++ [ Pivot ] ++ qs( Greater ). % Notice how in Erlang we can have sequences of expressions, as in OCaml. % As in OCaml, the value of a sequence of expressions is the value of the % last one, the previous are usually used for the effects their evaluation have % (tipically the introduction of a local valriable, like the use of 'where' or 'let' % in Haskell) partition ([],_,Less , Greater ) -> {Less , Greater } ; partition ([X| List ],Pivot , Less , Greater ) -> if X > Pivot -> partition (List , Pivot , Less , [X| Greater ]) ; true -> partition (List , Pivot , [X| Less ], Greater ) end . % Notice how this fuction is a tail-recursive version of the partition fuction. % Erlang can recognize tail-recursive functions and compile them very efficiencely. % Fuctions corresponding to the computation of a process should be necessarily % tail-recursive whenever the process can go on indefinitely. % Let us see a non tail-recursive version of the partition function in Haskell: % This version returns a pair of list, whereas the Erlang one returns a tuple of % two lists. partition [] piv = ([],[]) partition (x:xs) piv | x Parent ! {Tag ,[]} ; pqs([X],Parent ,Tag) -> Parent ! {Tag ,[X]} ; pqs(List , Parent , Tag) -> [ Pivot | Sublist ] = List , {Less , Greater } = partition ( Sublist , Pivot ,[], []), spawn (pqs ,pqs ,[Less , self () , less ]), spawn (pqs ,pqs ,[ Greater , self () , greater ]), receive {less , LessSorted } -> true end , receive { greater , GreaterSorted } -> true end , Parent ! {Tag , LessSorted ++ [ Pivot ] ++ GreaterSorted }. % Notice how we need to use two 'receive' expressions, since we need to be sure % to receive both the to ordered sublists before going on.