The following code could be written much better by using the cond operator. with {:is_email, true} {:error, :bad_request} {:is_available, false} -> {:error, :conflict} end cond do !email_address?(email) -> {:error, :bad_request} !EmailAddresses.available?(email) -> {:error, :conflict} true -> {:ok, email} end This gets rid of unnecessary duplication, and I think is easier to understand.
Keathley did a good job discussing this in https://keathley.io/blog/good-and-bad-elixir.html#:~:text=Av... . The preferred style is to specify the errors in separate functions e.g. def main do with {:ok, response} where call_service, decode and store_in_db return the specific errors like {:error, :bad_request}, {:error, :conflict}.
data
|> call_service
|> an_ok_unwrapper(decode)
|> an_ok_unwrapper(store_in_db)
Or is `with` the way of doing this for ok/error results?