Introduction:
Investigating languages/frameworks to scale up applications I came across to Erlang. I read a lot on the internet on the topic and found interesting tutorials. I thought to wrap up the investigation done so far and share the tutorial files I put together which allowed me to get started with Erlang in less than 1 hr.
Tutorial Files: Erlang in 5 minutes
NOTE: Files are attached to this page.
INTRODUCTION:
This tutorial in based on the tutorial available on the erlang web site. It runs under windows (in my case).
Please download Erlan and the documentation as provided below.
Download Erlang/OTP: http://www.erlang.org/download.html
Download the tutorial:www.erlang.org/download/getting_started-5.4.pdf
SETUP:
Create folder "Erlang" and "cd" into it (in my case it was located under "F:\Erlang")
Copy the files attached in this tutorial.
Execute "Erlang"
F:\Erlang>"<root>\erl6.3\bin\werl.exe"
START:
1. Compile "tut" module
>c(tut).
{ok,tut}
a new file called "tut.beam" is created in the local directory
NOTE: module content
---------------------------------------------------------------------------------
-module(tut). % module definition
-export([double/1]). % make the function "double" visible outside the module
double(X) -> % "double" definition
2 * X. % function logic
---------------------------------------------------------------------------------
The double function can be invoked like this:
1> tut:double(10).
20
2. Compile "tut2.erl"
2> c(tut2)
2> .
{ok,tut2}
Invoke the "convert" function for inch values
3> tut2:convert(3, inch).
1.1811023622047243
4> tut2:convert(7, centimeter).
17.78
Lesson learnt: "Atoms"
This module/function uses "Atoms". inch/centimeter are atoms (string with all lower case chars). Atoms are simply names, nothing else. They are not like variables which can have a value.
3. Compile "tut3.erl"
6> c(tut3).
{ok,tut3}
Invoke the function "convert_lenght" with "tuples"
7> tut3:convert_length({inch, 5}).
{centimeter,12.7}
Lesson learnt: "Tuples"
Tuples are Erlang way to group things together (more readable).
Tuples are surrounded by “{” and “}”.
So we can write {inch,3} to denote 3 inches and {centimeter,5} to denote 5 centimeters
Processess | Threads
In Erlang a "Process" is a thread.
4. Let's compile "tut14.erl"
11> c(tut14).
tut14.erl:3: Warning: variable 'What' is unused
{ok,tut14}
Let's invoke say_something with iteration = 2.
13> tut14:say_something(hello, 2).
hello
hello
done
14>
13> tut14:say_something(hello, 2).
hello
hello
done
14> tut14:start().
hello
goodbye
hello
goodbye
<0.70.0>
hello
goodbye
15>
Lesson learnt: "Processes"
Start() will "spawn" 2 processes (Threads) and since a function only return last function invocation in its body
<0.70.0> is the Pid of the last "spawn" in say_something
Passing Messages between Processes
5. Let's compile "tut15.erl"
15> c(tut15)
15> .
{ok,tut15}
16> tut15: start().
Pong received ping
Ping received pong
<0.78.0>
Pong received ping
Ping received pong
Pong received ping
Ping received pong
ping finished
Pong finished
17>
Let's analyze the module:
--------------------------------------------------------------------------------------------------------------------------
-module(tut15). %Module Definition
-export([start/0, ping/2, pong/0]). %Export functions ping,pong,start
ping(0, Pong_PID) ->
Pong_PID ! finished, %function pong(0,PidNum) send a message (atom-finished) to Process with Pid PidNum
io:format("ping finished~n", []);
ping(N, Pong_PID) ->
Pong_PID ! {ping, self()}, %function ping(N, Pong_PID) sends a message (tuple - {ping, Pid}) to Process with Pid Pong_PID
%self() - returns the Process Identity (Pid) of the process executing this function.
receive %receive is blocking in no new message (for the full behavior read on page 27)
pong ->
io:format("Ping received pong~n", [])
end,
ping(N - 1, Pong_PID).
pong() ->
receive % Pong waits for messages:
finished -> % if the message is "finished" (atom) the message is printed on screen
io:format("Pong finished~n", []);
{ping, Ping_PID} -> % if a tuple is received with format {ping, Ping_PID} then the pong message is sent to the process with pid "Ping_PID"
io:format("Pong received ping~n", []),
Ping_PID ! pong,
pong() % pong goes back in execution otherwise it would have exited (Is this same as recursion ???).
end.
start() ->
Pong_PID = spawn(tut15, pong, []), % Starts pong no arguments.
spawn(tut15, ping, [3, Pong_PID]). % Starts ping with arguments 3, Ping_PID
--------------------------------------------------------------------------------------------------------------------------
6. Compile "my151.erl"
In order to understand better communication between processes I created my151.erl which isolates the pong functionality.
> c(my151).
39> my151:start().
<0.101.0>
40> pid(0,101,0) ! ANYMESSAGE.
* 1: variable 'ANYMESSAGE' is unbound % ANYMESSAGE is not an atom (got to be lower case)
41> pid(0,101,0) ! anymessage. % anymessage does not match any of the messages in teh receive section of "pong" so the message does not
anymessage
42> pid(0,101,0) ! anymessage2. %unmatched
anymessage2
43> pid(0,101,0) ! anymessage3. %unmatched
anymessage3
This blog contains important information related to unmatched messages: http://ndpar.blogspot.se/2010/11/erlang-explained-selective-receive.html
52> process_info(pid(0,101,0)).
[{current_function,{my151,pong,0}},
{initial_call,{my151,pong,0}},
{status,waiting},
{message_queue_len,3},
{messages,[anymessage,anymessage2,anymessage3]},
...............................
The "module my151" is still running (check via i().)
53> pid(0,101,0) ! {ping,self()}. % message that matches
Pong received ping
Pong Exited
{ping,<0.78.0>}
54>
Module exited this time.
7. Compile "my152.erl"
69> my152:start().
<0.147.0>
70> process_info(pid(0,147,0)).
[{current_function,{my152,pong,0}},
{initial_call,{my152,pong,0}},
{status,waiting},
{message_queue_len,0},
{messages,[]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.25.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,2},
{reductions,1},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
71> pid(0,147,0) ! anymessage.
anymessage
72> pid(0,147,0) ! anymessage1.
anymessage1
73> pid(0,147,0) ! anymessage2.
anymessage2
74> process_info(pid(0,147,0)).
[{current_function,{my152,pong,0}},
{initial_call,{my152,pong,0}},
{status,waiting},
{message_queue_len,3},
{messages,[anymessage,anymessage1,anymessage2]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.25.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,2},
{reductions,1},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
75> pid(0,147,0) ! {ping,self()}.
Pong received ping
{ping,<0.78.0>}
76> pid(0,147,0) ! {ping,self()}.
Pong received ping
{ping,<0.78.0>}
77> pid(0,147,0) ! {ping,self()}.
Pong received ping
{ping,<0.78.0>}
78> process_info(pid(0,147,0)).
[{current_function,{my152,pong,0}},
{initial_call,{my152,pong,0}},
{status,waiting},
{message_queue_len,3},
{messages,[anymessage,anymessage1,anymessage2]},
{links,[]},
{dictionary,[]},
{trap_exit,false},
{error_handler,error_handler},
{priority,normal},
{group_leader,<0.25.0>},
{total_heap_size,233},
{heap_size,233},
{stack_size,8},
{reductions,46},
{garbage_collection,[{min_bin_vheap_size,46422},
{min_heap_size,233},
{fullsweep_after,65535},
{minor_gcs,0}]},
{suspending,[]}]
79>
This time the process is still alive.
USEFUL SHELL COMMANDS:
- View process information:
In an erlang shell we can get process information by calling i().
- Kill processes knowing the PID.
exit(pid(0,48,0),kill).
NOTE: the Pid can be found by getting the list of processes via i().
OTHER TUTORIALS: