Another alternative is to have a function that forwards everything that is
not :ok, and does something with {:ok, value} responses:
defp if_ok(input, fun) when is_function(fun) do
input
|> case do
{:ok, content} ->
case fun.(content) do
# if function does not return a tuple, but a map, make it {:ok,
res}
res = %{} -> {:ok, res}
other -> other
end
other -> other
end
end
defp if_ok(input, value) do
if_ok input, fn (_) -> value end
end
Then you could chain in the following way
do_something
|> if_ok(&Repo.delete/1)
|> if_ok(&AlgoliaService.delete_content_item/1)
|> if_ok(&broadcast_content_item_removed/1)
|> if_ok(:ok)
Can read a bit nicer than with, and easier to implement in existing
pipeline. Still quite clean imo.
Cheers,
Jaap
On Monday, August 15, 2016 at 7:29:59 PM UTC+8, alco wrote:
>
> The Elixir way is to use the pipe and `with` where each is more suitable
> for the task. So if you need to write a sequence of function calls where
> some or all are expected to return an error, `with` would be the most
> appropriate tool to use.
>
> One key difference between a pipeline and `with` is that the latter stops
> evaluation as soon as it encounters the first failed match. Whereas with a
> pipeline you'd have to wrap all constituent functions to pass the error
> from a previous step down to the next one. I see it as a downside to trying
> to use the pipe in places where it is more important to explicitly handle
> errors.
>
> On Mon, Aug 15, 2016 at 2:12 PM, Alvise Susmel <[email protected]
> <javascript:>> wrote:
>
>> Hi,
>>
>> I've started thinking about this, as maybe many of you, when I needed to
>> stop the pipe when an error is returned. I've seen that there are different
>> conversation about this, but I haven't clear what is the *elixir way* to
>> handle errors using pipes.
>>
>> I see two ways for achieving the same result.* The first* is to use the
>> *pipe* *|>* and for each function catch the error and propagate it down.
>> The error has to be always returned with the same pattern, like {:error,
>> reason}
>>
>> connect
>> |> receive_image
>> |> resize
>> |> rotate
>> |> save
>>
>>
>> If *receive_image* returns an error, *resize* needs to handle it
>>
>> def resize({:error, _}=error), do:error
>>
>> and to propagate it down through the pipe. So *rotate* needs to do the
>> same, etc..
>>
>> *The* *second* way I see is using the *with*, which to me seems to be
>> more generic (and less clean)
>>
>> with {:ok, pid} <- connect,
>> {:ok, image} <- receive_image(pid),
>> {:ok, resized_image} <- resize(image),
>> {:ok, rotated_image} <- rotate(resized_image),
>> :ok <- save(rotated_image)
>> do: IO.puts "saved"
>>
>>
>> Now, the cool thing about *with* is that when one of the assignments
>> fails, it exits returning the value that failed the pattern match.
>>
>> So, to you, what is the elixir way on handling the return errors on pipes
>> ? Could *"with" *be seen as a new (and more generic) way of piping with
>> error handling ?
>>
>> Thanks
>>
>> Alvise
>>
>> --
>> You received this message because you are subscribed to the Google Groups
>> "elixir-lang-core" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to [email protected] <javascript:>.
>> To view this discussion on the web visit
>> https://groups.google.com/d/msgid/elixir-lang-core/1dc86df2-eca0-4fa5-b883-7b9c2df36444%40googlegroups.com
>>
>> <https://groups.google.com/d/msgid/elixir-lang-core/1dc86df2-eca0-4fa5-b883-7b9c2df36444%40googlegroups.com?utm_medium=email&utm_source=footer>
>> .
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
--
You received this message because you are subscribed to the Google Groups
"elixir-lang-core" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/elixir-lang-core/4f0835a1-790a-4b1e-9dad-ed593292bc37%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.