Congratulations! Those OTP tools in particular really are the heart of what makes Elixir/erlang truly special, and mastering them is probably the largest learning curve to climb, since there’s little else like it out there in other languages!
I would second the sentiment of learning how, but also when, to use ETS. Once you have a multi-process setup using these new tools, passing data between them can become a bottleneck, and ETS is designed to circumvent this for data access in parallel, if order of operations is not important; whereas GenServer serializes the operations you perform within it.
After learning about how GenServers can help you and when to use them, the next thing to learn is when not to use them!
There is a great concept called “functional core”. The idea is that you do the vast majority of your code in pure functional style. This is actually not too hard once you get used to the idea, and it makes testing SOOO much easier (no mocks or anything, just call functions and check the return values!).
Then at the boundary of your code, you use some GenServers to hold the top level state of your application and act as the boundary of the system. This then minimises the painful testing of stateful stuff.
I think I’m at a similar place as you (or a little behind). One book that I really enjoyed was Tony Hammond’s Exploring Graphs with Elixir. (If you liked :digraph there is many more graphs in there.) As a self-teaching alchemist, I felt I’ve learnt a great deal just from how he setup a project (before even getting to any graphs.)
Learnt about Collectable protocol from Elixir in Action!!
defmodule Q do
defstruct entries: {}
def new(), do: %Q{entries: :queue.new()}
def enqueue(q, entry), do: %Q{entries: :queue.in(entry, q.entries)}
end
defimpl Collectable, for: Q do
def into(original) do
{original, &into_callback/2}
end
defp into_callback(q, {:cont, entry}) do
Q.enqueue(q, entry)
end
defp into_callback(q, :done), do: q
defp into_callback(_q, :halt), do: :ok
end
for i <- 1..3, into: Q.new(), do: I
# %Q{entries: {[3, 2], [1]}}