It’s the kind of thing that, if you are used to other languages, would make you very suspicious. Library code starting its own processes — you can almost feel the mess and sense the bugs just musing over it. Yet in Elixir (and, of course, Erlang too), this is a totally normal thing to do.
The Elixir approach to shared mutable state is wrapping it in a process. In this case, I needed a counter and the easiest way to implement it is to use an Agent
which is a kind of process designed to handle simple state. In this case, the get_and_update
function allows me to return the counter and increment it as an atomic operation.
To start an Agent you use the Agent.start_link
function. But where to call it? Do I have to add some equivalent initializer function to my library? While not exactly onerous it felt awkward somehow like my implementation was leaking into the caller. Then again did I have to stop the agent process somewhere? Where would I do that?
Now I figured out how to manage the life-cycle of the agent process myself within the library. But it turns out to be unnecessary. All I had to was make one change to my mix.exs
file and one addition to a module in my library.
def application do
[
extra_applications: [:logger]
]
end
becomes:
def application do
[
extra_applications: [:logger],
mod: {Ergo, []}
]
end
along with changing the referenced library module, Ergo
to look like:
defmodule Ergo
use Application
def start(_type, _args) do
Supervisor.start_link([Ergo.AgentModule], strategy: :one_for_one)
end
…
end
This is enough that any application using my library (called Ergo, btw) knows will automatically start the Agent and manage its life-cycle. Without me, or the calling application, needing to know anything about it at all.
This is a pretty neat trick.