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.

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.

2 Likes

Consider two message shapes:

  1. Nested: {:processed, {:ok, item}} and {:processed, {:error, item}}
  2. 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.

2 Likes

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.

3 Likes

Yes, remember, you are communicating mainly to other developers (including yourself many months later). If we were communicating mainly to the machine, we would (still) be writing in binary.

1 Like