Erlang

INTRODUCE

  • Built for concurrency
    • No Threading
    • Lightweight Processes
  • Functional language
  • Reliability

INSTALLATION FOR FREEBSD

# cd /usr/ports/lang/erland
# make install rehash; clean

BASIC FEATURES

root@meo:/usr/ports/lang/erlang # erl
Erlang/OTP 17 [erts-6.1] [source] [64-bit] [smp:32:32] [async-threads:10] [kernel-poll:false]

Eshell V6.1  (abort with ^G)
1>

Comments, Variables, and Expressions

  • Comment
1> % This is a comment
  • Data type
    • Integer
1> 2 + 2.
4

* Float
3> 2 + 2.0.
4.0

* String
4> "string".
"string"

*
* List
5> [1, 2, 3].
[1,2,3]
  • <span style="line-height: 1.5;">Variable MUST start with an upper case letter</span>
> 1> Var = 1.
> 1
> 2> Var = 2.
>  * exception error: no match of right hand side value 2
> 3>
>

</span>

Atoms, Lists and Tuples

  • Atom is a symbol, used to present something, and begins with a lower-case character
9> red.
red
10> Pill = blue.
blue
11> Pill.
blue
  • List can be any lengthList
13> List = [1, 2, 3].
[1,2,3]
14> List.
[1,2,3]
15>
  • Tuples are fixed-length
15> {one, two, three}.
{one,two,three}
16> Tuples = {one, two, three}.
{one,two,three}
17> Tuples.
{one,two,three}
18>
  • Use Tuple as a hash
18> {comic_strip, {name, "Calvin and Hobbies"}, {character, "Spaceman Spiff"}}.
{comic_strip,{name,"Calvin and Hobbies"},
             {character,"Spaceman Spiff"}}

Pattern Matching

  • Extract value from a tuple
1> Person = {person, {name, "Agent Smith"}, {profession, "Killing programs"}}.
{person,{name,"Agent Smith"},
        {profession,"Killing programs"}}
4> {person, {name, Name}, {profession, Profession}} = Person. 
{person,{name,"Agent Smith"},
        {profession,"Killing programs"}}
5> Name.
"Agent Smith"
6> Profession.
"Killing programs"
  • List pattern matching
7> [Head | Tail] = [1, 2, 3].
[1,2,3]
8> Head.
1
9> Tail.
[2,3]

Bit Matching

  • Pack a data structure
10> W = 1.
1
11> X = 2.
2
12> Y = 3.
3
13> Z = 4.
4
14> All = <<W:3, X:3, Y:5, Z:5>>.
  • Unpack a data structre
15> <<A:3, B:3, C:5, D:5>> = All.
<<"(d">>
16> A.
1
17> D.
4

Functions

  • Create a basic function
-module(basic).
-export([mirror/1]).
mirror(Anything) -> Anything.
  • Compile and run
1> c(basic).
{ok,basic}
2> basic:mirror(smiling_mug).
smiling_mug
3> basic:mirror(1).
1
  • Create a matching function
-module(matching_function).
-export([number/1]).
number(one) -> 1;
number(two) -> 2;
number(three) -> 3.
  • Compile and run
1> c(matching_function).
{ok,matching_function}
2> matching_function:number(one).
1
3> matching_function:number(two).
2
4> matching_function:number(three).
3
5> matching_function:number(four). 
 * exception error: no function clause matching matching_function:number(four) (matching_function.erl, line 4)

Control Structures

  • Case
1> Animal = "dog".
"dog"
2> case Animal of
2>   "dog" -> underdog;
2>   "cat" -> thundercat
2> end.
underdog
  • Use the underscore to match anything
3> case Animal of
3>   "elephant" -> dumbo;
3>   _ -> someting_else
3> end.
someting_else
  • If
4> if
4>   ProgramsTerminated > 0 ->
4>     success;
4>   ProgramsTerminated < 0 ->
4>     error
4> end.
* 2: variable 'ProgramsTerminated' is unbound
  • One of the statements must be true
5> X = 0.
0
6> if 
6>   X > 0 -> positive;
6>   X < 0 -> negative
6> end.
 * exception error: no true branch found when evaluating an if expression
  • Use true as 'else'
