In the previous lesson, you sent and received messages between processes. As a brief reminder, a process can use receive to block and wait for messages, and it often calls itself recursively to stay alive. Today, we will build on that by having a process remember data across messages. This is called process state management, and it is a core part of how Elixir programs model state without mutable variables.
You will see how a process:
- Holds state in a recursive loop
- Updates that state when it receives messages
- Replies to the caller with results
- Shuts down cleanly
Note: In this lesson, we use raw spawn and receive to help you understand the fundamentals of process state and messaging. In real-world Elixir applications, you will typically use OTP abstractions like GenServer, Task, and Supervisor for process management and reliability. We will explore these OTP tools later in this course path.
The following functions are part of a module we will call Counter. First, we define the core loop:
Explanation:
- The current state is the
countargument. It lives on the stack of this process. {:increment, caller}updates the state, replies to the caller with the new value, then recurses withloop(new_count).{:get, caller}does not change the state; it replies with the current count and recurses withloop(count).:stopprints a message and returns, ending the process.- This is the same receive-and-recursion pattern you used before, now carrying state forward.
Next, we add a public start/1 function to the Counter module to initialize the state:
Explanation:
start/1creates a new process that begins its loop withinitial_valueas state.spawnreturns thepidof the new process so you can send it messages.- The
loopfunction is private to keep control inside the module.
Explanation:
Counter.start(0)launches the stateful counter with an initial count of0.- You send
{:increment, self()}to request an update;self()is yourpidso the counter knows where to reply. receivewaits for{:count, value}and prints it.{:get, self()}asks for the current count without changing it.:stoptells the counter to print its final message and exit.
You now know how to keep state inside a process:
- Store state as a function argument in a recursive
receiveloop. - Update state by creating a new value and tail-recursing with it.
- Reply to callers so they can observe the state safely.
- Stop cleanly on command.
This pattern is the foundation for building servers and agents in Elixir. Ready to make it second nature? Head to the practice section and bring this counter to life.
