# OK

**Elegant error handling in elixir pipelines. See [Handling Errors in Elixir]( for a more detailed explanation**

[Documentation for OK is available on hexdoc](

## Installation

[Available in Hex](, the package can be installed as:

  1. Add ok to your list of dependencies in `mix.exs`:

    def deps do
      [{:ok, "~> 1.3.0"}]

## Usage

The erlang convention for functions that can fail is to return a result tuple.
A result tuple is a two-tuple tagged either as a success(`:ok`) or a failure(`:error`).

The OK module works with result tuples by treating them as a result monad.

{:ok, value} | {:error, reason}

### Result pipelines `~>>`

This macro allows pipelining result tuples through a pipeline of functions.
The `~>>` macro is the is equivalent to bind/flat_map in other languages.

import OK, only: ["~>>": 2]

def get_employee_data(file, name) do
  {:ok, file}
  ~>> Poison.decode
  ~>> Dict.fetch(name)

def handle_user_data({:ok, data}), do: IO.puts("Contact at #{data["email"]}")
def handle_user_data({:error, :enoent}), do: IO.puts("File not found")
def handle_user_data({:error, {:invalid, _}}), do: IO.puts("Invalid JSON")
def handle_user_data({:error, :key_not_found}), do: IO.puts("Could not find employee")

|> handle_user_data

### Result blocks `with`

For situations when the pipeline macro is not sufficiently flexible.

To extract a value for an ok tuple use the `<-` operator.

require OK

OK.with do
  user <- fetch_user(1)
  cart <- fetch_cart(1)
  order = checkout(cart, user)

The above code is equivalent to
case fetch_user(1) do
  {:ok, user} ->
    case fetch_cart(1) do
      {:ok, cart} ->
        order = checkout(cart, user)
      {:error, reason} ->
        {:error, reason}
  {:error, reason} ->
    {:error, reason}

### Railway programming

`OK` can be used for railway programming.
An explanation of this is available in this [blog](

### Similar Libraries

For reference.

- [exceptional](
- [elixir-monad](
- [happy_with](
- [monad](
- [ok_jose](
- [towel](

*Possible extensions to include implementing bind on structs so that errors can be better handled.
Implement a catch functionality for functions that error.
Implement existing monad library protocols so can extend similar DB functionality e.g. slick*