Charla de erlang
El viernes pasado di una charla sobre erlang en el trabajo. En la misma, muestro como escribir programas secuenciales en erlang y después un poco de programación distribuida. Para presentar usé esta presentación (ojo, tenés que abrirla con OpenOffice). Abajo pongo lo que planifiqué dar (que es muy parecido a lo que di).
Programación secuencial
"Variables" inmutables
1> X = 1.
1
2> X = 2.
** exception error: no match of right hand side value 2
3> X = 1.
1
Atomos
4> aaaa.
aaaa
5> bbbb.
bbbb
6> Y = bbbb.
bbbb
7> Y.
bbbb
8> Y = aaaa.
** exception error: no match of right hand side value aaaa
Tuplas y listas (y pattern matching)
9> L = [1, 2, 3]
.
[1,2,3]
10> L2 = [-1, 0 | L]
.
[-1,0,1,2,3]
11> [H1, H2, H3 | _ ] = L2
.
[-1,0,1,2,3]
12> H1.
-1
13> H2.
0
14> H3.
1
15> Yo = {person, {fname, "Aureliano"}, {lname, "Calvo"}}.
{person,{fname,"Aureliano"},{lname,"Calvo"}}
16> Yo.
{person,{fname,"Aureliano"},{lname,"Calvo"}}
17> {person, {fname, Name},_} = Yo.
{person,{fname,"Aureliano"},{lname,"Calvo"}}
18> Name.
"Aureliano"
Expresiones case
34> X = 1. EL = []. L = [1,2,3].
1
35> []
36> [1,2,3]
38> case X of [] -> empty_list; [H|T] -> {head, H}; _ -> not_a_list end.
not_a_list
39> case EL of [] -> empty_list; [H|T] -> {head, H}; _ -> not_a_list end.
empty_list
40> case L of [] -> empty_list; [H|T] -> {head, H}; _ -> not_a_list end.
{head,1}
Funciones
En fruit.erl,
price( pear ) -> 4.0;
price( banana ) -> 3.0;
price( apple ) -> 6.0;
price( orange ) -> 2.0;
En consola,
13> fruit:price( pear ).
4.0
14> fruit:price( banana ).
3.0
Usando pattern matching un toque más en serio: En fruit.erl,
price( [] ) -> 0.0;
price( [{Kilos, Fruit}|Rest] ) -> Kilos * price( Fruit ) + price( Rest ).
En la consola,
15> fruit:price( [] ).
0.0
18> fruit:price( [{2.5, banana}, {1.5, apple}] ).
16.5
Pero para que use mejor el espacio (y no se acabe el stack), lo escribo tail recursive (acá explico que el stack sino se va a la mierda):
price2( L ) -> price2( L, 0.0 ).
price2( [], Acum ) -> Acum;
price2( [{Kilos, Fruit}|Rest], Acum ) -> price2( Rest, Kilos * price( Fruit ) + Acum ).
Programación distribuida
spawn, send y receive
spawn lanza un proceso nuevo de erlang, send (!) manda un mensaje a un proceso, y receive lo recibe.
Hago el fruit_shop.erl.
42> c( fruit_shop).
{ok,fruit_shop}
43> FS = fruit_shop:spawn_shop( [ {banana, 15.0}, {pear, 10.0}, {apple, 10.0}], [ {banana, 3.0}, {pear, 4.0}, {apple, 6.0} ] ).
Y lo uso:
(a@tango)3> fruit_shop:stock(FS).
[{banana,15.0},{pear,10.0},{apple,10.0}]
(a@tango)8> fruit_shop:buy(FS, [{2.0, apple}]).
{purchased,12.0}
(a@tango)10> fruit_shop:stock(FS).
[{apple,8.0},[{banana,15.0},{pear,10.0}]]
Por último, lanzar procesos en otros nodos es casi lo mismo: (spawn con un parámetro más, todo el resto igual).
Pongo 2 consolas de erlang:
>erl -sname a -setcookie oreo
>erl -sname b -setcookie oreo
En la primera lanzo la frutería sobre el segundo nodo:
(a@tango)7> FS = fruit_shop:spawn_shop( 'b@tango', [ {banana, 15.0}, {pear, 10.0
}, {apple, 10.0}], [ {banana, 3.0}, {pear, 4.0}, {apple, 6.0} ] ).
Hago lo mismo que antes:
(a@tango)3> fruit_shop:stock(FS).
[{banana,15.0},{pear,10.0},{apple,10.0}]
(a@tango)8> fruit_shop:buy(FS, [{2.0, apple}]).
{purchased,12.0}
(a@tango)10> fruit_shop:stock(FS).
[{apple,8.0},[{banana,15.0},{pear,10.0}]]
Mato la segunda consola y ahora las operaciones dan timeout:
(a@tango)11> fruit_shop:stock(FS).
timeout
(a@tango)12> fruit_shop:buy(FS, [{2.0, apple}]).
timeout