tomekowal

tomekowal

Easiest way to find the function that didn't match in the `with` statement

I have code like this (but bigger)

defmodule WithStacktrace do
  def hello do
    with {:ok, _value} <- {:ok, 1},
      {:ok, value} <- :wrong_shape do
      {:ok, value}
    else
      {:error, e} -> {:error, e}
    end
  end
end

This will raise:

** (WithClauseError) no with clause matching: :wrong_shape
    (with_stacktrace 0.1.0) lib/with_stacktrace.ex:3: WithStacktrace.hello/0

So, the stack trace points to the beginning of the with statement (line 3).
But the expression that matches neither its success case nor else block is at line 4 {:ok, value} <- :wrong_shape.

Let’s say my with expression consists of 10 such calls. How can I easiest check which one returns the :wrong_shape?

I am currently adding IO.inspects after each of those statements :smiley:

Marked As Solved

kartheek

kartheek

You can run debug session - if you can replicate the error.

Also if your code is not confirming to type spec - won’t the code analysis tools catch this ?

Also Liked

Nicd

Nicd

A couple of ideas.

Sometimes I’ve used tagged tuples. Something like:

with {:part1, {:ok, data}} <- {:part1, get_data()},
  {:part2, :ok} <- {:part2, process_data()} do

This allows matching the failed value in else and would show up in the WithClauseError too. But this can get very noisy very quickly.

Another option is to use a helper module like this (from ihumanable/icecreamcohen on Discord):

defmodule WithHelper do
  @spec op(atom(), any(), :strict | :permissive) :: any()
  def op(label, thing, mode \\ :strict) do
    if mode == :permissive do
      opt_permissive(label, thing)
    else
      op_strict(label, thing)
    end
  end

  defp opt_permissive(label, err) when err in [:error, false, nil], do: {label, err}
  defp opt_permissive(label, {:error, _} = err), do: {label, err}
  defp opt_permissive(_label, val), do: val

  defp op_strict(_label, val) when val in [:ok, true], do: val
  defp op_strict(_label, {:ok, _} = success), do: success
  defp op_strict(label, other), do: {label, other}
end

And now we can do it like this:

with {:ok, data} <- op(:part1, get_data()),
  :ok <- op(:part2, process_data()) do
    ...
else
  {:part1, error} -> do_something(error)
  {:part2, _error} -> halt()
end

This lessens the noise somewhat, assuming your functions stick to the ok-tuple standard.

tomekowal

tomekowal

I agree with Crhis but I think I wasn’t clear with my question.

I don’t want to identify failing cases because I want to handle them differently.

In my scenario all functions should return {:ok, value} or {:error, reason} and that else clause is irrelevant.

But one of the functions does not conform to specification and returns something different which I’d like to fix. I encounter that scenario fairly often and each time it is a pain :stuck_out_tongue:

kartheek

kartheek

Relevant thread about with statements.

https://medium.com/very-big-things/towards-maintainable-elixir-the-anatomy-of-a-core-module-b7372009ca6d

Look at With chaining section of Sasa Juric article (I am unable to provide link to section as it has no hyperlink)

Kernel.SpecialForms — Elixir v1.20.2 - this is from elixir docs explain about else in with .

I am convinced with statements don’t need else most of the time. Are you trying to do some error recovery from else ?

if you have only one clause in else pattern matching error with {:error, e} and returning same, then its not needed. with will return error directly without need of else. I realised it very recently.

Where Next?

Popular in Questions Top

sergio
In Ruby, I can go: User.find_by(email: "foobar@email.com").update(email: "hello@email.com") How can I do something similar in Elixir? ...
New
senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
Darmani72
If I have a post route which an argument: post /my_post_route/:my_param1, MyController.my_post_handler How would get the post params ...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
johnnyicon
Hi all, I’ve just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I’m trying to use Postgres...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? Ecto.Repo — Ecto v3.14.0 has exampl...
New
itssasanka
Hi all, Trying to get some more clarity over utc_datetime and naive_datetime for Ecto: The documentation above suggests that while ...
New
jason.o
In the code below, if the create action is not set to accept “extra_key” as an input, it errors out with a message shown above. Is there ...
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
hariharasudhan94
Lets say I have map like this fetching from my database %{"_id" =&gt; #BSON.ObjectId&lt;58eb1a7a9ad169198c3dXXXX&gt;, "email" =&gt; ...
New

Other popular topics Top

senggen
Erlang/OTP 25 [erts-13.2.2] [source] [64-bit] [smp:8:8] [ds:8:8:10] [async-threads:1] 15:22:35.803 [error] gen_event {lager_file_backend...
New
mcarvalho
What is the difference between System.get_env and Application.get_env? For example, what are best practices to use one versus another.
New
chrismccord
As promised, the first release candidate of Phoenix 1.3.0 is out! This release focuses on code generators with improved project structure...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
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
grych
Hi folks, Few months ago I have announced the proof-of-concept of the library to manipulate the browsers DOM objects directly from Elixi...
639 52673 488
New
rms.mrcs
Hi, I need to transform a list of numbers into a map where the keys are the indexes and the values are the original values of the list. ...
New
Brian
What is the proper way to load a module from a file in to IEX? In the python world, doing something like this pretty standard: from ....
New
AstonJ
We’ve put together this wiki for Phoenix LiveView - please feel free to add any info you feel is worth including. What is Phoenix LiveV...
New
Qqwy
Update: How to use the Blogs &amp; Podcasts section You can post links to your blog posts or podcasts either in one of the Official Blog...
3271 127089 1222
New

Latest on Elixir Forum

Elixir Forum

We're in Beta

About us Mission Statement