croma v0.10.2 Croma.Result View Source

A simple data structure to represent a result of computation that can either succeed or fail, in the form of {:ok, any} or {:error, any}.

In addition to many utility functions, this module also provides implementation of Croma.Monad interface for Croma.Result.t/1. This enables the following Haskell-ish syntax:

iex> use Croma
...> Croma.Result.m do
...>   x <- {:ok, 1}
...>   y <- {:ok, 2}
...>   pure x + y
...> end
{:ok, 3}

The above code is expanded to the code that uses pure/1 and bind/2.

Croma.Result.bind({:ok, 1}, fn x ->
  Croma.Result.bind({:ok, 2}, fn y ->
    Croma.Result.pure(x + y)
  end)
end)

This is useful when handling multiple computations that may go wrong in a short-circuit manner:

iex> use Croma
...> Croma.Result.m do
...>   x <- {:error, :foo}
...>   y <- {:ok, 2}
...>   pure x + y
...> end
{:error, :foo}

Link to this section Summary

Functions

Default implementation of Applicative’s ap operation. Modules that implement Croma.Monad may override this default implementation. Note that the order of arguments is different from the Haskell counterpart, in order to leverage Elixir’s pipe operator |>

Implementation of bind operation of Monad. Executes the given function if the result is in :ok state; otherwise returns the failed result

Based on existing functions that return Croma.Result.t(any), defines functions that raise on error

Returns true if the given result is in the form of {:error, _}

Returns the value associated with :ok in the given result. Returns nil if the result is in the form of {:error, _}

Returns the value associated with :ok in the given result. Returns default if the result is in the form of {:error, _}

Returns the value associated with :ok in the given result. Raises ArgumentError if the result is in the form of {:error, _}

A macro that provides Hakell-like do-notation

Default implementation of Functor’s fmap operation. Modules that implement Croma.Monad may override this default implementation. Note that the order of arguments is different from the Haskell counterpart, in order to leverage Elixir’s pipe operator |>

Transforms a result by applying a function to its contained :error value. If the given result is in :ok state it is returned without using the given function

Returns true if the given result is in the form of {:ok, _value}

Tries to take one result in :ok state from the given two. If the first result is in :ok state it is returned. Otherwise the second result is returned. Note that or_else/2 is a macro instead of a function in order to short-circuit evaluation of the second argument, i.e. the second argument is evaluated only when the first argument is in :error state

Implementation of pure operation of Monad (or Applicative). Wraps the given value into a Croma.Result, i.e., returns {:ok, arg}

Converts the given list of monadic (to be precise, applicative) objects into a monadic object that contains a single list. Modules that implement Croma.Monad may override this default implementation

Executes the given function within a try-rescue block and wraps the return value as {:ok, retval}. If the function raises an exception, try/1 returns the exception in the form of {:error, exception}

Simply checks if the given term is ok- or error-tuple

Wraps a given value in an :ok tuple if mod.valid?/1 returns true for the value. Otherwise returns an :error tuple

Link to this section Types

Link to this type t(a, b) View Source
t(a, b) :: {:ok, a} | {:error, b}

Link to this section Functions

Link to this function ap(ma, mf) View Source
ap(t(a), t((a -> b))) :: t(b) when a: any(), b: any()

Default implementation of Applicative’s ap operation. Modules that implement Croma.Monad may override this default implementation. Note that the order of arguments is different from the Haskell counterpart, in order to leverage Elixir’s pipe operator |>.

Link to this function bind(result, f) View Source
bind(t(a), (a -> t(b))) :: t(b) when a: any(), b: any()

Implementation of bind operation of Monad. Executes the given function if the result is in :ok state; otherwise returns the failed result.

Link to this macro define_bang_version_of(name_arity_pairs) View Source (macro)

Based on existing functions that return Croma.Result.t(any), defines functions that raise on error.

Each generated function simply calls the specified function and then passes the returned value to Croma.Result.get!/1.

Examples

iex> defmodule M do
...>   def f(a) do
...>     {:ok, a + 1}
...>   end
...>   Croma.Result.define_bang_version_of(f: 1)
...> end
iex> M.f(1)
{:ok, 2}
iex> M.f!(1)
2

If appropriate spec of original function is available, spec of the bang version is also declared. For functions that have default arguments it’s necessary to explicitly pass all arities to Croma.Result.define_bang_version_of/1.

