sequence1.erl (raw implementation) -module(sequence1). -export([make_sequence/0,

get_next/1, reset/1]).

 

Create a new shared counter.
make_sequence() ->
spawn(fun() ->
sequence_loop(0)
end).

 

sequence_loop(N) -> receive

{From, get_next} ->
From {self(), N},
sequence_loop(N + 1);
reset ->
sequence_loop(0)
end.

Retrieve and increment. get_next(Sequence) ->

Sequence! {self(),g et_next}, receive

{Sequence, N} -> N end.

 

Re-initialize counter to zero. reset(Sequence) ->

Sequence reset.

Erlang value, and it is sent atomically and immutably. The message is sent to the receiving process’s mailbox, and the sender continues to execute—it does not wait for the receiving process to retrieve the message.

A process uses the receive expression to extract messages from its mailbox. It specifies a set of patterns and associated handler code and scans the mailbox looking for the first message that matches any of the patterns, blocking if no such message is found. This is the only blocking primitive in Erlang. Like the patterns in function clauses, the patterns in receive options match structures and bind new variables. If a pattern uses a variable that has already been bound to a value, then matching the pattern requires a match with that value, as in the value for Sequence in the receive expression in get_next/1.

The code here implements a simple client-server protocol. In a call, the client process sends a request message to the server process and blocks wait-

figure 2: server.erl.

ing for a response message. Here, the get_next/1 call request message is a two-element tuple: the client’s own pid followed by the atom get_next. The client sends its own pid to let the server know where to send the response, and the get_next atom will let us differentiate this protocol operation from others. The server responds with its own two-element tuple: the server pid followed by the retrieved counter value. Including the server pid lets the client distinguish this response from other messages that might be sitting it its mailbox.

A cast is a request to a server that needs no response, so the protocol is just a request message. The reset/1 cast has a request message of just a bare atom.

 

abstracting Protocols Brief as it is, the Erlang implementation of sequences is much longer and less clear than the original Java version. Much of the code is not particular to sequences, however, so it should

 

The spawn/1 primitive creates a new process, returning its process identifier (pid) to the caller. An Erlang process, like a thread, is an independently scheduled sequential activity with its own call stack, but like an operating-system process, it shares no data with other processes—processes interact only by sending messages to each other. The self/0 primitive returns the pid of the caller. A pid is used to address messages to a process. Here the pid is also the data abstraction—a sequence is just the pid of a server process that understands our sequence-specific messaging protocol.

The new process starts executing the function specified in spawn/1 and will terminate when that function returns. Long-lived processes therefore avoid premature returns, often by executing a loop function. Tail-call optimization ensures that the stack does not grow in functions such as sequence_loop/1. The state of the sequence process is carried in the argument to this eternal call.

Messages are sent with the syntax pid message. A message can be any

-module(server).

-export([start/1, loop/2, call/2, cast/2]). % Client-server messaging framework. % % The callback module implements the following callbacks: % init() -> InitialState % handle_call(Params, State) -> {Reply, NewState} % handle_cast(Params, State) -> NewState

Return the pid of a new server with the given callback module. start(Module) ->

spawn(fun() -> loop(Module, Module:init()) end).

loop(Module, State) -> receive

{call, {Client, Id}, Params} ->

{Reply, NewState} = Module:handle_call(Params, State), Client {Id, Reply}, loop(Module, NewState);

{cast, Params} ->

NewState = Module:handle_cast(Params, State), loop(Module, NewState)

end.

Client-side function to call the server and return its reply. call(Server, Params) ->

Id = make_ref(),

Server {call, {self(), Id}, Params}, receive

{Id, Reply} -> Reply end.

Like call, but no reply is returned. cast(Server, Params) ->

Server {cast, Params}.

References:

Archives