josefrichter
Nested response tuples - best practices?
Hi, in some situations I need to broadcast a message from genserver, need to say it’s error, what kind of error, and also return the element that caused the error. So I end up with something like this:
{:enrollment_processed, {:error, {:already_enrolled, enrollment}}}
I guess it’s kinda ugly and error prone. What’s the best practice to deal with this?
a) tuple with more than 2 elements
{:enrollment_processed, {:error, :already_enrolled, enrollment}}
b) merge error + type of error
{:enrollment_processed, {:already_enrolled, enrollment}}
c) other solutions?
Note I need to have enrollment in this message, because based on that I can broadcast it to the right channel and the liveview can report which enrollment wasn’t saved.
Most Liked
josefrichter
Thank you both. I went for flat tuples and more verbose code. I guess it’s better to have multiple smaller and simpler handler functions that are clearer and easier to reason about, especially if you look at them a few years later, or come into the project from the outside.
dimitarvp
To be fair I wouldn’t even use :enrollment_processed because that implies that it finished successfully. Whatever you opt for IMO the first element of the tuple should be something along the lines of :enrollment_failed and then 2nd element and further should elaborate on why did it fail.
dom
Consider two message shapes:
- Nested:
{:processed, {:ok, item}}and{:processed, {:error, item}} - Flat:
{:succeeded, item}and{:failed, item}
The “nested” option makes it possible to write a generic handler for “processed”:
def handle_info({:processed, status}, socket) do
update_status(status)
end
…but leads to extra destructuring if you have two separate handlers:
def handle_info({:processed, {:ok, item}}, socket) do
set_succeeded(item)
end
def handle_info({:processed, {:error, item}}, socket) do
set_failed(item)
end
The “flat” option forces you to use two handlers, but they’re more readable and efficient than the two handlers of the “nested” option:
def handle_info({:succeeded, item}, socket) do
set_succeeded(item)
end
def handle_info({:failed, item}, socket) do
set_failed(item)
end
So I’d recommend nesting as much as you need for pattern matching, and not more.








