<<  ruby implementation    index    single process erlang implementation  >>

brutally short intro to erlang

the obvious choice for this kind of distributed algorithm is erlang
there are lots of tutes online but here's my quick intro

all examples done in erlang shell erl

bash> erl

variables start with a capital and are immutable

erl> X = 2.
X is unbound, assigns it value 2
erl> X = 3.
ERROR! since X is already bound
erl> X = 2.
X is bound, and has value 2 so equality holds
erl> Y = 3.
Y is unbound, assigns it value 3
erl> X = Y-1.
X is value 2, Y-1 is value 2, so equality holds

(note: values in erl shell can be unbound with f(orget) function)

erl> X = 2.
erl> f().
erl> X = 3.
no error

there are various types of variables

standard numerics; ints & floats

erl> A = 2.
erl> B = 3.5.

atoms; symbolic constants that start with a lowercase

erl> C = shutdown_command.

tuples; short lists used for building structs

erl> D = {min_max,3,10}.

lists; variables sized array with lots of helper functions

erl> E = [1,2,3].
erl> length(E).
3
erl> E ++ [4].
[1,2,3,4]

functions; are first class objects

erl> G = fun(X) -> X * 2 end.
erl> G(5).
10

pattern matching

binding of variables is through pattern matching

erl> f().
erl> X = 2.
simplest pattern match there is 
erl> {min_max,Min,Max} = {min_max,3,10}.
erl> Min.
3
erl> Max.
10

special variable 'underscore' used for matching values we don't care about

erl> f().
erl> {min_max,Min,5} = {min_max,4,6}.
FAIL, no match. 5 != 6
erl> {min_max,Min,_} = {min_max,4,6}.
erl> Min.
3

some special list matching with 'pipe' list splitter

erl> [Head|Tail] = [1,2,3].
erl> Head.
1
erl> Tail.
[2,3]

most looping code implemented in function programming style, ie recursively
using pattern matching a different version of the overloaded sum function is called

sum([]) ->
    0;
sum([H|T]) ->
    H + sum(T).

executed as

sum([1,2,3])
= 1 + sum([2,3])
= 1 + ( 2 + sum([3]) )
= 1 + ( 2 + ( 3 + sum([]) ) )
= 1 + ( 2 + ( 3 + ( 0 ) ) )
= 6

actually compiled though to use an accumulator
thus can use last call optimisation to avoid recursive stackoverflow
(ie can then be implement as loop using jump operation).

sum(L) ->
    sum(L,0);
sum([],Acc) ->
    Acc;
sum([H|T],Acc) ->
    sum(T, Acc+H).

executed as

sum([1,2,3])
= sum([1,2,3],0)
= sum([2,3],0+1)
= sum([3],1)
= sum([3],1+2)
= sum([3],3)
= sum([],3+3)
= sum([],6)
= 6

concurrency support

erlangs main strength is it's concurrency support
any function call can be run asynchronously using the spawn command

main() ->
    say_word_n_times('hello',4),
    say_word_n_times('goodbye',4).
results in
hello
hello
hello
hello
goodbye
goodbye
goodbye
goodbye
main() ->
    spawn(fun() -> say_word_n_times('hello',4) end),
    spawn(fun() -> say_word_n_times('goodbye',4) end).
results in something like
hello
goodbye
hello
hello
goodbye
goodbye
hello
goodbye

spawn creates a new erlang 'process'.
each process has it's own context and runs on one of N erlang managed system threads.
processes are extremely lightweight and very cheap to create/destroy; it's quite feasible to have thousands of running processes

processes can only communicate through messaging.
a process can be sent a message with the ! operator.
messages are queued in a mailbox and handling with the receive operator.

main() ->
    P = spawn(fun() -> message_loop() end),
    P ! hello,    
    P ! hello,
    P ! goodbye,
    P ! hello.

message_loop() ->
    receive
	hello ->
	    io:format("i got a hello\n"),
	    message_loop();
	goodbye ->
	    io:format("bye bye!\n")
    end.
results in
i got a hello
i got a hello
bye bye!
main process spawns a process that enters the message loop
main process then sends 4 messages to spawned process
spawned process receives a 'hello', writes some output and reenter message loop
spawned process receives a 'hello', writes some output and reenter message loop
spawned process receives a 'shutdown', writes some output but doesnt reenter message loop
final hello message unprocessed

the real power comes from fact processes can be spawned just as easily on another erlang vm

    P = spawn(AnotherNode, fun() -> message_loop() end),
no other change to the code is required.

<<  ruby implementation    index    single process erlang implementation  >>

nov 2008