7> if
7>   X > 0 -> positive;
7>   X < 0 -> negative;
7>   true -> zero
7> end.
zero

Anonymous Function

  • In Erlang, you can assign arbitrary functions to variables and pass them around like any other data types
8> Negate = fun(I) -> -I end.
#Fun<erl_eval.6.90072148>
9> Negate(1).
-1
10> Negate(-1).
1

List and Higher Order Functions

  • Applying Functions to Lists
11> Numbers = [1, 2, 3, 4].
[1,2,3,4]
12> lists:foreach(fun(Number) -> io:format("~p~n", [Number]) end, Numbers).
1
2
3
4
ok
  • Build a list with lists:map
13> lists:map(fun(X) -> X + 1 end, Numbers).
[2,3,4,5]
  • Filter lists
14> Small = fun(X) -> X < 3 end.
#Fun<erl_eval.6.90072148>
15> Small(4).
false
16> Small(1).
true
17> lists:filter(Small, Numbers).
[1,2]
  • Test lists with all and any
18> lists:all(Small, [0, 1, 2]).
true
19> lists:all(Small, [0, 1, 2, 3]).
false
20> lists:any(Small, [0, 1, 2, 3]).
true
21> lists:any(Small, [3, 4, 5]).
false
  • Make a list of all the elements at the head of a list that match a filter or discard all the items at the front of a list that satisfy the filter
22> lists:takewhile(Small, Numbers).
[1,2]
23> lists:dropwhile(Small, Numbers).
[3,4]
24> lists:takewhile(Small, [1, 2, 1, 4, 1]).
[1,2,1]
25> lists:dropwhile(Small, [1, 2, 1, 4, 1]).
[4,1]
  • foldl
26> Numbers.
[1,2,3,4]
27> lists:foldl(fun(X, Sum) -> X + Sum end, 0, Numbers).
10

Advanced List Concepts

  • List Construction
  • Double each item of a list
