croma v0.4.5 Croma.Result

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}.

This module provides implementation of Croma.Monad interface for Croma.Result.t. 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}

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

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

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

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

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

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 Croma.Result by applying a function to its contained :error value. If the given Croma.Result is in :ok state it is returned without using the given function

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

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}

Macros

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

A macro that provides Hakell-like do-notation

Tries to take one Croma.Result in :ok state from the given two. If the first Croma.Result is in :ok state it is returned. Otherwise the second Croma.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

Types

t(a) :: {:ok, a} | {:error, any}

Functions

ap(ma, mf)

Specs

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 |>.

bind(result, f)

Specs

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.

error?(result)

Specs

error?(t(a)) :: boolean when a: any

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

get(result)

Specs

get(t(a)) :: nil | a when a: any

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

Examples

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

iex> Croma.Result.get({:error, :foo})
nil
get(result, default)

Specs

get(t(a), a) :: a when a: any

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

Examples

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

iex> Croma.Result.get({:error, :foo}, 0)
0
get!(result)

Specs

get!(t(a)) :: a when a: any

Returns the value associated with :ok in the given Croma.Result. Raises ArgumentError if the argument 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}
map(ma, f)

Specs

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 |>.

map_error(result, f)

Specs

map_error(t(a), (any -> any)) :: t(a) when a: any

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

ok?(result)

Specs

ok?(t(a)) :: boolean when a: any

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

pure(a)

Specs

pure([{:a, 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}.

sequence(l)

Specs

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}
try(f)

Specs

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"}}

Macros

define_bang_version_of(name_arity_pairs)

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.

m(list)

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)
or_else(result1, result2)

Tries to take one Croma.Result in :ok state from the given two. If the first Croma.Result is in :ok state it is returned. Otherwise the second Croma.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.