F3.07 · part 3 of 3
Holding state in a loop
A process has no variables that outlive a single message. What it has is a recursive loop: after
handling a message, the process tail-calls itself with updated state. The state is the loop's argument, living
in a function that never returns. This recursive receive is exactly what
GenServer abstracts in F3.08.
The portal runs a tally process that counts events. It loops on a count, answers
:inc and {:get, from}, and carries the running total forward.
Step through three messages and watch the state move through the loop.
State through the loop
Each turn of the loop receives one message, computes the next state, and recurses with it. An
:inc raises the count; a {:get, from} replies and keeps the
count the same. Step through the sequence and read the state in, the clause taken, and the state passed on.
Message in the sequence · select one
Nothing mutates. Each message produces a fresh state value handed to the next call, and the process stays alive because the loop never returns — it only recurses.
The whole server
Start the loop with an initial state, then drive it with messages. The recursion is in tail position, so the
process holds steady memory no matter how many messages it handles. This is a hand-written
GenServer.
def loop(count) do receive do :inc -> loop(count + 1) {:get, from} -> send(from, count); loop(count) end end counter = spawn(fn -> loop(0) end) # starts holding 0 send(counter, :inc) send(counter, :inc) send(counter, {:get, self()}) receive do n -> n end # => 2
The idea
Remember across messages without mutable variables — carry the memory forward by hand.
In Elixir
State is the loop's argument; tail-recursion keeps it endless. GenServer hides the loop and the receive behind callbacks.
That completes processes: spawn, message passing, and a stateful loop — the actor model in
full. Next module: F3.08 — OTP: GenServer & supervisors, where this
loop becomes handle_call/3 and a supervisor restarts the process when it crashes.
References
Primary sources for this lesson, and where it connects in the course.
Sources
- Processes — Elixir documentation — spawn, send, receive.
- Actor model — Wikipedia — the concurrency model.
- Armstrong, J. (2003). Making reliable distributed systems in the presence of software errors. — isolation and message passing.