View Source Oxide.Result (oxide v0.6.0)

Helpers for working with result tuples, {:ok, value} and {:error, reason}.

Unless otherwise stated, functions raise FunctionClauseError when given an unexpected non-result. :ok, :error, {:ok, value1, value2}, etc. are not considered results.

Summary

Functions

Result pipe operator.

Return true if an enumerable of results are all ok.

Transform an unwrapped ok value with f, or return an error unchanged.

Return true if any of an enumerable of results is ok.

Assert a result.

Collects a list of results into a single result.

Convert a maybe-nil value to a result.

Wrap a value in an error result.

Return whether a result is an error.

Transform a result, mapping an ok value with f and leaving errors unchanged.

Return a result leaving ok values unchanged but transforming an error reason with f.

Return an unwrapped ok value transformed by f, or default if result is an error.

Wrap a value in an ok result.

Return whether a result is ok.

Return an ok result unchanged, or transform an unwrapped error reason with f.

Returns true if the given value is a result tuple.

Equivalent to Kernel.tap/2 for error results.

Equivalent to Kernel.tap/2 for ok results.

Unwrap an :ok result, and raise an :error reason.

Unwrap an :ok result, falling back to default for an :error result.

Unwrap an :ok result, or else act on the error in some way.

Types

@type t() :: {:ok, any()} | {:error, any()}
@type t(v) :: {:ok, v} | {:error, any()}
@type t(v, e) :: {:ok, v} | {:error, e}

Functions

Link to this macro

left &&& right

View Source (macro)

Result pipe operator.

The result pipe operator &&&/2 is a result-aware analogue to the pipe operator |>. It allows chaining functions that return results, piping the inner value of :ok results and short-circuiting the pipeline if any of the functions return an error. For example

with {:ok, x1} <- f1(x),
     {:ok, x2} <- f2(x1) do
  f3(x2)
end

can be written as

x |> f1() &&& f2() &&& f3()

More examples:

iex> {:ok, :foo} &&& Atom.to_string()
"foo"
iex> {:ok, 3} &&& then(fn x -> {:ok, x + 1} end) &&& List.wrap()
[4]
iex> {:ok, :foo} &&& Atom.to_string() |> String.capitalize()
"Foo"
iex> {:error, :oops} &&& Atom.to_string() |> String.capitalize()
{:error, :oops}
@spec all?([t()]) :: boolean()

Return true if an enumerable of results are all ok.

iex> Result.all?([{:ok, 1}, {:ok, 2}])
true
iex> Result.all?([{:ok, 1}, {:error, 2}])
false
@spec and_then(t(v, e), (v -> w)) :: w | {:error, e} when v: var, w: var, e: var

Transform an unwrapped ok value with f, or return an error unchanged.

Similar to map/2 except the transformed value is returned unwrapped. Useful when passing a result into another function that returns a result.

iex> {:ok, 3} |> Result.and_then(fn x -> x + 1 end)
4
iex> {:error, :nan} |> Result.and_then(fn x -> x + 1 end)
{:error, :nan}
@spec any?([t()]) :: boolean()

Return true if any of an enumerable of results is ok.

iex> Result.any?([{:ok, 1}, {:error, 2}])
true
iex> Result.any?([{:error, 1}, {:error, 2}])
false
Link to this function

assert_result!(maybe_result)

View Source
@spec assert_result!(any()) :: t() | no_return()

Assert a result.

Returns the value unchanged if it is a result; raises RuntimeError otherwise.

iex> Result.assert_result!({:ok, 42})
{:ok, 42}
iex> Result.assert_result!({:error, :not_found})
{:error, :not_found}
iex> Result.assert_result!(42)
** (RuntimeError) Not a result

iex> Result.assert_result!({:ok, 42, 43})
** (RuntimeError) Not a result

iex> Result.assert_result!(:ok)
** (RuntimeError) Not a result
@spec collect([t()]) :: t()

Collects a list of results into a single result.

If any of the results is an error, the first error is returned. Otherwise, a single ok result is returned with a list of the result values.

iex> [{:ok, 1}, {:ok, 2}, {:ok, 3}] |> Result.collect()
{:ok, [1, 2, 3]}
iex> [{:ok, 1}, {:error, 2}, {:ok, 3}, {:error, 4}] |> Result.collect()
{:error, 2}
Link to this function

err_if_nil(value, reason)

View Source
@spec err_if_nil(v | nil, e) :: t(v, e) when v: var, e: var

Convert a maybe-nil value to a result.

Maps nil to {:error, reason} and any non-nil value to {:ok, value}.

iex> %{"key" => "value"} |> Map.get("key") |> Result.err_if_nil(:notfound)
{:ok, "value"}
iex> %{"key" => "value"} |> Map.get("missing") |> Result.err_if_nil(:notfound)
{:error, :notfound}
@spec error(e) :: {:error, e} when e: var

