DEV Community

Ryan Will
Ryan Will

Posted on • Originally published at til.ryanwill.dev on

Pattern match in with clause 🤯

I had a problem where I needed to make sure a string was returned from a function that was part of a with clause. I thought, “You can’t pattern match here, right? That would just be too convenient.”

Turns out you totally can. Here’s an example from the docs.

users = %{"melany" => "guest", "bob" => :admin}

with {:ok, role} when not is_binary(role) <- Map.fetch(users, "bob") do
  {:ok, to_string(role)}
end

Top comments (1)

Collapse
 
edisonywh profile image
Edison Yap • Edited

This is why the with syntax is so awesome.

If your pattern match fails, it tries to pattern match in the else block too, which allows you to handle all of your errors below, and all of your happy paths above, so at a glance it's clear what your intent is.

This is also basis to another really interesting pattern that I've really come to like - tagged tuple.

Sometimes your pattern matching might return something pretty generic, so it's hard for you to handle the errors for them, for example:

data = "a"
with true <- is_binary(a),
         true <- a == "a" do
...
else
  false ->
     # here you handle the error, but you don't know which one of the function fails.
end

Tagged tuple changes this, by wrapping each execution in a tuple, like this:

data = "a"
with {:string, true} <- {:string, is_binary(a)},
         {:a, true} <- {:a, a == "a"} do
...
else
   # now you can handle specific errors
   {:string, false} ->
     ...
   {:a, false} ->
     ...

end

I have an example running in the wild here:

github.com/edisonywh/committee/blo...

You can see how elegant error handling gets!