Skip to content
Erlang

Stateful Server via Tail Recursion

Hold mutable state in a process by threading it through recursive calls.

By EZ4Code Team
statetail-recursionserver

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