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
comments powered by Disqus