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 ![]()
Marked As Solved
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
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
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 ![]()
kartheek
Relevant thread about with statements.
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.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance









