View Source Fey.Result (Fey v0.0.3)

The module provides functions to work with a well-established pattern of result tuples (sometimes called "tagged tuples") in Elixir. By them we mean {:ok, value} or {:error, error} tuples.

Fey.Result adds a bunch of convenience to work with these, especially in pipelines. They can replace with chains, which are frowned upon by some people in the Elixir community. Let's see an example:

Repo.fetch(OrderLineItem, 123)
|> Fey.Result.map(& &1.sku)
|> Fey.Result.bind(&Repo.get_by(Product, sku: &1) |> Fey.Result.wrap_not_nil(:sku_not_found))
|> Fey.Result.map(& &1.name)

This either returns a name of the product for line item (matches by SKU) or any of the intermediate errors that might happpen: {:ok, :not_found} (from Repo.fetch) or {:error, :sku_not_found}.

Summary

Functions

If the result is a success, returns a value of fun applied on result's value. It's analogous to map/2, but does not wrap in {:ok, val} automatically, which makes it useful if fun returns a result.

If the result is an error, return its details.

Returns whether or not the passed value is an error.

If the result is a success, returns its value.

If the result is a success, return its value but if the result is an error, return default value instead of raising exception (like get!/1 does).

If the result is a success, returns {:ok, val}, where val is a return value of fun applied on result's value.

Returns whether or not the passed value is a success. Raises Fey.Result.BadArgument if the value is not a valid result tuple.

Inverse of and_then/2 - if the result is an error, executes fun and returns its result.

Wraps a value in a success result tuple (i.e. {:ok, value}).

Similar to wrap/1, but only returns a success result if the passed value is not nil. If it is, {:error, error} is returned, where error is either passed as a second argument, or by default is :not_found.

Types

@type t(value) :: success(value) | error()

Functions

@spec and_then(t(a), (a -> b)) :: b | error() when a: term(), b: term()

If the result is a success, returns a value of fun applied on result's value. It's analogous to map/2, but does not wrap in {:ok, val} automatically, which makes it useful if fun returns a result.

If the result is an error, returns error itself.

Raises Fey.Result.BadArgument if the value passed as argument is not a valid result tuple.

Examples

iex> Fey.Result.and_then({:ok, 42}, fn v -> {:ok, v / 2} end)
{:ok, 21.0}

iex> Fey.Result.and_then({:ok, 42}, fn v -> v / 2 end)
21.0

iex> Fey.Result.and_then({:error, :not_found}, fn v -> {:ok, v / 2} end)
{:error, :not_found}

iex> Fey.Result.and_then(:some_atom, fn v -> {:ok, v / 2} end)
** (Fey.Result.BadArgument) :some_atom is not a valid result tuple
@spec error!(t(term())) :: term()

If the result is an error, return its details.

Raises NotError if the result is not errorneous.

Raises Fey.Result.BadArgument is the value passed as argument is not a valid result tuple.

Examples

iex> Fey.Result.error!({:error, :not_found})
:not_found

iex> Fey.Result.error!({:ok, 42})
** (Fey.Result.NotError) {:ok, 42} is not an error

iex> Fey.Result.error!(:some_atom)
** (Fey.Result.BadArgument) :some_atom is not a valid result tuple
@spec error?(t(term())) :: boolean()

Returns whether or not the passed value is an error.

Raises Fey.Result.BadArgument if the value is not a valid result tuple.

Examples

iex> Fey.Result.error?({:ok, true})
false

iex> Fey.Result.error?({:error, :not_found})
true

iex> Fey.Result.error?(42)
** (Fey.Result.BadArgument) 42 is not a valid result tuple
@spec get!(t(term())) :: term()

If the result is a success, returns its value.

Raises Fey.Result.NotSuccess if the result is not successful. Raises Fey.Result.BadArgument if the value passed as argument is not a valid result tuple.

Examples

iex> Fey.Result.get!({:ok, 42})
42

iex> Fey.Result.get!({:error, :not_found})
** (Fey.Result.NotSuccess) {:error, :not_found} is not a success

