Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 79 additions & 4 deletions lib/ok.ex
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,43 @@ defmodule OK do
if is_list(result), do: {:ok, Enum.reverse(result)}, else: result
end

@doc """
Splits the `enumerable` in two lists according to the result of the given
function `func`.

Splits the given `enumerable` in two lists by calling `func` with each element
in the `enumerable` as its only argument. Returns a tuple with the first list
containing the result of all the elements in `enumerable` for which applying
`func` returned an :ok tuple, and a second list with all the elements for
which applying `func` returned an `:error` tuple.

The elements in both the returned lists are in the same relative order as they
were in the original enumerable (if such enumerable was ordered, like a
list). See the examples below.

## Examples
iex> OK.map_all_split([1, nil, 3, :a], fn x -> if is_integer(x), do: {:ok, x}, else: {:error, x} end)
{[1, 3], [nil, :a]}

iex> OK.map_all_split(%{a: 1, b: -2, c: 1, d: -3}, fn {k, v} -> if v < 0, do: {:ok, k}, else: {:error, k} end)
{[:b, :d], [:a, :c]}

iex> OK.map_all_split(%{}, fn _ -> :ok end)
{[], []}
"""
@spec map_all_split([a], (a -> {:ok, b} | {:error, reason})) :: {[a], [reason]}
when a: any, b: any, reason: any
def map_all_split(list, func) when is_function(func, 1) do
{oks, failures} =
list
|> Enum.map(func)
|> Enum.split_with(&success?/1)

ok_values = Enum.map(oks, &unwrap_ok/1)
failure_values = Enum.map(failures, &unwrap_failure/1)
{ok_values, failure_values}
end

@doc """
Takes a result tuple, a predicate function, and an error reason.
If the result tuple is tagged as a success then its value will be passed to the predicate function.
Expand Down Expand Up @@ -179,12 +216,12 @@ defmodule OK do
iex> require OK
...> f = fn result when OK.is_success(result) -> "ok" end
...> f.({:error, :some_reason})
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_success/1 (31)\"/1
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_success/1 (39)\"/1

iex> require OK
...> f = fn result when OK.is_success(result) -> "ok" end
...> f.(nil)
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_success/1 (32)\"/1
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_success/1 (40)\"/1
"""
@spec is_success(term()) :: Macro.t()
defguard is_success(result)
Expand All @@ -207,12 +244,12 @@ defmodule OK do
iex> require OK
...> f = fn result when OK.is_failure(result) -> "error" end
...> f.({:ok, "some value"})
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest."doctest OK.is_failure/1 (28)"/1
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest."doctest OK.is_failure/1 (36)"/1

iex> require OK
...> f = fn result when OK.is_failure(result) -> "error" end
...> f.(nil)
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_failure/1 (29)\"/1
** (FunctionClauseError) no function clause matching in anonymous fn/1 in OKTest.\"doctest OK.is_failure/1 (37)\"/1
"""
@spec is_failure(term()) :: Macro.t()
defguard is_failure(result)
Expand Down Expand Up @@ -264,6 +301,44 @@ defmodule OK do
def wrap({:error, reason}), do: {:error, reason}
def wrap(other), do: {:ok, other}

@doc """
Unwraps any term in an `:ok` tuple, `:error` tuple, or returns the value.

## Examples

iex> OK.unwrap("value")
"value"

iex> OK.unwrap({:ok, "value"})
"value"

iex> OK.unwrap({:error, "reason"})
"reason"
"""
def unwrap({:ok, value}), do: value
def unwrap({:error, reason}), do: reason
def unwrap(other), do: other

@doc """
Unwraps any term in an `:ok` tuple.

## Examples

iex> OK.unwrap_ok({:ok, "value"})
"value"
"""
def unwrap_ok({:ok, value}), do: value

@doc """
Unwraps any term in an `:error` tuple.

## Examples

iex> OK.unwrap_failure({:error, "reason"})
"reason"
"""
def unwrap_failure({:error, reason}), do: reason

@doc """
Require a variable not to be nil.

Expand Down