Link to this function error?(result) View Source
error?(t(a)) :: boolean() when a: any()

Returns true if the given result is in the form of {:error, _}.

Link to this function get(result) View Source
get(t(a)) :: nil | a when a: any()

Returns the value associated with :ok in the given result. Returns nil if the result is in the form of {:error, _}.

Examples

iex> Croma.Result.get({:ok, 1})
1

iex> Croma.Result.get({:error, :foo})
nil
Link to this function get(result, default) View Source
get(t(a), a) :: a when a: any()

Returns the value associated with :ok in the given result. Returns default if the result is in the form of {:error, _}.

Examples

iex> Croma.Result.get({:ok, 1}, 0)
1

iex> Croma.Result.get({:error, :foo}, 0)
0
Link to this function get!(result) View Source
get!(t(a)) :: a when a: any()

Returns the value associated with :ok in the given result. Raises ArgumentError if the result is in the form of {:error, _}.

Examples

iex> Croma.Result.get!({:ok, 1})
1

iex> Croma.Result.get!({:error, :foo})
** (ArgumentError) element not present: {:error, :foo}

A macro that provides Hakell-like do-notation.

Examples

MonadImpl.m do
  x <- mx
  y <- my
  pure f(x, y)
end

is expanded to

MonadImpl.bind(mx, fn x ->
  MonadImpl.bind(my, fn y ->
    MonadImpl.pure f(x, y)
  end)
end)
Link to this function map(ma, f) View Source
map(t(a), (a -> b)) :: t(b) when a: any(), b: any()

Default implementation of Functor’s fmap operation. Modules that implement Croma.Monad may override this default implementation. Note that the order of arguments is different from the Haskell counterpart, in order to leverage Elixir’s pipe operator |>.

Link to this function map_error(result, f) View Source
map_error(t(a), (any() -> any())) :: t(a) when a: any()

Transforms a result by applying a function to its contained :error value. If the given result is in :ok state it is returned without using the given function.

Link to this function ok?(result) View Source
ok?(t(a)) :: boolean() when a: any()

Returns true if the given result is in the form of {:ok, _value}.

Link to this macro or_else(result1, result2) View Source (macro)

Tries to take one result in :ok state from the given two. If the first result is in :ok state it is returned. Otherwise the second result is returned. Note that or_else/2 is a macro instead of a function in order to short-circuit evaluation of the second argument, i.e. the second argument is evaluated only when the first argument is in :error state.

Link to this function pure(a) View Source
pure(a) :: t(a) when a: any()

Implementation of pure operation of Monad (or Applicative). Wraps the given value into a Croma.Result, i.e., returns {:ok, arg}.

Link to this function sequence(l) View Source
sequence([t(a)]) :: t([a]) when a: any()

Converts the given list of monadic (to be precise, applicative) objects into a monadic object that contains a single list. Modules that implement Croma.Monad may override this default implementation.

Examples (using Croma.Result)

iex> Croma.Result.sequence([{:ok, 1}, {:ok, 2}, {:ok, 3}])
{:ok, [1, 2, 3]}

iex> Croma.Result.sequence([{:ok, 1}, {:error, :foo}, {:ok, 3}])
{:error, :foo}
Link to this function try(f) View Source
try((() -> a)) :: t(a) when a: any()

Executes the given function within a try-rescue block and wraps the return value as {:ok, retval}. If the function raises an exception, try/1 returns the exception in the form of {:error, exception}.

Examples

iex> Croma.Result.try(fn -> 1 + 1 end)
{:ok, 2}

iex> Croma.Result.try(fn -> raise "foo" end)
{:error, %RuntimeError{message: "foo"}}

Simply checks if the given term is ok- or error-tuple.

Using this function you can write e.g. r :: v[Croma.Result.t(integer)] in your parameter list of defun macro to validate r is of type t/0. However note that this function only checks the outmost structure of an argument; 2nd value in the 2-tuple won’t be validated for the given type parameter (in the above example it won’t verify whether r contains an integer or not).

Link to this function wrap_if_valid(v, mod) View Source
wrap_if_valid(a, module()) :: t(a) when a: any()

Wraps a given value in an :ok tuple if mod.valid?/1 returns true for the value. Otherwise returns an :error tuple.