# Brex.Result
![](https://raw.githubusercontent.com/brexhq/result/master/Brex.Result.png)
[![Build Status](https://travis-ci.org/brexhq/result.svg?branch=master)](https://travis-ci.org/brexhq/result)
[![hex.pm version](https://img.shields.io/hexpm/v/brex_result.svg)](https://hex.pm/packages/brex_result)
[![Coverage Status](https://coveralls.io/repos/github/brexhq/result/badge.svg?branch=master)](https://coveralls.io/github/brexhq/result?branch=master)
[![license](https://img.shields.io/github/license/brexhq/result.svg)](https://github.com/brexhq/result/blob/master/LICENSE)
This library provides tools to handle three common return values in Elixir
```elixir
:ok | {:ok, value} | {:error, reason}
```
## Table of Contents
- [Overview](#overview)
- [Usage](#usage)
- [Differences rom Similar Libraries](#differences-from-similar-libraries)
- [Definitions](#definitions)
- [Types](#types)
- [Base](#base)
- [Helpers](#helpers)
- [Mappers](#mappers)
- [Known Problems](#known-problems)
- [Installation](#installation)
## Overview
`Brex.Result` is split into three main components:
- `Brex.Result.Base` - Base provides tools for creating and passing around `ok`/`error` tuples. The tools follow the property: if there’s a success continue the computation, if there’s an error propagate it.
- `Brex.Result.Helpers` - Helpers includes tools for modifying the reason in `error` tuples. The functions in this module always propogate the success value.
- `Brex.Result.Mappers` - Mappers includes tools for applying functions that return `:ok | {:ok, val} | {:error, reason}` over `Enumerables`.
## Usage
Include the line `use Brex.Result` to import the entire library or `import Brex.Result.{Base, Helpers, Mappers}` to import the modules individually.
A sampling of functions and examples are provided below. For more in-depth examples see [examples](https://github.com/brexhq/result/tree/master/examples).
## Differences from Similar Libraries
Other libraries like [OK](https://github.com/CrowdHailer/OK), [Monad](https://github.com/rmies/monad), and [Ok Jose](https://github.com/vic/ok_jose) have embraced the concept of monadic error handling and have analogous functions to the ones we have in `Brex.Result.Base`.
`Brex.Result` separates itself by:
- Extending support beyond classic monadic functions
- support for `:ok` as a success value
- support for modifying `errors` tuple reasons
- support for mapping functions that return `ok | {:ok, value} | {:error, reason}` over `enumerables`
- Respecting Elixir idioms
- encourages use of elixir builtins like the `with` statement when appropriate
- provides style guidelines
- actively avoids heavy macro magic that can turn a library into a DSL
## Definitions
- **Result tuples**:`{:ok, value} | {:error, reason}`
- **Error tuples**: `{:error, reason}`
- **OK/Success tuples**: `{:ok, value}`
- **Success values**: `:ok | {:ok, value}`
- **Propagate a value**: Return a value unchanged
## Types
Parametrized types
```elixir
@type s(x) :: {:ok, x} | {:error, any}
@type t(x) :: :ok | s(x)
```
Convenience types
```elixir
@type p() :: :ok | {:error, any}
@type s() :: s(any)
@type t() :: t(any)
```
#### Style Recommendation
Write specs and callbacks usings these shorthands.
```elixir
# Original
@spec my_fun({:ok, String.t} | {:error, any}, Integer) :: :ok | {:error, any}
# With shorthands
@spec my_fun(Brex.Result.s(String.t), Integer) :: Brex.Result.p()
```
## Base
Use `Brex.Result.Base.ok/1` to wrap a value in an `ok` tuple.
```elixir
iex> 2
...> |> ok
{:ok, 2}
```
`Brex.Result.Base.error/1` wraps a value in an `error` tuple.
```elixir
iex> :not_found
...> |> error
{:error, :not_found}
```
#### Style Recommendation
_Don't_ use `ok/1` and `error/1` when tuple syntax is more explicit:
```elixir
# No
ok(2)
# Yes
{:ok, 2}
```
_Do_ use `ok/1` and `error/1` at the end of a chain of pipes:
```elixir
# No
val =
arg
|> get_values()
|> transform(other_arg)
{:ok, val}
# Yes
arg
|> get_values()
|> transform(other_arg)
|> ok
```
Use `Brex.Result.Base.fmap/2` to transform the value within a success tuple. It propogates the error value.
```elixir
iex> {:ok, 2}
...> |> fmap(fn x -> x + 5 end)
{:ok, 7}
iex> {:error, :not_found}
...> |> fmap(fn x -> x + 5 end)
{:error, :not_found}
```
Use `Brex.Result.Base.bind/2` to apply a function to the value within a success tuple. The function _must_ returns a result tuple.
```elixir
iex> {:ok, 2}
...> |> bind(fn x -> if x > 0, do: {:ok, x + 5}, else: {:error, :neg})
{:ok, 7}
iex> {:ok, -1}
...> |> bind(fn x -> if x > 0, do: {:ok, x + 5}, else: {:error, :neg})
{:error, :neg}
iex> {:error, :not_found}
...> |> bind(fn x -> if x > 0, do: {:ok, x + 5}, else: {:error, :neg})
{:error, :not_found}
```
Infix bind is given for convience as `Brex.Result.Base.~>/2`
```elixir
iex> {:ok, [[1, 2, 3, 4]}
...> ~> Enum.member(2)
...> |> fmap(fn x -> if x, do: :found_two, else: :not_found end)
{:ok, :found_two}
```
#### Style Recommendation
Avoid single `~>`s and only use `~>` when the function argument is named and fits onto one line.
```elixir
# No
{:ok, file}
~> File.read()
# Yes
bind({:ok, file}, &File.read/1)
# No
{:ok, val}
~> (fn x -> if x > 0, do: {:ok, x}, else: {:error, neg}).()
~> insert_amount()
# Yes
{:ok, val}
|> bind(fn x -> if x > 0, do: {:ok, x}, else: {:error, neg})
~> insert_amount()
```
## Helpers
`Brex.Result.Helpers.map_error/2` allows you to transform the reason in an `error` tuple.
```elixir
iex> {:error, 404}
...> |> map_error(fn reason -> {:invalid_response, reason} end)
{:error, {:invalid_reponse, 404}}
iex> {:ok, 2}
...> |> map_error(fn reason -> {:invalid_response, reason} end)
{:ok, 2}
iex> :ok
...> |> map_error(fn reason -> {:invalid_response, reason} end)
:ok
```
`Brex.Result.Helpers.mask_error/2` disregards the current reason and replaces it with the second argument.
```elixir
iex> {:error, :not_found}
...> |> mask_error(:failure)
{:error, :failure}
```
`Brex.Result.Helpers.convert_error/3` converts an `error` into a success value if the reason matches the second argument.
```elixir
iex> {:error, :not_found}
...> |> convert_error(:not_found)
:ok
iex> {:error, :not_found}
...> |> convert_error(:not_found, default)
{:ok, default}
```
`Brex.Result.Helpers.log_error/3` logs on error. It automatically includes the reason in the log metadata.
```elixir
iex> {:error, :not_found}
...> |> log_error("There was a problem", level: :warn)
{:error, :not_found}
```
`Brex.Result.Helpers.normalize_error/2` converts a naked `:error` atom into an `error` tuple. It's good for functions from outside libraries.
```elixir
iex> :error
...> |> normalize_error(:not_found)
{:error, :not_found}
iex> {:ok, 2}
...> |> normalize_error(:not_found)
{:ok, 2}
iex> :ok
...> |> normalize_error(:not_found)
:ok
```
## Mappers
`Brex.Result.Mappers.map_while_success/2`, `Brex.Result.Mappers.each_while_success/2`, `Brex.Result.Mappers.reduce_while_success/3` all mimic the Enum functions `Enum.map/2`, `Enum.each/2`, `Enum.reduce/3`, but take a function that returns `:ok | {:ok, value} | {:error, reason}` as the mapping/reducing argument. Each of these functions produce a success value containing the final result or the first `error`.
## Known Problems
- Credo complains pipe chain is not started with raw value when preceeded by `~>`.
## Installation
The package can be installed by adding `brex_result` to your list of dependencies in `mix.exs`:
```elixir
def deps do
[
{:brex_result, "~> 0.4.0"}
]
end
```