iex> Fey.Result.get!(:some_atom)
** (Fey.Result.BadArgument) :some_atom is not a valid result tuple
Link to this function

get_or(result, default_value)

View Source
@spec get_or(t(a), a) :: a when a: var

If the result is a success, return its value but if the result is an error, return default value instead of raising exception (like get!/1 does).

Raises Fey.Result.BadArgument if the value passed as argument is not a valid result tuple.

Examples

iex> Fey.Result.get_or({:ok, 42}, 1567)
42

iex> Fey.Result.get_or({:error, :not_found}, 1567)
1567

iex> Fey.Result.get_or("string", 1567)
** (Fey.Result.BadArgument) "string" is not a valid result tuple
@spec map(t(a), (a -> b)) :: t(b) when a: term(), b: term()

If the result is a success, returns {:ok, val}, where val is a return value of fun applied on result's value.

If the result is an error, returns error itself.

Raises Fey.Result.BadArgument if the value passed as argument is not a valid result tuple.

Examples

iex> Fey.Result.map({:ok, 42}, fn v -> v / 2 end)
{:ok, 21.0}

iex> Fey.Result.map({:error, :not_found}, fn v -> v / 2 end)
{:error, :not_found}

iex> Fey.Result.map(:some_atom, fn v -> v / 2 end)
** (Fey.Result.BadArgument) :some_atom is not a valid result tuple
@spec ok?(t(term())) :: boolean()

Returns whether or not the passed value is a success. Raises Fey.Result.BadArgument if the value is not a valid result tuple.

Examples

iex> Fey.Result.ok?({:ok, true})
true

iex> Fey.Result.ok?({:error, :not_found})
false

iex> Fey.Result.ok?(42)
** (Fey.Result.BadArgument) 42 is not a valid result tuple
@spec or_else(t(value), (-> b)) :: t(a) | b when value: term(), a: term(), b: term()

Inverse of and_then/2 - if the result is an error, executes fun and returns its result.

If the result is success, just returns it.

Raises Fey.Result.BadArgument if the value passed as argument is not a valid result tuple.

This function makes most sense when used in a pipeline where you want to take the first successful result. For example:

parse_as_iso_datetime(input)
|> Fey.Result.or_else(fn -> parse_as_iso_date(input) end)
|> Fey.Result.or_else(fn -> parse_as_naive_datetime(input) end)
|> Fey.Result.or_else(fn -> parse_as_naive_date(input) end)

# {:ok, parsed_date} | {:error, :bad_format}

Examples

iex> Fey.Result.or_else({:error, :not_found}, fn -> {:ok, 42} end)
{:ok, 42}

iex> Fey.Result.or_else({:ok, nil}, fn -> {:ok, 42} end)
{:ok, nil}

iex> Fey.Result.or_else(nil, fn -> {:ok, 42} end)
** (Fey.Result.BadArgument) nil is not a valid result tuple
@spec wrap(value) :: success(value) when value: term()

Wraps a value in a success result tuple (i.e. {:ok, value}).

Note that it does not check whether a value passed as argument is already a valid result tuple, or not.

Examples

iex> Fey.Result.wrap(true)
{:ok, true}

iex> Fey.Result.wrap({:ok, "fourty two"})
{:ok, {:ok, "fourty two"}}
Link to this function

wrap_not_nil(value, error \\ :not_found)

View Source
@spec wrap_not_nil(term(), term()) :: t(term())

Similar to wrap/1, but only returns a success result if the passed value is not nil. If it is, {:error, error} is returned, where error is either passed as a second argument, or by default is :not_found.

Note that generally Fey.Option.wrap_not_nil/1 is preferred, as it's more idiomatic. However, if you're set on using Result, this is available.

Examples

iex> Fey.Result.wrap_not_nil(42)
{:ok, 42}

iex> Fey.Result.wrap_not_nil(nil)
{:error, :not_found}

iex> Fey.Result.wrap_not_nil(nil, :number_missing)
{:error, :number_missing}