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:
- Your logic and functions require a state to be stored and maintained.
- You need to perform a piece of logic asynchronously on the same machine or on a different one.
- 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
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
: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
:put messages are evaluated
execute function is called again with the newly generated state (or in the case of
:get the same state). Since
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
Storage a test run:
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
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.
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 🤖