Elixir State
2024-10-09
Elixir is blowing my mind :)
State: in an OOP language you'd manage state (let's assume a key-value store) probably via a class and use mutexes and locking for safe access across threads.
In Elixir it's idiomatic to simple use a module, spawn your 'state to be managed' as a process to be sent to and retrieved from.
- KV is our module
start_link/1
spawns a process- it starts the function
loop/1
- the
loop/1
function is private to the module and can't be used outside the module - as argument, it passes an empty map the first time round
%{}
- the
- the
loop/1
function itself- simply waits for receiving messages
- the messages are tuples, the first of which an
atom
specifying the operation - via pattern matching different actions are executed based on the received tuple
{:get ...
-> sends the retrieved value back to the parent process{:put ...
-> stores the key-value pair in the map
Task.start_link(fn -> loop(%) end)
end
receive do
->
send(caller, Map.get(map, key))
loop(map)
->
loop(Map.put(map, key, value))
end
end
end
= KV.start_link()
- KV is a process which now keeps state (a map)
- we can send to the KV process (via its PID)
- we use :put and :get atoms to indicate storage/retrieval
send(kvPID, )
send(kvPID, )
send(kvPID, )
# kvPID will send its response to the parent process
answer = receive do
val ->
nil ->
after
500 ->
end
IO.puts answer # value retrieved: 1
- to reiterate: KV is not a class, we haven't done anything w/ mutexes or locking
- the map we manage could now be accessed by TENS OF THOUSANDS of processes simultaneously without any race conditions or anything of the sort!
Processes can be spawned in vast numbers, be registered (given a name) for easier referencing, be managed by supervisors to handle faults and respawn, and much more... Highly resilient with minimal syntax and boilerplate! And Elixir supports this process based workflow through and through...
Small example:
# Agents are higher level abstractions around state
# the whole KV functionality can simply be written as
= Agent.start_link(fn -> % end)
Agent.update(pid, fn map -> Map.put(map, , 1) end)
Agent.update(pid, fn map -> Map.put(map, , 2) end)
val = Agent.get(pid, fn map -> Map.get(map, ) end, 500)
IO.puts # value retrieved: 1