Current section

Files

Jump to
exceptional lib exceptional value.ex
Raw

lib/exceptional/value.ex

defmodule Exceptional.Value do
@moduledoc ~S"""
Provide an escape hatch for propagating unraised exceptions
## Convenience `use`s
Everything:
use Exceptional.Value
Only named functions (`exception_or_continue`):
use Exceptional.Value, only: :named_functions
Only operators (`~>`):
use Exceptional.Value, only: :operators
"""
defmacro __using__(only: :named_functions) do
quote do
import unquote(__MODULE__), except: [~>: 2]
end
end
defmacro __using__(only: :operators) do
quote do
import unquote(__MODULE__), only: [~>: 2]
end
end
defmacro __using__(_) do
quote do
import unquote(__MODULE__)
end
end
@doc ~S"""
If an exception, return the exception, otherwise continue computation.
Essentially an `Either` construct for `Exception`s.
Note that this does not rescue `raise`s.
If you want that behaviour, please see `Exceptional.Safe.safe/1`
## Examples
iex> 1 |> exception_or_continue(fn value -> value * 100 end.())
100
iex> %ArgumentError{message: "exception handled"}
...> |> exception_or_continue(fn value -> value * 100 end.())
%ArgumentError{message: "exception handled"}
iex> %ArgumentError{message: "exception handled"}
...> |> exception_or_continue(fn x -> x + 1 end.())
...> |> exception_or_continue(fn y -> y - 10 end.())
%ArgumentError{message: "exception handled"}
iex> %ArgumentError{message: "exception not caught"}
...> |> raise
...> |> exception_or_continue(fn value -> value * 100 end.())
** (ArgumentError) exception not caught
iex> Enum.fetch!([], 9) |> exception_or_continue(fn v -> v * 10 end.())
** (Enum.OutOfBoundsError) out of bounds error
"""
# @lint {Credo.Check.Design.AliasUsage, false}
@spec exception_or_continue(Exception.t() | any, fun) :: Exception.t() | any
defmacro exception_or_continue(maybe_exception, continue) do
quote do
require Exceptional.Control
Exceptional.Control.branch unquote(maybe_exception),
value_do: unquote(continue),
exception_do: fn exception -> exception end.()
end
end
@doc ~S"""
Operator alias for `exception_or_continue`
## Examples
iex> 1 ~> fn value -> value * 100 end.()
100
iex> exception = %Enum.OutOfBoundsError{message: "exception"}
...> exception ~> fn x -> x + 1 end.()
%Enum.OutOfBoundsError{message: "exception"}
...> exception
...> ~> fn x -> x + 1 end.()
...> ~> fn y -> y - 10 end.()
%Enum.OutOfBoundsError{message: "exception"}
...> raise(exception) ~> fn x -> x + 1 end.()
** (Enum.OutOfBoundsError) out of bounds error
iex> Enum.fetch!([], 9) ~> fn x -> x + 1 end.()
** (Enum.OutOfBoundsError) out of bounds error
"""
defmacro maybe_exception ~> continue do
quote do
require Exceptional.Control
Exceptional.Control.branch unquote(maybe_exception),
value_do: unquote(continue),
exception_do: fn exception -> exception end.()
end
end
end