Recently I tackled a problem which involved updating a large number of key values.
Naturally, I considered using a Map
, with operations like Map.put/3
.
However this seemed insufficient, given the immutable nature of data structures in Elixir:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1}
I then solved the problem by holding the state of the Map
in a GenServer
, and updating it using handle_cast/3
calls.
Generally, is this the right approach, or was this too much here?
I then solved the problem by holding the state of the
Map
in aGenServer
[...] Generally, is this the right approach, or was this too much here?
It heavily depends on your goal. There are many different ways to store the state. Rebinding variables like:
m = Map.put(%{}, :a, 1)
#⇒ %{a: 1}
m = Map.put(m, :b, 2)
#⇒ %{a: 1, b: 2}
Does not store anything. It binds the local variable m
to RHO and as soon as the control flow leaves the scope, this variable becomes garbage collected. Whether you need the aforementioned map within a single scope, GenServer
(and other state holders) is an overkill.
OTOH, if you need to store the state for a long time and share it between different scopes (e. g. between different processes,) GenServer
is the simplest way to accomplish that. In Elixir we have Agent
module to decrease the boilerplate for GenServer
that is used as a simple in-memory storage, but my advice would be to always use GenServer
: sooner or later Agent
will become too tight for your purposes.
Also, one might use ets
module to keep in-memory key-value storage, shared between processes.
dets
is a way to store the state between process restarts.
And, finally, mnesia
is an OTP native approach to share the state between both restarts and different nodes (in distributed environment.)
Your first approach was right, you just got one thing wrong.
You should rebind the variable when you update the map, like here:
iex> m = Map.put(%{}, :a, 1)
%{a: 1}
iex> m = Map.put(m, :b, 2)
%{a: 1, b: 2}
iex> m
%{a: 1, b: 2}
But you gotta undestand here that it doesn't mutate the variable, it creates a new map and rebinds it to the same variable.
Now, this approach is the most simple one and you'd have to pass this map to every function that uses it. As an alternative, you may consider using the Agent
module. All the info what it is and what it is used for can be found in its docs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With