Current section

Files

Jump to
ok lib ok.ex
Raw

lib/ok.ex

defmodule OK do
@moduledoc """
The `OK` module enables clean and expressive error handling in pipelines.
Many Elixir libraries follow the tagged tuple convention for functions that will not alway return a valid response.
In case of a success the value is returned in an `:ok` tagged tuple.
If the function fails then a reason is returned in an `:error` tagged tuple.
Calling code the matches on these two possible outcomes.
```elixir
case my_func(args) do
{:ok, value} ->
do_more(value) # continue with subsequent processing
{:error, reason} ->
{:error, reason} # return early.
end
```
`OK` allows this code to be replaced by a result pipeline.
```elixir
my_func(args)
~>> &do_more/1
```
*`OK` treates the combination of tagged tuples `{:ok, value} | {:error, reason}` as a result monad.
The result monad is sometimes know as the try or either monad.*
"""
@doc """
Takes a result tuple and a next function.
If the result tuple is tagged as a success then its value will be passed to the next function.
If the tag is failure then the next function is skipped.
## Examples
iex> OK.bind({:ok, 2}, fn (x) -> {:ok, 2 * x} end)
{:ok, 4}
iex> OK.bind({:error, :some_reason}, fn (x) -> {:ok, 2 * x} end)
{:error, :some_reason}
"""
def bind({:ok, value}, func) when is_function(func, 1), do: func.(value)
def bind(failure = {:error, _reason}, _func), do: failure
@doc """
Wraps a value as a successful result tuple.
## Examples
iex> OK.success(:value)
{:ok, :value}
"""
def success(value), do: {:ok, value}
@doc """
Creates a failed result tuple with the given reason.
## Examples
iex> Ok.failure("reason")
{:error, "reason"}
"""
def failure(reason), do: {:error, reason}
defmacro lhs ~>> rhs do
quote bind_quoted: [lhs: lhs, rhs: rhs] do
OK.bind(lhs, rhs)
end
end
end