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