When to use processes in Elixir - Part 1: Storing state

I have had a question roaming through my head about what are the reasons to wrap your logic in processes when programming in Elixir. After a couple of weeks of research and asking around I think can sum up the major reasons that should push you toward accessing your logic from a process instead of directly.

The three major reasons are:

  1. Your logic and functions require a state to be stored and maintained.
  2. You need to perform a piece of logic asynchronously on the same machine or on a different one.
  3. You want to control or limit the access to a particular component in your application.

I will try to cover each one of the three reasons in its own blog post. In this article, I will cover the first reason: Your logic and functions require a state to be stored and maintained. This topic is already aptly covered in the Elixir documentation but for completion sake, and since the series of article is aimed at listing all the different situation that are good candidates for using a process, I will also write about the topic.

Processes and State

Elixir programs are structured in modules and functions that are immutable and stateless in nature. To maintain state we have to use processes, message sending and receiving.

Let’s take a look on how to accomplish that.

This is how this works, start function creates a new process and registers is under the name Storage. We will use this name to send messages to the process. When the newly created process is started, it will call execute with the initial value that was passed to start.

execute function is how the process keeps its state. In this function, we call receive which stops and wait until the process receives a message (using send). When a message is received by this process, it pattern matched to a :get or a :put. The :get message contains the pid of the sender process. This is used to send the state back to the sender. The :put contains the value that we want to append to the process state.

The trick to maintaining the state in execute is by recursing whenever a message is received and processed; After :get and :put messages are evaluated execute function is called again with the newly generated state (or in the case of :get the same state). Since execute contains receive call inside of it. It will stop and wait until a new message is received.

This recursion of calling execute, waiting for a message and recursively calling execute is how the process manages to retain its state in a pure way.

The remaining 2 functions are just wrapper function; put sends a message to Storage with the value to append. And get, while still being a wrapper, does a little bit more. It first sends a :get message to Storage with the address of the current process and then calls receive to wait for a reply from Storage. Remember that in execute function when we receive the :get message we send the Storage state back to the sender. That message is what we will wait for in the get function receive.

Let’s give Storage a test run:

Elixir Agents

Although using processes to store the state is the only way to store state in Elixir you don’t have to rollout your own solution as above. Elixir provides the Agent module which helps you easily store state.

Let’s have a very quick look on how to create, update and retrieve state using Agent:

Here is how this works:
- Agent.start_link creates a new agent — which is really a process — and gives it Storage name. It also takes a block that defines the initial state of the process.
- Agent.update takes the process name and a block. It executes the block in the context of the process. The value returned from the block is used as the new process state.
- Agent.get as with update, takes the process name and a block. The value returned from the block, instead of updating the state, it is returned to the caller to be used.

Agent is implemented as a GenServer. It’s implementation is simple. Please do check how get and update are implemented.


Recap

This concludes our first instalment in the greater topic on when to use processes in Elixir. I explained here how processes are required and used to store state.


If you liked it, don’t forget to recommend. Following me on twitter @ifnottrue would also mean a lot to me 🤖