yourpalal

yourpalal

With/1 design patterns

When using with/1 to handle a bunch of matches and things that return {:ok, _} or :error or {:error, _} I often need to differentiate between errors, and I’ll wrap the individual matches in tuples, like this:

with(  
  {{:ok, shoes}, _} <- {get_shoes(outfit), :shoes},
  {{:ok, shirt}, _} <- {get_shirt(outfit), :shirt},
  {true, _} <- {shoes_match_shirt?(shoes, shirt), :matching}
) do
  {:ok, "service allowed"}
else
  {error, :shoes} ->
    {:error, "you forgot your shoes!"}
  _ ->
    :error
end

Mainly, I’m just curious how other people are handling things like this! What are you doing when you need to handle multiple error cases from with/1 ? One obvious solution is to give up on with/1 and use if or regular case statements or something like that.

Second, I’m open to any suggestions/criticism of the above code style.

Most Liked

hlx

hlx

I think you’re better of returning a more meaningful error from get_shoes/1

Example

{:error, {ShoesNotFound, "you forgot your shoes!"}}

# or

{:error, %ShoesNotFound{message: "you forgot your shoes!"}}
peerreynders

peerreynders

instead of Map.fetch(params, :shoes)

design patterns

Following Scott Wlaschin’s guidance - use functions!

def fetch_item(map, key) do
  case Map.fetch(map, key) do
    :error ->
      {:error, key}
    result ->
      result
  end
end

  ...

  with(  
    {:ok, shoes} <- fetch_item(outfit, :shoes),
    {:ok, shirt} <- fetch_item(outfit, :shirt),
    {true, _} <- {shoes_match_shirt?(shoes, shirt), :matching}
  ) do
    {:ok, "service allowed"}
  else
    {:error, :shoes} ->
      {:error, "you forgot your shoes!"}
    _ ->
      :error
  end

  ...

or

def fetch_item(map, key, msg) when is_binary(msg) do
  case Map.fetch(map, key) do
    :error ->
      {:error, msg}
    result ->
      result
  end
end

  ...

  with(  
    {:ok, shoes} <- fetch_item(outfit, :shoes, "you forgot your shoes!"),
    {:ok, shirt} <- fetch_item(outfit, :shirt, "you forgot your shirt!"),
    {true, _} <- {shoes_match_shirt?(shoes, shirt), :matching}
  ) do
    {:ok, "service allowed"}
  else
    {:error, msg} = msg_error when is_binary(msg) ->
      msg_error
    _ ->
      :error
  end

  ...
peerreynders

peerreynders

The idiom is {:ok, value}, {:error, reason}

So the pattern match should focus on the contents of reason.

For example:

Process.monitor/1 will result in a general message of the format:

{:DOWN, ref, :process, object, reason}

where reason can take on values like :normal, :noproc or :noconnection, i.e. values that are highly distinct and imply their context. So it’s a good idea to follow the same practice with {:error, reason} tuples.

Where Next?

Popular in Questions Top

chokchit
** (DBConnection.ConnectionError) connection not available and request was dropped from queue after 2733ms. You can configure how long re...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
Kurisu
For example for a current url like http://localhost:4000/cosmetic/products?_utf8=✓&amp;query=perfume&amp;page=2, I would like to get: ...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New
chrisalley
ExUnit now has describe blocks which is a welcome addition coming from RSpec. In the docs, it states that nested hierarchies of describe ...
New
gshaw
What is the idiomatic way of matching for not nil in Elixir? E.g., First way: defp halt_if_not_signed_in(conn, signed_in_account) when...
New
JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
JulienCorb
I am trying to implement my new.html.eex file to create new posts on my website. new.html.eex: &lt;h1&gt;Create Post&lt;/h1&gt; &lt;%= ...
New
RisingFromAshes
I’ve read in another post that it may be possible with a router helper - but I couldn’t find an appropriate one, and tbh, I’m still just ...
New
bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New

Other popular topics Top

JeremM34
Hello, how can I check the Phoenix version ? Thanks !
New
jononomo
I am trying to figure out how Mix knows whether the environment is test, dev, or prod – where is this set? Thanks.
New
stefanchrobot
What’s the safe way to decode a JSON string into a struct? I want to avoid calling String.to_atom. Jason.decode can give me a map with st...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
boundedvariable
I am going through the kafka architecture. All the features what the kafka is providing are already in Erlang. I would like hear your opi...
New
romenigld
I am trying to run a deploy with docker and I successfully runned with this command: docker build -t romenigld/blog-prod . but when I t...
New
shijith.k
I am trying to start a new phoenix project with elixir 1.9, but mix phx.new does not work. It says that ** (Mix) The task "phx.new" could...
New
svb
Hi! Currently I want to submit a form by pressing the Enter key. However, since my input field is of type “textarea” this is just adds a...
New
sergio
Kind of like when jquery came out, it was super necessary. Existing drag and drop libraries have a bunch of baggage to support old browse...
New
lanycrost
Hi everyone! I need implement if…else if…else condition from my elixir code, and anymore of this control flow structures not work proper...
New

Latest on Elixir Forum

We're in Beta

About us Mission Statement