Académique Documents
Professionnel Documents
Culture Documents
html
Erlang Overview
Erlang is a programming language which has many features more commonly associated with an
operating system than with a programming language: concurrent processes, scheduling, memory
management, distribution, networking, etc.
The initial open-source Erlang release contains the implementation of Erlang, as well as a large part of
Ericsson's middleware for building distributed high-availability systems.
Concurrency - Erlang has extremely lightweight processes whose memory requirements can vary
dynamically. Processes have no shared memory and communicate by asynchronous message passing.
Erlang supports applications with very large numbers of concurrent processes. No requirements for
concurrency are placed on the host operating system.
Robustness - Erlang has various error detection primitives which can be used to structure fault-tolerant
systems. For example, processes can monitor the status and activities of other processes, even if these
processes are executing on other nodes. Processes in a distributed system can be configured to fail-over
to other nodes in case of failures and automatically migrate back to recovered nodes.
Soft real-time - Erlang supports programming "soft" real-time systems, which require response times
in the order of milliseconds. Long garbage collection delays in such systems are unacceptable, so Erlang
uses incremental garbage collection techniques.
Hot code upgrade - Many systems cannot be stopped for software maintenance. Erlang allows
program code to be changed in a running system. Old code can be phased out and replaced by new
code. During the transition, both old code and new code can coexist. It is thus possible to install bug
fixes and upgrades in a running system without disturbing its operation.
Incremental code loading - Users can control in detail how code is loaded. In embedded systems, all
code is usually loaded at boot time. In development systems, code is loaded when it is needed, even
when the system is running. If testing uncovers bugs, only the buggy code need be replaced.
External interfaces - Erlang processes communicate with the outside world using the same message
1 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
passing mechanism as used between Erlang processes. This mechanism is used for communication with
the host operating system and for interaction with programs written in other languages. If required for
reasons of efficiency, a special version of this concept allows e.g. C programs to be directly linked into
the Erlang runtime system.
Components
Open-source Erlang comes with several standalone components that can be used as building blocks
when developing applications. These components understands Erlang's systems messages (load, unload,
start, stop, restart, change code).
Mnesia - Distributed real-time database for Erlang. Supports RAM-replication as well as disk storage,
allows dynamic schema changes, allows arbitrarily complex data structures to be stored. Mnesia is very
fast since it runs in the same address space as the applications that use it - this is possible since both
Mnesia and the applications are written in Erlang. Mnesia is a nice example of the power of Erlang: in
how many languages could you write a fully-featured industrial-strength distributed DBMS in less than
20,000 lines of code?
ASN.1 - Compile-time and runtime package which supports the ASN.1 Basic Notation and the
encoding rules BER, DER and PER (aligned).
ERTS - Erlang runtime system, including the virtual machine, the garbage collector, and the port
mapper daemon.
IC - Compiler from OMG's Interface Definition Language (IDL) to Erlang and C and Java.
Kernel - C code necessary to run the Erlang system: Erlang built-in functions (BIFs); code, boot and
name servers; networking and distribution support; loaders, linkers and loggers; OS and file system
interfaces.
2 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
Mnesia Session - Foreign languages interface to Mnesia defined in IDL, providing Mnesia access via
the IIOP and erl_interface protocols.
OS monitor (OS_MON) - Monitoring of CPU, disk and memory utilization, including SNMP v1/v2
MIBs. Interfaces to Solaris syslogd and Windows NT event log.
Parse tools - LALR-1 parser generator for Erlang (yecc), similar to yacc. Yecc takes a BNF grammar
definition as input, and produces Erlang code for a parser as output. Yecc is used to generate the Erlang
parser.
PMan - Tool for tracing and viewing the state of Erlang processes (locally or on remote nodes).
SASL - Progress/error/crash report handling, report browsing, release handling, overload regulation.
Stdlib - Libraries for: input/output; incore and disk-based table storage (ETS and DETS); graphs,
dictionaries, lists, strings, sets, queues; regular expressions; math. Erlang interpreter, tokenizer, parser,
lint and pretty-printer. Generic frameworks for fault-tolerant servers, event handlers, state machines, and
process supervisors. Etc, etc.
Tools - Coverage analyser, profiler, text-based tracer, Emacs mode, Emacs TAGS file generator, make
utility, call graph utility.
Erlang in 14 Examples
The following sections describe the basic features of Erlang through a number of small examples. This
is hardly enough to learn the language, so check out the links at the end of this document for suggested
further reading.
Sequential Erlang
Example 1 - Factorial
The Erlang program for computing factorial closely resembles this definition:
fac(N) when N > 0 -> N * fac(N-1);
3 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
This program has two clauses, one for each of the cases (N>0 and N=0). When computing, say,
fac(5) we call each of the clauses in order - the first to match the call is selected and executed (the first
clause in this case, since 5>0).
then the call matches the clause if X matches Y and the Test is true. The arguments X and Y match if they
are identical or can be made identical by binding variables in Y to corresponding values in X. The call
fac(5) matches the first clause, since fac(5) is made identical to fac(N) by binding N to 5 and 5 > 0
is true (but fac(5) does not match the second clause, since it is not identical to fac(0)).
All Erlang functions are defined in modules. A module math containing the factorial function can be
written:
-module(math).
-export([fac/1]).
Once a module has been compiled and loaded into the system the query evaluator can be used for
function evaluation:
mymachine> erl
Erlang (JAM) emulator version 4.7.3
Example 2 - Last
4 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
Erlang uses the following syntax for linked lists (with three elements in this example): [X,Y,Z]. The
notation [First|Rest] means that First is the first element of the list (X in our example) and Rest is
the remainder of the list ([Y,Z] in our example). The empty list is denoted [].
Example 3 - Append
The second clause says that concatenating [First|Rest] with a list List yeilds a list whose first
element is First and whose remainder is the list resulting from concatenating Rest with List.
Note here that the notation [...|...] has different meanings in the function head and function body. In
the head it denotes list decomposistion, while in the body it denotes list construction.
Example 4 - Sort
The notation [Expr || Qualifier1, Qualifier2, ...] introduces a list comprehension. Here
Expression is an arbitrary expression, and each Qualifier is either a generator or a filter.
The list of X such that X is taken from the list [1,2,a,3,4,b,5,6] and X is an integer and X is greater than
3.
Here X <- [1,2,a,3,4,b,5,6] is a generator, and integer(X) and X > 3 are filters. This list
comprehension evaluates to [4,5,6].
Example 5 - Adder
5 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
This program looks up a numeric key in a dictionary represented as a binary tree. The value stored with
the key is returned. The binary tree is represented as a tuple {Key,Val,Left,Right}, where Left and
Right are the left and right subtrees. (Tuples store fixed numbers of arguments, which themselves may
be tuples or any other data type).
lookup(Key, {Key1,Val,Left,Right}) when Key == Key1 ->
Val;
lookup(Key,{Key1,Val,Left,Right}) when Key < Key1 ->
lookup(Key, Left);
lookup(Key, {Key1,Val,Left,Right}) when Key > Key1 ->
lookup(Key, Right).
We can simplify the first clause by replacing Key1 with Key in the head, since Key == Key1 for a call to
match this clause (the test Key == Key1 is then redundant):
lookup(Key, {Key,Val,Left,Right}) ->
Val;
This works as follows: The first occurrence of Key is bound to an integer n in the call; then the second
occurrence - which is now bound to n - is compared to the key in the current root node of the tree. If we
have a match, the corresponding value is returned.
(Note also that the test Key > Key1 in the third clause is redundant, since clauses are matched in
sequential order. However, it is good programming practice to include it for clarity.)
Concurrent Erlang
The next program is an "area server" - you can ask the server what the area of a square or rectangle is,
or, you can ask it to return the total of all areas that it has been requested to compute (the total is the local
state kept by the server). Messages to the server are tuples {Pid,Request}, where Pid is the client's
process identifier.
-module(area_server).
-export([start/1, loop/1]).
start() ->
6 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
loop(Tot) ->
receive
{Client, {square, X}} ->
Client ! X*X,
loop(Tot + X*X);
{Client, {rectangle,X,Y}} ->
Client ! X*Y,
loop(Tot + X*Y);
{Client, areas} ->
Client ! Tot,
loop(Tot)
end.
The Erlang primitive spawn(Module,Fun,Args) creates a parallel process which evaluates Module:Fun
with arguments from the list Args. It returns a process identifier (Pid) which can be used to
communicate with the new process.
The expression Pid ! Msg sends the message Msg to the process Pid. Sending a message is a
non-blocking operation. Messages are received using the receive ... end construction, which selects
what message to receive by matching the message with the receive clauses in sequential order. The
receive operation is blocking - it suspends until a matching message is delivered to the process. This is
how synchronization is expressed in Erlang.
Client code which uses the above server can be written (self() returns the client's Pid):
Server ! {self(), {square, 10}},
receive
Area ->
Area
end
Here the area is simply returned from the receive statement.
In the above examples, the process identifier of the server had to made known to the client. To provide a
named service we can instead register the process under a name, which is local to the node:
Pid = spawn(Module,Fun,Args),
register(area_server, Pid)
This associates the name area_server with Pid. Now any process evaluating in the node can send a
message to area_server:
area_server ! SomeMessage
Distribution
We can start Erlang systems (nodes) on several machines and have them talk to each other. Here is a
7 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
simple example.
-module(ex9).
-export([start/0, world/0]).
start() ->
Pid = spawn('b@host2,ex9,world,[]),
Pid ! {self(), 'hello world!'},
receive
Msg ->
io:format('~w~n',[Msg])
end.
world() ->
receive
{Pid,'hello world!'} ->
io:format(user,'hello world!~n',[]),
Pid ! 'hello, little thread'
end.
Running the example on two machines yields the following:
host1>erl -sname a host2>erl -sname b
a@host1>c(ex9). b@host1>c(ex9).
a@host1>ex9:start(). b@host2>
a@host1> b@host2>hello, world!
a@host1>hello, little thread b@host2>
Error detection
Erlang is designed for programming "robust" systems, so there are a number of primitives for trapping
errors. Error recovery is not automatic. The programmer must design a fault-tolerant architecture which
can be implemented using the error detection mechanisms.
Example 10 - Catch
All Erlang programs run within Erlang processes. If a runtime error occurs in a program, it crashes the
process it runs within but leaves other processes intact. So, for example, if you evaluate 1/0 within a
process, the process crashes with the printout:
** exited: {badarith,{erl_eval,eval_op,['/',1,0]}} **
(In fact, the Erlang shell detects the process crash and prints the message; see Example 13.) A computation Expr can
also be enclosed within catch/1, that is we run (catch Expr) instead of just Expr. Any error within
Expr is then converted to a data structure describing the error and the process does not crash. Thus
(catch 1/0) returns the tuple:
{'EXIT',{badarith,{erl_eval,eval_op,['/',1,0]}}}
Non-local returns can be performed with throw(Expr), which causes Expr to be evaluated and returned
by the enclosing catch. Say that f/1 is a function that may throw an exception:
f(X) when X > 0 ->
...
8 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
NormalReturnValue;
f(X) when X =< 0 ->
...
throw({exception1, ...}).
In some other part of the program we have a call to f/1, which is wrapped by a catch:
case catch f(X) of
{exception1, Why} ->
handle_exception(Why,...);
NormalReturnValue ->
handle_normal_case(NormalReturnValue,...)
end
Processes can be linked together. If a process dies an error message is sent to all processes to which it is
linked and which have set the flag trap_exit.
process_flag(trap_exit, true),
Pid = spawn_link(Mod, Fun, Args),
receive
{'EXIT', Pid, Why} ->
handle_process_crash(Why,...);
...
end
Here spawn_link(Mod,Fun,Args) creates a parallel process and links the parent with the child.
If the child process crashes then an error message is sent to all processes which the child process is
linked to. In our example an error message is sent to the parent process. The parent process can receive
the error message and take appropriate action (for example, restart the child process).
-module(ex13).
-export([start/0, loop/0]).
start() ->
process_flag(trap_exit, true),
parent(Child, 5),
Child = spawn(ex13, child, []).
parent(Child, K) ->
receive
{'EXIT', Child, Why} when K > 0 ->
io:format('child died with reason ~p~n', [Why]),
NewChild = spawn_link(ex13, child, []),
parent(NewChild, K-1);
{'EXIT', Child, _} ->
io:format('too many restarts, bye~n', [])
end.
9 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
Program ex13 implements a simple supervisor, which restarts child processes that die. We can easily
make Parent supervise Child remotely: just spawn the processes on two separate nodes. The rest of the
program is unchanged.
Erlang is designed for "non-stop" systems. We need to be able to replace code and data without
stopping the system. This example shows how we can change code in a server, without stopping the
server.
-module(ex14).
-export([start/0,server/0,client/0]).
server( ) ->
receive
Msg ->
io:format('received ~p~n', [Msg]),
ex14:server( )
end.
This example requires that you edit and recompile ex14 while ex14 is running. The recompilation also
loads the new code. Once the new module code has been loaded, the next fully qualified call
ex14:server() switches to the new version of server().
mymachine> erl
Erlang (JAM) emulator version 4.7.3.3
10 z 11 16.06.2009 22:30
Erlang whitepaper http://www.erlang.org/white_paper.html
Applications can also load new code without using the Erlang shell.
Further reading
More links for getting started.
11 z 11 16.06.2009 22:30