# `Croma.Result`
[🔗](https://github.com/skirino/croma/blob/main/lib/croma/result.ex#L1)

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 `t: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}

# `t`

```elixir
@type t() :: t(any())
```

# `t`

```elixir
@type t(a) :: t(a, any())
```

# `t`

```elixir
@type t(a, b) :: {:ok, a} | {:error, b}
```

# `ap`

```elixir
@spec ap(t(a), t((a -&gt; 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`

```elixir
@spec bind(t(a), (a -&gt; 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.

# `define_bang_version_of`
*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`.

# `error?`

```elixir
@spec error?(t(a)) :: boolean() when a: any()
```

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

# `get`

```elixir
@spec 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

# `get`

```elixir
@spec 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

# `get!`

```elixir
@spec 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}

# `m`
*macro* 

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)

# `map`

```elixir
@spec map(t(a), (a -&gt; 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`

```elixir
@spec map_error(t(a), (any() -&gt; 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.

# `ok?`

```elixir
@spec ok?(t(a)) :: boolean() when a: any()
```

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

# `or_else`
*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.

# `pure`

```elixir
@spec 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}`.

# `sequence`

```elixir
@spec 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`

```elixir
@spec try((-&gt; 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"}}

# `valid?`

```elixir
@spec valid?(any()) :: boolean()
```

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

# `wrap_if_valid`

```elixir
@spec 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.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
