Stateful Server via Tail Recursion
Hold mutable state in a process by threading it through recursive calls.
Code
-module(kvstore).
-export([start/0, put/2, get/1, dump/0]).
-export([loop/1]).
%% API: clients send tagged requests with a reference for replies.
start() -> spawn(fun() -> loop(#{}) end).
put(K, V) -> cast({put, K, V}).
get(K) -> call({get, K}).
dump() -> call(dump).
cast(Msg) -> ?MODULE ! Msg, ok. % Note: ?MODULE used as a registered name
% in this demo — register it in real code.
call(Msg) ->
Ref = make_ref(),
?MODULE ! {call, Ref, self(), Msg},
receive {Ref, Reply} -> Reply end.
%% Server loop: state is the function argument, threaded through tail calls.
loop(State) ->
receive
{put, K, V} ->
loop(maps:put(K, V, State));
{call, Ref, From, {get, K}} ->
From ! {Ref, maps:get(K, State, undefined)},
loop(State);
{call, Ref, From, dump} ->
From ! {Ref, State},
loop(State)
end.Explanation
BEAM processes have no mutable state — instead, state is the argument to a tail-recursive loop function. Each message is handled by computing the next state and calling loop(NewState) as the last expression, which (being a tail call) doesn't grow the stack. This is exactly what gen_server formalises; writing it once by hand shows why the pattern works.
More Erlang Snippets
Ping-Pong Processes
Two processes pass messages back and forth using spawn and receive.
gen_server Counter
Build a stateful server with the gen_server behaviour.
Supervisor Tree
Define a supervisor that starts workers and restarts them on crash.
Selective Receive with References
Match a specific reply out of many messages using a unique reference.
Links, Exit Trapping & 'Let It Crash'
Use link and trap_exit to detect process death and recover.
Parallel Map with rpc:pmap
Apply a function to each list element in parallel across processes.