Wrap a value in an error result.

iex> :some_error_reason |> Result.error()
{:error, :some_error_reason}
@spec error?(t()) :: boolean()

Return whether a result is an error.

iex> Result.error?({:ok, 3})
false
iex> Result.error?({:error, 3})
true
@spec map(t(v, e), (v -> w)) :: t(w, e) when v: var, w: var

Transform a result, mapping an ok value with f and leaving errors unchanged.

Similar to and_then/2 and useful for transformations that don't return a result.

iex> {:ok, 3} |> Result.map(fn x -> x + 1 end)
{:ok, 4}
iex> {:error, :nan} |> Result.map(fn x -> x + 1 end)
{:error, :nan}
@spec map_err(t(v, e), (e -> f)) :: t(v, f) when e: var, f: var

Return a result leaving ok values unchanged but transforming an error reason with f.

iex> Result.map_err({:ok, 3}, fn x -> x + 1 end)
{:ok, 3}
iex> Result.map_err({:error, :nan}, &:erlang.atom_to_binary/1)
{:error, "nan"}
Link to this function

map_or(result, default, f)

View Source
@spec map_or(t(v, any()), w, (v -> x)) :: x | w when w: var, v: var, x: var

Return an unwrapped ok value transformed by f, or default if result is an error.

iex> Result.map_or({:ok, 3}, 0, fn x -> x + 1 end)
4
iex> Result.map_or({:error, :nan}, 0, fn x -> x + 1 end)
0
@spec ok(v) :: {:ok, v} when v: var

Wrap a value in an ok result.

iex> 3 |> Result.ok()
{:ok, 3}
iex> Result.ok({:ok, 3})
{:ok, {:ok, 3}}
@spec ok?(t()) :: boolean()

Return whether a result is ok.

iex> Result.ok?({:ok, 3})
true
iex> Result.ok?({:error, 3})
false
@spec or_else(t(v, e), (e -> f)) :: {:ok, v} | f when e: var, f: var, v: var

Return an ok result unchanged, or transform an unwrapped error reason with f.

iex> Result.or_else({:ok, :xylophone}, fn err -> err + 1 end)
{:ok, :xylophone}
iex> Result.or_else({:error, 3}, fn err -> err + 1 end)
4
@spec result?(any()) :: boolean()

Returns true if the given value is a result tuple.

iex> Result.result?({:ok, 42})
true
iex> Result.result?({:error, :not_found})
true
iex> Result.result?(42)
false
iex> Result.result?({:ok, 42, 43})
false
iex> Result.result?(:ok)
false
@spec tap_err(t(v, e), (e -> any())) :: t(v, e) when e: var

Equivalent to Kernel.tap/2 for error results.

Calls f with the reason of an :error result, and returns the result unchanged.

iex> {:ok, 3} |> Result.tap_err(&IO.inspect/1)
{:ok, 3}
iex> {:error, :oops} |> Result.tap_err(&IO.inspect/1)
:oops
{:error, :oops}
@spec tap_ok(t(v), (v -> any())) :: t(v) when v: var

Equivalent to Kernel.tap/2 for ok results.

Calls f with the value of an :ok result, and returns the result unchanged.

iex> {:ok, 3} |> Result.tap_ok(&IO.inspect/1)
3
{:ok, 3}
iex> {:error, :oops} |> Result.tap_ok(&IO.inspect/1)
{:error, :oops}
@spec unwrap!(t(v)) :: v when v: var

Unwrap an :ok result, and raise an :error reason.

If an error reason is an exception, it is raised as-is; otherwise, a RuntimeError is raised with the inspected error reason in the exception message.

iex> Result.unwrap!({:ok, :value})
:value
iex> Result.unwrap!({:error, %{code: 500}})
** (RuntimeError) Unwrapped an error: %{code: 500}

iex> Result.unwrap!({:error, ArgumentError.exception("oh no")})
** (ArgumentError) oh no
@spec unwrap_err!(t(any(), e)) :: e when e: var
Link to this function

unwrap_or(result, default)

View Source
@spec unwrap_or(t(v), w) :: v | w when w: var, v: var

Unwrap an :ok result, falling back to default for an :error result.

iex> Result.unwrap_or({:ok, :cake}, :icecream)
:cake
iex> Result.unwrap_or({:error, :peas}, :icecream)
:icecream
Link to this function

unwrap_or_else(result, f)

View Source
@spec unwrap_or_else(t(v, e), (e -> w)) :: v | w when e: var, w: var, v: var

Unwrap an :ok result, or else act on the error in some way.

iex> Result.unwrap_or_else({:ok, 0}, fn e -> e + 1000 end)
0
iex> Result.unwrap_or_else({:error, 0}, fn e -> e + 1000 end)
1000