ok v2.3.0 OK View Source
The OK
module enables clean and expressive error handling when coding with
idiomatic :ok
/:error
tuples. We’ve included many examples in the function
docs here, but you can also check out the
README for more
details and usage.
Feel free to open an issue for any questions that you have.
Link to this section Summary
Guards
Checks if a result tuple is tagged as :error
, and returns true
if so.
If the tuple is tagged as :ok
, returns false
Checks if a result tuple is tagged as :ok
, and returns true
if so.
If the tuple is tagged as :error
, returns false
Functions
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.
If the predicate returns true
, then the result tuple stay the same.
If the predicate returns false
, then the result tuple becomes {:error, reason}
.
If the tag is failure then the predicate function is skipped
Creates a failed result tuple with the given reason
Checks if a result tuple is tagged as :error
, and returns true
if so.
If the tuple is tagged as :ok
, returns false
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
Lightweight notation for working with the values from serval failible components
Applies a function to the interior value of a result tuple
Transform every element of a list with a mapping function. The mapping function must return a result tuple
Require a variable not to be nil
Wraps a value as a successful result tuple
Checks if a result tuple is tagged as :ok
, and returns true
if so.
If the tuple is tagged as :error
, returns false
Handle return value from several failible functions
Wraps any term in an :ok
tuple, unless already a result monad
Pipeline version of map/2
The OK result pipe operator ~>>
, or result monad flat_map operator, is similar
to Elixir’s native |>
except it is used within happy path. It takes the
value out of an {:ok, value}
tuple and passes it as the first argument to
the function call on the right
Link to this section Guards
Checks if a result tuple is tagged as :error
, and returns true
if so.
If the tuple is tagged as :ok
, returns false
.
Allowed in guards.
Examples
iex> require OK
...> f = fn result when OK.is_failure(result) -> "error" end
...> f.({:error, :some_reason})
"error"
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
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
Checks if a result tuple is tagged as :ok
, and returns true
if so.
If the tuple is tagged as :error
, returns false
.
Allowed in guards.
Examples
iex> require OK
...> f = fn result when OK.is_success(result) -> "ok" end
...> f.({:ok, "some value"})
"ok"
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
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
Link to this section Functions
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.
If the predicate returns true
, then the result tuple stay the same.
If the predicate returns false
, then the result tuple becomes {:error, reason}
.
If the tag is failure then the predicate function is skipped.
Examples
iex> OK.check({:ok, 2}, fn (x) -> x == 2 end, :bad_value)
{:ok, 2}
iex> OK.check({:ok, 2}, fn (x) -> x == 3 end, :bad_value)
{:error, :bad_value}
iex> OK.check({:error, :some_reason}, fn (x) -> x == 4 end, :bad_value)
{:error, :some_reason}
Creates a failed result tuple with the given reason.
Examples
iex> OK.failure("reason")
{:error, "reason"}
Checks if a result tuple is tagged as :error
, and returns true
if so.
If the tuple is tagged as :ok
, returns false
.
Examples
iex> OK.failure?({:error, :some_reason})
true
iex> OK.failure?({:ok, "some value"})
false
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.flat_map({:ok, 2}, fn (x) -> {:ok, 2 * x} end)
{:ok, 4}
iex> OK.flat_map({:error, :some_reason}, fn (x) -> {:ok, 2 * x} end)
{:error, :some_reason}
Lightweight notation for working with the values from serval failible components.
Values are extracted from an ok tuple using the in (<-
) operator.
Any line using this operator that trys to match on an error tuple will result in early return.
If all bindings can be made, i.e. all functions returned {:ok, value}
,
then the after block is executed to return the final value.
Return values from the after block are wrapped as an ok result, unless they are already a result tuple. The return value of a for comprehension is always a result monad
iex> OK.for do
...> a <- safe_div(8, 2)
...> b <- safe_div(a, 2)
...> after
...> a + b
...> end
{:ok, 6.0}
iex> OK.for do
...> a <- safe_div(8, 2)
...> b <- safe_div(a, 2)
...> after
...> OK.success(a + b)
...> end
{:ok, 6.0}
iex> OK.for do
...> a <- safe_div(8, 2)
...> _ <- safe_div(a, 2)
...> after
...> {:error, :something_else}
...> end
{:error, :something_else}
Regular matching using the =
operator is also available,
for calculating intermediate values.
iex> OK.for do
...> a <- safe_div(8, 2)
...> b = 2.0
...> after
...> a + b
...> end
{:ok, 6.0}
iex> OK.for do
...> a <- safe_div(8, 2)
...> b <- safe_div(a, 0) # error here
...> after
...> a + b # does not execute this line
...> end
{:error, :zero_division}
iex> OK.for do: :literal, after: :result
{:ok, :result}
Applies a function to the interior value of a result tuple.
If the tuple is tagged :ok
the value will be mapped by the function.
A tuple tagged :error
will be unchanged.
Examples
iex> OK.map({:ok, 2}, fn (x) -> 2 * x end)
{:ok, 4}
iex> OK.map({:error, :some_reason}, fn (x) -> 2 * x end)
{:error, :some_reason}
Transform every element of a list with a mapping function. The mapping function must return a result tuple.
If all of the result tuples are tagged :ok, then it returns a list tagged with :ok. If one or more of the result tuples are tagged :error, it returns the first error.
Examples
iex> OK.map_all(1..3, &safe_div(6, &1))
{:ok, [6.0, 3.0, 2.0]}
iex> OK.map_all([-1, 0, 1], &safe_div(6, &1))
{:error, :zero_division}
Require a variable not to be nil.
Optionally provide a reason why variable is required.
Examples
iex> OK.required(:some)
{:ok, :some}
iex> OK.required(nil)
{:error, :value_required}
iex> OK.required(Map.get(%{}, :port), :port_number_required)
{:error, :port_number_required}
Checks if a result tuple is tagged as :ok
, and returns true
if so.
If the tuple is tagged as :error
, returns false
.
Examples
iex> OK.success?({:ok, "some value"})
true
iex> OK.success?({:error, :some_reason})
false
Handle return value from several failible functions.
Values are extracted from an ok tuple using the in (<-
) operator.
Any line using this operator that trys to match on an error tuple will result in early return.
If all bindings can be made, i.e. all functions returned {:ok, value}
,
then the after block is executed to return the final value.
If any binding fails then the rescue block will be tried.
Note: return value from after will be returned unwrapped
Examples
iex> OK.try do
...> a <- safe_div(8, 2)
...> b <- safe_div(a, 2)
...> after
...> a + b
...> rescue
...> :zero_division ->
...> :nan
...> end
6.0
iex> OK.try do
...> a <- safe_div(8, 2)
...> b <- safe_div(a, 0)
...> after
...> a + b
...> rescue
...> :zero_division ->
...> :nan
...> end
:nan
Wraps any term in an :ok
tuple, unless already a result monad.
Examples
iex> OK.wrap("value")
{:ok, "value"}
iex> OK.wrap({:ok, "value"})
{:ok, "value"}
iex> OK.wrap({:error, "reason"})
{:error, "reason"}
Pipeline version of map/2
.
Examples
iex> {:ok, 5} ~> Integer.to_string
{:ok, "5"}
iex> {:error, :zero_division_error} ~> Integer.to_string
{:error, :zero_division_error}
iex> {:ok, "a,b"} ~> String.split(",")
{:ok, ["a", "b"]}
The OK result pipe operator ~>>
, or result monad flat_map operator, is similar
to Elixir’s native |>
except it is used within happy path. It takes the
value out of an {:ok, value}
tuple and passes it as the first argument to
the function call on the right.
It can be used in several ways.
Pipe to a local call.
(This is equivalent to calling double(5)
)
iex> {:ok, 5} ~>> double()
{:ok, 10}
Pipe to a remote call.
(This is equivalent to calling OKTest.double(5)
)
iex> {:ok, 5} ~>> OKTest.double()
{:ok, 10}
iex> {:ok, 5} ~>> __MODULE__.double()
{:ok, 10}
Pipe with extra arguments.
(This is equivalent to calling safe_div(6, 2)
)
iex> {:ok, 6} ~>> safe_div(2)
{:ok, 3.0}
iex> {:ok, 6} ~>> safe_div(0)
{:error, :zero_division}
It also works with anonymous functions.
iex> {:ok, 3} ~>> (fn (x) -> {:ok, x + 1} end).()
{:ok, 4}
iex> {:ok, 6} ~>> decrement().(2)
{:ok, 4}
When an error is returned anywhere in the pipeline, it will be returned.
iex> {:ok, 6} ~>> safe_div(0) ~>> double()
{:error, :zero_division}
iex> {:error, :previous_bad} ~>> safe_div(0) ~>> double()
{:error, :previous_bad}