-module(double).
-export([double_all/1]).
double_all([]) -> [];
double_all([First|Rest]) -> [First + First|doubble_all(Rest).
  • Run and compile
1> c(double)
1> .
{ok,double}
2> double:double_all([1, 2, 3]).
[2,4,6]
  • List construction
4> [1|[2,3]].
[1,2,3]
5> [[2, 3] | 1].
[[2,3]|1]
6> [[] | [2, 3]].
[[],2,3]
7> [1 | []].
[1]

List Comprehensions

  • Map the old-fashioned way
8> Fibs = [1, 1, 2, 3, 5].
[1,1,2,3,5]
9> Double = fun(X) -> X * 2 end.
#Fun<erl_eval.6.90072148>
10> lists:map(Double, Fibs).
[2,2,4,6,10]
  • Equivalent
11> [Double(X) || X <- Fibs].
[2,2,4,6,10]
12> [ X * 2 || X <- [1, 1, 2, 3, 5]].
[2,2,4,6,10]
  • More complicated example
13> Cart = [{pencil, 4, 0.25}, {pen, 1, 1.20}, {paper, 2, 0.20}].
[{pencil,4,0.25},{pen,1,1.2},{paper,2,0.2}]
14> WithTax = [{Product, Quantity, Price, Price * Quantity * 0.08} || {Product, Quantity, Price} <- Cart].
[{pencil,4,0.25,0.08},{pen,1,1.2,0.096},{paper,2,0.2,0.032}]
  • Create a new catalog with 50 percent discount
15> Cat = [{Product, Price} || {Product, _, Price} <- Cart].    
[{pencil,0.25},{pen,1.2},{paper,0.2}]
16> DiscountedCat = [{Product, Price / 2} || {Product, Price} <- Cat].
[{pencil,0.125},{pen,0.6},{paper,0.1}]

Basic Concurrency Primitives

  • A basic receive loop
-module(translate).
-export([loop/0]).
loop() ->
        receive
                "casa" ->
                        io:format("house~n"),
                        loop();
                "blanca" ->
                        io:format("white~n"),
                        loop();
                _ ->
                        io:format("I don't understand~n"),
                        loop()
end.
  • Spawning a Process
1> c(translate).
{ok,translate}
2> Pid = spawn(fun translate:loop/0).
<0.39.0>
  • Sending Messages
3> Pid ! "casa".
house
"casa"
4> Pid ! "blanca".
white
"blanca"
5> Pid ! "loco"
5> .
I don't understand
"loco"

Synchronous Messaging

  • Receiving Synchronously
root@meo:/usr/home/meo/study/erlang # vim translate_service.erl
-module(translate_service).
-export([loop/0, translate/2]).
loop() ->
        receive
                {From, "casa"} ->
                        From ! "house",
                        loop();
                {From, "blanca"} ->
                        From ! "white",
                        loop();
                {From, _} ->
                        From ! "I don't understand",
                        loop()
end.
translate(To, Word) ->
        To ! {self(), Word},
        receive
                Translation -> Translation
        end.
1> c(translate_service).
{ok,translate_service}
2> Translator = spawn(fun translate_service:loop/0).
<0.39.0>
3> translate_service:translate(Translator, "blanca").
"white"
4> translate_service:translate(Translator, "casa").
"house"

Linking Processes for Reliability

  • A Russian roulette game. It has a gun with six chambers, to fire a chamber, you just send a number 1-6 to the gun process. Enter the wrong number, and the process kills itself.
-module(roulette).
-export([loop/0]).
% send a number, 1 - 6
loop() ->
        receive
                3 -> io:format("bang.~n"), exit({roulette,die,at,erlang:time()});
                _ -> io:format("click~n") loop()
end.
  • Compile and run
1> c(roulette).
{ok,roulette}
2> Gun = spawn(fun roulette:loop/0).
<0.39.0>
3> Gun !1.
click
1
4> Gun !3.
bang.
3
5> Gun !4.1> c(roulette).
{ok,roulette}
2> Gun = spawn(fun roulette:loop/0).
<0.39.0>
3> Gun !1.
click
1
4> Gun !3.
bang.
3
5> Gun !4.
4
6> Gun !1.
1
7> erlang:is_process_alive(Gun).
false
4
6> Gun !1.
1
7> erlang:is_process_alive(Gun).
false
  • Build a monitor to check if a process id died
-module(coroner).
-module([loop/0]).
loop() ->
        process_flag(trap_exit, true),
        receive
                {monitor, Process} ->
                        link(Process),
                        io:format("Monitoring process.~n"),
                        loop();
                {'EXIT', From, Reason} ->
                        io:format("The shooter ~p died with reason ~p.", [From, Reason]),
                        io:format("Start another one.~n"),
                        loop()
end.
  • Compile and run
2> c(coroner).
{ok,coroner}
3> c(roulette).
{ok,roulette}
4> c(coroner).
{ok,coroner}
5> Revolver=spawn(fun roulette:loop/0).
<0.53.0>
6> Coroner=spawn(fun coroner:loop/0).
<0.55.0>
7> Coroner ! {monitor, Revolver}.
Monitoring process.
{monitor,<0.53.0>}
8> Revolver !1. 
click
1
9> Revolver !3.
bang.
The shooter <0.53.0> died with reason {roulette,die,at,{17,18,6}}.3
Start another one.
  • Doctor
-module(doctor).
-export([loop/0]).
loop() ->
        process_flag(trap_exit, true),
        receive
                new ->
                        io:format("Creating and monitoring process.~n"),
                        register(revolver, spawn_link(fun roulette:loop/0)),
                        loop();
                {'EXIT', From, Reason} ->
                        io:format("The shooter ~p died with reason ~p.", [From, Reason]),
                                io:format(" Restarting.~n"),
                        self() ! new,
                        loop()
                end.
  • Compile and run
10> c(doctor).
{ok,doctor}
11> Doc = spawn(fun doctor:loop/0).
<0.66.0>
12> revolver !1.
 * exception error: bad argument
     in operator  !/2
        called as revolver ! 1
13> revolver ! 1.
 * exception error: bad argument
     in operator  !/2
        called as revolver ! 1
14> Revolver !1.
1
15> Doc ! new.
Creating and monitoring process.
new
16> revolver ! 1.
click
1
17> revolver ! 3.
bang.
The shooter <0.73.0> died with reason {roulette,die,at,{17,32,30}}.3
 Restarting.
Creating and monitoring process.
18> revolver ! 4.
click
4

REFERENCES

APPENDIX

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License