Elixir Tricks – Adding Context to with Clauses
September 2, 2017
elixir
Problem: It’s hard to tell errors apart in the else clause of with
I love the with statement in Elixir as it allows me to flatten what would otherwise be a long dive through case statements.
with \
{:ok, result} <- Foo.do_thing(input),
{:ok, more_result} <- Bar.do_another_thing(result),
{:ok, final_result} <- Baz.do_one_more_thing(more_result)
do
{:ok, final_result}
else
{:error, error} ->
IO.puts "failed: #{inspect error}"
{:error, error}
end
The problem is that when we get to the else, sometimes it’s important to know from which clause in the with the error came from. We can’t go back and modify the functions to return a different format for their error case and we really shouldn’t. This is a problem that’s local to this usage.
Solution: Add context to your with clauses
What we can do is surround a with clause in a tuple to add context to the result. For example, let’s pretend that it’s really important that we capture in a special way the error from Bar.do_another_thing(). We can surround it in a tuple and capture that in the else clause.
with \
{:ok, result} <- Foo.do_thing(input),
- {:ok, more_result} <- Bar.do_another_thing(result),
+ {{:ok, more_result}, _} <- {Bar.do_another_thing(result), :bar},
{:ok, final_result} <- Baz.do_one_more_thing(more_result)
do
{:ok, final_result}
else
+ {{:error, error}, :bar} ->
+ IO.puts "this is the error from Bar.do_another_thing(): #{inspect error}"
+ {:error, error}
{:error, error} ->
IO.puts "failed: #{inspect error}"
{:error, error}
end