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 waiting 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
cesses 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-spe-cific 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 Erlang value, and it is sent atomically and immutably. The message is placed in 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.
-module(server).
-export([start/1, loop/2, call/2, cast/2]).
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) ->
References:
Archives