Erlang is a concurrent programming language – this means that parallel activities (processes) can be programmed directly in Erlang and that the parallelism is provided by Erlang and not the host operating system.
In order to control a set of parallel activities Erlang has primitives for multi- processing: spawn
starts a parallel computation (called a process); send
sends a message to a process; and receive
receives a message from a process.
spawn/3
starts execution of a parallel process and returns an identifier which may be used to send messages to and receive messages from the process.
The syntax Pid ! Msg
is used to send a message. Pid
is an expression or constant which must evaluate to a process identity. Msg
is the message which is to be sent to Pid. For example:
Pid ! {a, 12}
means send the message {a, 12}
to the process with identifier Pid
(Pid is short for process identifier). All arguments are evaluated before sending the message, so:
foo(12) ! math3:area({square, 5})
means evaluate the function foo(12)
(this must yield a valid process identifier) and evaluate math3:area({square, 5})
then send the result (i.e. 25
) as a message to the process. The order of evaluation of the two sides of the send
primitive is undefined.
The primitive receive
is used to receive messages. receive
has the following syntax:
receive
Message1 ->
... ;
Message2 ->
... ;
...
end
This means try to receive a message which is described by one of the patterns Message1,Message2,...
The process which is evaluating this primitive is sus- pended until a message which matches one of the patterns Message1,Message2,...
is received. If a match occurs the code after the ->
is evaluated.
Any unbound variables occurring in the message reception patterns become bound if a message is received.
The return value of receive
is the value of the sequence which is evaluated as a result of a receive option being matched.
While we can think of send
as sending a message and receive
as receiving a message, a more accurate description would be to say that send sends a message to the mailbox of a process and that receive
tries to remove a message from the mailbox of the current process.
receive
is selective, that is to say, it takes the first message which matches one of the message patterns from a queue of messages waiting for the attention of the receiving process. If none of the receive patterns matches then the process is suspended until the next message is received – unmatched messages are saved for later processing.
1.5.1. An echo process
As a simple example of a concurrent process we will create an echo process which echoes any message sent to it. Let us suppose that process A
sends the message {A, Msg}
to the echo process, so that the echo process sends a new message containing Msg
back to process A
. This is illustrated in Figure 1.1.
// TODO: FIGURE !!!
In Program 1.5 echo:start()
creates a simple echo process which returns any
message sent to it.
-module(echo).
-export([start/0, loop/0]).
start() ->
spawn(echo, loop, []).
loop() ->
receive
{From, Message} ->
From ! Message,
loop()
end.
spawn(echo, loop, [])
causes the function represented by echo:loop()
to be
evaluated in parallel with the calling function. Thus evaluating:
... Id = echo:start(), Id ! {self(), hello} ...
causes a parallel process to be started and the message {self(), hello}
to be sent to the process – self()
is a BIF which returns the process identifier of the current process.