Error handling for Elixir. Let it flow.
Err is a tiny library for composing and normalizing error flows in Elixir.
It works with the conventions Elixir developers already use:
{:ok, value}and{:error, reason}nilas absence- existing return values from Phoenix, Ecto, Oban, and your own code
Instead of introducing a new result type or DSL, Err helps turn mixed return styles into
flows that are easier to compose, transform, and reason about.
Use it to:
- compose result pipelines cleanly
- normalize
nil, tuples, and exception-based APIs - transform errors close to the boundary
- keep
with-based application code readable
Features
- Works with existing
:ok/:errortuples of any size - Treats
nilas absence for Option-style flows - Normalizes values into result flows with
from_nil/2andtry_rescue/2 - Wraps
Taskwork withasync/1,await/2, andawait_many/2 - Composes success and error paths with
map/2,map_err/2,and_then/2, andor_else/2 - Adds side effects without changing values using
tap/2andtap_err/2 - Keeps branching explicit with
match/2 - Includes list helpers like
all/1,values/1, andpartition/1 - Ships with exception helpers like
wrap/1andmessage/1
Installation
Add err to your list of dependencies in mix.exs:
def deps do
[
{:err, "~> 0.2"}
]
endWhy Err?
Elixir already has excellent primitives for error handling: pattern matching, with, case,
and tagged tuples.
The friction usually starts when application code combines several styles from several libraries:
Repo.get/2returnsnilRepo.insert/2returns{:ok, struct}or{:error, changeset}- some APIs raise exceptions
- others return custom tuples or atoms
Err is a small glue layer for normalizing those differences.
Usage
Wrap values
iex> Err.ok(42)
{:ok, 42}
iex> Err.error(:timeout)
{:error, :timeout}Normalize nil into a result
iex> Err.from_nil("config.json", :not_found)
{:ok, "config.json"}
iex> Err.from_nil(nil, :not_found)
{:error, :not_found}Convert raising code into a result
iex> Err.try_rescue(fn -> 100 + 23 end)
{:ok, 123}
iex> Err.try_rescue(fn -> raise "boom" end) |> Err.map_err(&Exception.message/1)
{:error, "boom"}Run Task work through results
iex> task = Err.async(fn -> 40 + 2 end)
iex> Err.await(task)
{:ok, 42}
iex> [Task.async(fn -> 1 end), Task.async(fn -> {:error, :boom} end)] |> Err.await_many()
[{:ok, 1}, {:error, :boom}]Unwrap with defaults
iex> Err.unwrap_or({:ok, "config.json"}, "default.json")
"config.json"
iex> Err.unwrap_or({:error, :not_found}, "default.json")
"default.json"Transform success values
iex> Err.map({:ok, 5}, fn num -> num * 2 end)
{:ok, 10}Transform error values
iex> Err.map_err({:error, :timeout}, fn reason -> "#{reason}_error" end)
{:error, "timeout_error"}Chain operations
iex> Err.and_then({:ok, 5}, fn num -> {:ok, num * 2} end)
{:ok, 10}Add side effects without changing the result
iex> Err.tap({:ok, 5}, fn value -> send(self(), {:seen, value}) end)
{:ok, 5}
iex> Err.tap_err({:error, :timeout}, fn reason -> send(self(), {:seen_error, reason}) end)
{:error, :timeout}Branch explicitly at the boundary
iex> Err.match({:ok, 5}, ok: &(&1 * 2), error: fn _ -> 0 end)
10
iex> Err.match(nil, ok: & &1, error: fn _ -> :missing end)
:missingFlatten nested results
iex> Err.flatten({:ok, {:ok, 1}})
{:ok, 1}Eager fallback
iex> Err.or_else({:error, :cache_miss}, {:ok, "disk.db"})
{:ok, "disk.db"}Lazy fallback
iex> Err.or_else_lazy({:error, :cache_miss}, fn _reason -> {:ok, "disk.db"} end)
{:ok, "disk.db"}Combine results (fail fast)
iex> Err.all([{:ok, 1}, {:ok, 2}, {:ok, 3}])
{:ok, [1, 2, 3]}
iex> Err.all([{:ok, 1}, {:error, :timeout}])
{:error, :timeout}Extract ok values
iex> Err.values([{:ok, 1}, {:error, :x}, {:ok, 2}])
[1, 2]Split into ok and error lists
iex> Err.partition([{:ok, 1}, {:error, "a"}, {:ok, 2}])
{[1, 2], ["a"]}Check if result is ok
def process(result) when Err.is_ok(result) do
result
endCheck if result is error
def process(result) when Err.is_err(result) do
result
endReal-World Example
def fetch_user_profile(id) do
with {:ok, user} <- Repo.get(User, id) |> Err.from_nil(:not_found),
{:ok, account} <- Accounts.fetch_account(user) |> Err.map_err(&normalize_error/1),
{:ok, stats} <- Stats.fetch(account) |> Err.map_err(&normalize_error/1) do
{:ok, %{user: user, account: account, stats: stats}}
end
endErr complements with, case, and pattern matching. It does not try to replace them.
Summary
Types
An option type representing either some value or none.
A result type representing either success or failure.
Functions
Combines a list of values into a single result.
Chains the result by calling fun when the value is present.
Starts a task and normalizes its return value into a result.
Awaits a task and converts its outcome into a result without exiting the caller.
Awaits multiple tasks and converts each outcome into a result without exiting the caller.
Ensures the value satisfies predicate, otherwise returns {:error, error}.
Returns true when the value is an error result and its payload satisfies predicate.
Wraps value in an {:error, value} tuple.
Returns the wrapped value from an {:ok, value} tuple or raises the provided exception.
Returns the wrapped error from an {:error, reason} tuple or raises the provided exception.
Flattens a nested result into a single layer.
Returns the second value if the first one is successful/present.
Normalizes a nullable value into a result.
Checks if a value is an {:error, ...} result tuple.
Checks if a value is none (nil).
Checks if a value is an {:ok, ...} result tuple.
Checks if a value is "some" (not nil).
Transforms the success value inside an {:ok, value} tuple or some value by applying a function to it.
Transforms the error inside an {:error, reason} tuple by applying a function to it.
Matches on success/presence and error/absence with explicit handlers.
Wraps value in an {:ok, value} tuple.
Returns true when the value is an ok result and its payload satisfies predicate.
Returns the first value if it is present/successful, otherwise returns the second value.
Returns the first value if it is present/successful, otherwise calls the function to compute an alternative value.
Splits a list of results into ok values and error values.
Replaces the value inside an {:ok, value} tuple with a new value.
Replaces the error inside an {:error, reason} tuple with a new value.
Replaces the error inside an {:error, reason} tuple by calling a function.
Replaces the value inside an {:ok, value} tuple by calling a function.
Returns true when the value is present and satisfies predicate.
Calls fun with the success value and returns the original value unchanged.
Calls fun with the error value and returns the original value unchanged.
Executes fun and converts rescued exceptions into an error result.
Returns the wrapped error or default when the result is ok or value is present.
Returns the wrapped value or default when the result is error or value is empty.
Returns the wrapped value or computes it from default_fun when the result is an error or value
is empty.
Extracts all success values from a list of results.
Combines two successful/present values into a pair.
Types
@type option() :: any() | nil
An option type representing either some value or none.
Can be:
value- Some value is presentnil- No value (none)
@type result() :: tuple()
A result type representing either success or failure.
Can be:
{:ok, value}- A successful result with a value{:error, error}- A failed result with an error- Any tuple starting with
:okor:error(supports multiple elements)
Functions
Combines a list of values into a single result.
- If all values are
{:ok, value}, returns{:ok, list_of_values}. - If any value is an error, returns the first error encountered (fail fast).
- If any value is
nil, returnsnil
Examples
iex> Err.all([{:ok, 1}, {:ok, 2}, {:ok, 3}])
{:ok, [1, 2, 3]}
iex> Err.all([{:ok, 1}, {:error, :timeout}, {:ok, 3}])
{:error, :timeout}
iex> Err.all([{:ok, 1}, nil, {:ok, 3}])
nil
iex> Err.all([])
{:ok, []}
iex> Err.all([{:ok, "a"}, {:ok, "b"}])
{:ok, ["a", "b"]}
Chains the result by calling fun when the value is present.
For {:ok, value} the extracted value (or list of values) is passed to fun.
Error tuples and nil are returned unchanged, allowing the pipeline to short-circuit.
Examples
iex> Err.and_then({:ok, 5}, fn num -> num * 2 end)
10
iex> Err.and_then(5, fn num -> num * 2 end)
10
iex> Err.and_then({:ok, :admin, %{id: 1}}, fn [role, user] -> {:ok, %{role: role, user_id: user.id}} end)
{:ok, %{role: :admin, user_id: 1}}
iex> Err.and_then({:error, :timeout}, fn num -> {:ok, num * 2} end)
{:error, :timeout}
iex> Err.and_then(nil, fn value -> {:ok, value} end)
nil
Starts a task and normalizes its return value into a result.
Plain values are wrapped as {:ok, value}. Existing result tuples are returned unchanged.
Rescued exceptions become {:error, exception}. Throws and exits are returned as tagged errors.
This is useful when adapting Task-based work into the same result flow used by synchronous
code.
Examples
iex> task = Err.async(fn -> 40 + 2 end)
iex> Err.await(task)
{:ok, 42}
iex> task = Err.async(fn -> {:ok, :cached} end)
iex> Err.await(task)
{:ok, :cached}
iex> task = Err.async(fn -> raise "boom" end)
iex> Err.await(task) |> Err.map_err(&Exception.message/1)
{:error, "boom"}
Awaits a task and converts its outcome into a result without exiting the caller.
Plain task replies are wrapped as {:ok, value}. Existing result tuples are returned unchanged.
If the task exits, returns {:error, {:exit, reason}}. If the timeout is reached, the task is
shut down and {:error, :timeout} is returned.
Examples
iex> Task.async(fn -> 21 * 2 end) |> Err.await()
{:ok, 42}
iex> Task.async(fn -> {:error, :not_found} end) |> Err.await()
{:error, :not_found}
Awaits multiple tasks and converts each outcome into a result without exiting the caller.
Results are returned in the same order as the input tasks. Each reply follows the same
normalization rules as await/2.
Examples
iex> [Task.async(fn -> 1 end), Task.async(fn -> {:error, :boom} end)] |> Err.await_many()
[{:ok, 1}, {:error, :boom}]
Ensures the value satisfies predicate, otherwise returns {:error, error}.
Existing error tuples are returned unchanged. For successful result tuples the extracted value
(or list of values) is passed to predicate. For plain values and option-style values, a truthy
predicate keeps the original value and a falsy predicate returns {:error, error}.
Examples
iex> Err.ensure({:ok, 10}, &(&1 > 5), :too_small)
{:ok, 10}
iex> Err.ensure({:ok, 3}, &(&1 > 5), :too_small)
{:error, :too_small}
iex> Err.ensure({:error, :timeout}, &(&1 > 5), :too_small)
{:error, :timeout}
iex> Err.ensure("hello", &(String.length(&1) > 3), :too_short)
"hello"
iex> Err.ensure(nil, & &1, :missing)
{:error, :missing}
Returns true when the value is an error result and its payload satisfies predicate.
Returns false for non-error values without calling predicate.
Examples
iex> Err.err_and?({:error, :timeout}, &(&1 == :timeout))
true
iex> Err.err_and?({:error, :boom}, &(&1 == :timeout))
false
iex> Err.err_and?({:ok, 1}, &(&1 == :timeout))
false
Wraps value in an {:error, value} tuple.
Examples
iex> Err.error(:timeout)
{:error, :timeout}
iex> Err.error({:validation_failed, :email})
{:error, {:validation_failed, :email}}
@spec expect!(value(), Exception.t()) :: any()
Returns the wrapped value from an {:ok, value} tuple or raises the provided exception.
For two-element result tuples ({:ok, value}) it returns value. When the tuple contains
additional metadata, it returns the remaining elements as a list.
If the value is {:error, _}, nil, or any other value, raises the provided exception.
Examples
iex> Err.expect!({:ok, "config.json"}, RuntimeError.exception("config not found"))
"config.json"
iex> Err.expect!({:ok, :user, %{role: :admin}}, RuntimeError.exception("user not found"))
[:user, %{role: :admin}]
@spec expect_err!(value(), Exception.t()) :: any()
Returns the wrapped error from an {:error, reason} tuple or raises the provided exception.
For two-element error tuples ({:error, reason}) it returns reason. When the tuple contains
additional metadata, it returns the remaining elements as a list.
If the value is {:ok, _}, nil, or any other value, raises the provided exception.
Examples
iex> Err.expect_err!({:error, :timeout}, RuntimeError.exception("expected an error"))
:timeout
iex> Err.expect_err!({:error, 404, "Not Found"}, RuntimeError.exception("expected an error"))
[404, "Not Found"]
Flattens a nested result into a single layer.
If the outer result is {:ok, inner} and inner is also a result tuple,
returns the inner result. Otherwise returns the value unchanged.
Examples
iex> Err.flatten({:ok, {:ok, 1}})
{:ok, 1}
iex> Err.flatten({:ok, {:ok, 1, :meta}})
{:ok, 1, :meta}
iex> Err.flatten({:ok, {:error, :timeout}})
{:error, :timeout}
iex> Err.flatten({:error, :failed})
{:error, :failed}
iex> Err.flatten({:ok, "value"})
{:ok, "value"}
Returns the second value if the first one is successful/present.
For Result types ({:ok, value} or {:error, reason}), returns the second value if the first
is {:ok, _}, otherwise returns the first error unchanged.
For Option types (nil or any value), returns the second value if the first is not nil,
otherwise returns nil.
Examples
iex> Err.followed_by({:ok, 1}, {:ok, 2})
{:ok, 2}
iex> Err.followed_by({:ok, 1}, {:error, :boom})
{:error, :boom}
iex> Err.followed_by({:error, :timeout}, {:ok, 2})
{:error, :timeout}
iex> Err.followed_by("primary", "secondary")
"secondary"
iex> Err.followed_by(nil, "secondary")
nil
Normalizes a nullable value into a result.
Returns {:ok, value} for any non-nil value. Returns {:error, reason} when the value is
nil. Existing result tuples are returned unchanged.
This is useful for adapting APIs such as Repo.get/2 that return nil on absence into flows
that work naturally with with, map_err/2, and or_else/2.
Examples
iex> Err.from_nil("config.json", :not_found)
{:ok, "config.json"}
iex> Err.from_nil(nil, :not_found)
{:error, :not_found}
iex> Err.from_nil({:ok, 1}, :not_found)
{:ok, 1}
iex> Err.from_nil({:error, :timeout}, :not_found)
{:error, :timeout}
Checks if a value is an {:error, ...} result tuple.
Returns true for any tuple starting with :error, false otherwise.
Allowed in guard tests.
Examples
iex> Err.is_err({:error, :timeout})
true
iex> Err.is_err({:error, 404, "Not Found"})
true
iex> Err.is_err({:ok, 1})
false
iex> Err.is_err(nil)
false
iex> Err.is_err("error")
false
def my_function(result) when is_err(result)
Checks if a value is none (nil).
Returns true only for nil.
Allowed in guard tests.
Examples
iex> Err.is_none(nil)
true
iex> Err.is_none(1)
false
iex> Err.is_none({:ok, 1})
false
def my_function(value) when Err.is_none(value)
Checks if a value is an {:ok, ...} result tuple.
Returns true for any tuple starting with :ok, false otherwise.
Allowed in guard tests.
Examples
iex> Err.is_ok({:ok, 1})
true
iex> Err.is_ok({:ok, 1, 2})
true
iex> Err.is_ok({:error, :timeout})
false
iex> Err.is_ok(nil)
false
iex> Err.is_ok("value")
false
def my_function(result) when is_ok(result)
Checks if a value is "some" (not nil).
Returns true for any value except nil.
Allowed in guard tests.
Examples
iex> Err.is_some(1)
true
iex> Err.is_some("hello")
true
iex> Err.is_some({:ok, 1})
true
iex> Err.is_some(false)
true
iex> Err.is_some(nil)
false
def my_function(value) when is_some(value)
Transforms the success value inside an {:ok, value} tuple or some value by applying a function to it.
For Result types ({:ok, value} or {:error, reason}), applies the function to the value
if it's {:ok, _}, otherwise returns the error unchanged.
For Option types (nil or any value), applies the function to the value if it's not nil,
otherwise returns nil.
Examples
iex> Err.map({:ok, 5}, fn num -> num * 2 end)
{:ok, 10}
iex> Err.map({:ok, "hello"}, &String.upcase/1)
{:ok, "HELLO"}
iex> Err.map({:error, :timeout}, fn num -> num * 2 end)
{:error, :timeout}
iex> Err.map(nil, fn num -> num * 2 end)
nil
iex> Err.map("hello", &String.upcase/1)
"HELLO"
Transforms the error inside an {:error, reason} tuple by applying a function to it.
For Result types ({:ok, value} or {:error, reason}), applies the function to the error
if it's {:error, _}, otherwise returns the ok tuple unchanged.
Ignores nil and non-Result values, returning them unchanged.
Examples
iex> Err.map_err({:error, 404}, fn code -> "HTTP #{code}" end)
{:error, "HTTP 404"}
iex> Err.map_err({:ok, "success"}, fn reason -> "#{reason}_error" end)
{:ok, "success"}
iex> Err.map_err(nil, fn reason -> "#{reason}_error" end)
nil
iex> Err.map_err(404, fn reason -> "#{reason}_error" end)
404
Matches on success/presence and error/absence with explicit handlers.
Existing result tuples dispatch to :ok or :error. nil dispatches to :error. Any other
non-nil value dispatches to :ok.
Examples
iex> Err.match({:ok, 5}, ok: &(&1 * 2), error: fn _ -> 0 end)
10
iex> Err.match({:error, :timeout}, ok: & &1, error: &inspect/1)
":timeout"
iex> Err.match(nil, ok: & &1, error: fn _ -> :missing end)
:missing
iex> Err.match("value", ok: &String.upcase/1, error: fn _ -> :missing end)
"VALUE"
Wraps value in an {:ok, value} tuple.
Examples
iex> Err.ok(%{id: 1, email: "john@example.com"})
{:ok, %{email: "john@example.com", id: 1}}
iex> Err.ok({:ok, 100})
{:ok, {:ok, 100}}
Returns true when the value is an ok result and its payload satisfies predicate.
Returns false for non-ok values without calling predicate.
Examples
iex> Err.ok_and?({:ok, 10}, &(&1 > 5))
true
iex> Err.ok_and?({:ok, 3}, &(&1 > 5))
false
iex> Err.ok_and?({:error, :timeout}, &(&1 > 5))
false
Returns the first value if it is present/successful, otherwise returns the second value.
For Result types ({:ok, value} or {:error, reason}), returns the first value if it's {:ok, _},
otherwise returns the second value.
For Option types (nil or any value), returns the first value if it's not nil,
otherwise returns the second value.
Examples
iex> Err.or_else({:ok, "cache.db"}, {:ok, "disk.db"})
{:ok, "cache.db"}
iex> Err.or_else({:ok, "cache.db"}, {:error, :unavailable})
{:ok, "cache.db"}
iex> Err.or_else({:error, :cache_miss}, {:ok, "disk.db"})
{:ok, "disk.db"}
iex> Err.or_else({:error, :cache_miss}, {:error, :disk_full})
{:error, :disk_full}
iex> Err.or_else("primary", "backup")
"primary"
iex> Err.or_else(nil, "backup")
"backup"
Returns the first value if it is present/successful, otherwise calls the function to compute an alternative value.
For Result types ({:ok, value} or {:error, reason}), returns the first value if it's {:ok, _},
otherwise calls the function with the error reason.
For Option types (nil or any value), returns the first value if it's not nil,
otherwise calls the function.
This is the lazy version of or_else/2 - the function is only called when needed.
Examples
iex> Err.or_else_lazy({:ok, "cache.db"}, fn _ -> {:ok, "disk.db"} end)
{:ok, "cache.db"}
iex> Err.or_else_lazy({:error, :cache_miss}, fn _reason -> {:ok, "disk.db"} end)
{:ok, "disk.db"}
iex> Err.or_else_lazy({:error, :timeout}, fn reason -> {:error, "Fallback failed: #{reason}"} end)
{:error, "Fallback failed: timeout"}
iex> Err.or_else_lazy("primary", fn _ -> "backup" end)
"primary"
iex> Err.or_else_lazy(nil, fn _ -> "backup" end)
"backup"
Splits a list of results into ok values and error values.
Returns a tuple {ok_values, error_values} where:
ok_valuescontains all values from{:ok, value}tupleserror_valuescontains all values from{:error, reason}tuples
Any other value is ignored.
Examples
iex> Err.partition([{:ok, 1}, {:error, "a"}, {:ok, 2}])
{[1, 2], ["a"]}
iex> Err.partition([1, nil])
{[], []}
iex> Err.partition([{:ok, "x"}, {:ok, "y"}])
{["x", "y"], []}
iex> Err.partition([{:error, :timeout}, {:error, :crash}])
{[], [:timeout, :crash]}
iex> Err.partition([])
{[], []}
Replaces the value inside an {:ok, value} tuple with a new value.
If the result is {:ok, _}, returns {:ok, new_value}.
Otherwise returns the original value unchanged.
Examples
iex> Err.replace({:ok, "old"}, "new")
{:ok, "new"}
iex> Err.replace({:error, :timeout}, 999)
{:error, :timeout}
iex> Err.replace(nil, 999)
nil
iex> Err.replace(100, 999)
100
Replaces the error inside an {:error, reason} tuple with a new value.
If the result is {:error, _}, returns {:error, new_error}.
Otherwise returns the original value unchanged.
Examples
iex> Err.replace_err({:error, :timeout}, :network_error)
{:error, :network_error}
iex> Err.replace_err({:error, 404}, :not_found)
{:error, :not_found}
iex> Err.replace_err({:ok, 1}, :error)
{:ok, 1}
iex> Err.replace_err(nil, :error)
nil
Replaces the error inside an {:error, reason} tuple by calling a function.
If the result is {:error, _}, calls the function and returns {:error, result}.
Otherwise returns the original value unchanged without calling the function.
This is the lazy version of replace_err/2 - the function is only called when needed.
Examples
iex> Err.replace_err_lazy({:error, 404}, fn value -> "Status: #{value}" end)
{:error, "Status: 404"}
iex> Err.replace_err_lazy({:ok, 1}, fn _ -> :error end)
{:ok, 1}
iex> Err.replace_err_lazy(nil, fn _ -> :error end)
nil
Replaces the value inside an {:ok, value} tuple by calling a function.
If the result is {:ok, _}, calls the function and returns {:ok, result}.
Otherwise returns the original value unchanged without calling the function.
This is the lazy version of replace/2 - the function is only called when needed.
Examples
iex> Err.replace_lazy({:ok, 1}, fn value -> value + 1 end)
{:ok, 2}
iex> Err.replace_lazy({:error, :timeout}, fn value -> value + 1 end)
{:error, :timeout}
iex> Err.replace_lazy(nil, fn value -> value + 1 end)
nil
Returns true when the value is present and satisfies predicate.
Returns false for nil without calling predicate.
Examples
iex> Err.some_and?("hello", &(String.length(&1) > 3))
true
iex> Err.some_and?("hi", &(String.length(&1) > 3))
false
iex> Err.some_and?(nil, &(String.length(&1) > 3))
false
Calls fun with the success value and returns the original value unchanged.
This is useful for logging, tracing, or other side effects in a flow without changing the wrapped value.
Examples
iex> Err.tap({:ok, 5}, fn value -> send(self(), {:seen, value}) end)
{:ok, 5}
iex> Err.tap({:error, :timeout}, fn _ -> raise "should not run" end)
{:error, :timeout}
iex> Err.tap(nil, fn _ -> raise "should not run" end)
nil
Calls fun with the error value and returns the original value unchanged.
This is useful for logging, tracing, or metrics on error paths without changing the error.
Examples
iex> Err.tap_err({:error, :timeout}, fn reason -> send(self(), {:seen_error, reason}) end)
{:error, :timeout}
iex> Err.tap_err({:ok, 5}, fn _ -> raise "should not run" end)
{:ok, 5}
iex> Err.tap_err(nil, fn _ -> raise "should not run" end)
nil
@spec try_rescue((-> any()), (Exception.t() -> any())) :: result()
Executes fun and converts rescued exceptions into an error result.
Returns {:ok, value} when the function succeeds. If the function raises, returns
{:error, exception} by default or {:error, mapper.(exception)} when a mapper is provided.
This is useful at library boundaries where a raising API needs to be adapted into a result flow.
Examples
iex> Err.try_rescue(fn -> 100 + 23 end)
{:ok, 123}
iex> Err.try_rescue(fn -> raise "boom" end) |> Err.map_err(&Exception.message/1)
{:error, "boom"}
iex> Err.try_rescue(fn -> raise "boom" end, fn error -> %{kind: :runtime_error, message: Exception.message(error)} end)
{:error, %{kind: :runtime_error, message: "boom"}}
Returns the wrapped error or default when the result is ok or value is present.
For two-element error tuples ({:error, reason}) it returns reason. When the tuple contains
additional metadata, it returns the remaining elements as a list.
Accepts nil, any {:ok, value} or {:error, reason} tuple (with or without extra metadata),
and other terms.
Examples
iex> Err.unwrap_err_or({:error, :timeout}, :no_error)
:timeout
iex> Err.unwrap_err_or({:error, :boom, %{code: 500}}, :no_error)
[:boom, %{code: 500}]
iex> Err.unwrap_err_or({:ok, 1}, :no_error)
:no_error
iex> Err.unwrap_err_or(nil, :no_error)
:no_error
Returns the wrapped value or default when the result is error or value is empty.
For two-element result tuples ({:ok, value}) it returns value. When the tuple
contains additional metadata, it returns the remaining elements as a list.
Accepts nil, any {:ok, value} or {:error, reason} tuple (with or without extra metadata),
and other terms.
Examples
iex> Err.unwrap_or({:ok, "config.json"}, "default.json")
"config.json"
iex> Err.unwrap_or({:ok, :user, %{role: :admin}}, [])
[:user, %{role: :admin}]
iex> Err.unwrap_or({:error, :not_found}, "default.json")
"default.json"
iex> Err.unwrap_or(nil, "default.json")
"default.json"
Returns the wrapped value or computes it from default_fun when the result is an error or value
is empty.
For successful tuples ({:ok, value}) the unwrapped value is returned. When the tuple contains
extra data, the remaining elements are returned as a list. For error tuples the extracted value(s)
are passed to default_fun.
The function receives the extracted value(s): a single value for two-element tuples or a list for larger tuples.
This is the lazy version of unwrap_or/2 - the function is only called when needed.
Examples
iex> Err.unwrap_or_lazy({:ok, "config.json"}, fn _ -> "default.json" end)
"config.json"
iex> Err.unwrap_or_lazy({:ok, :admin, %{perms: [:read]}}, fn _ -> [] end)
[:admin, %{perms: [:read]}]
iex> Err.unwrap_or_lazy({:error, :enoent}, fn reason -> "Error: #{reason}" end)
"Error: enoent"
iex> Err.unwrap_or_lazy(nil, fn _ -> %{role: :guest} end)
%{role: :guest}
Extracts all success values from a list of results.
Returns a list containing all values, except {:error, _} tuples or nil.
Examples
iex> Err.values([{:ok, 1}, {:error, :timeout}, {:ok, 2}])
[1, 2]
iex> Err.values([{:ok, 1}, nil, 2])
[1, 2]
iex> Err.values([{:ok, "a"}, {:ok, "b"}])
["a", "b"]
iex> Err.values([{:error, :x}, {:error, :y}])
[]
iex> Err.values([1])
[1]
iex> Err.values([])
[]
Combines two successful/present values into a pair.
For Result types, returns the first error encountered. When both values are ok, their extracted
payloads are returned inside {:ok, {left, right}}.
For Option types, returns {left, right} when both values are present. If either side is nil,
returns nil.
Examples
iex> Err.zip({:ok, 1}, {:ok, 2})
{:ok, {1, 2}}
iex> Err.zip({:ok, :user, %{id: 1}}, {:ok, :admin})
{:ok, {[:user, %{id: 1}], :admin}}
iex> Err.zip({:error, :timeout}, {:ok, 2})
{:error, :timeout}
iex> Err.zip("left", "right")
{"left", "right"}
iex> Err.zip(nil, "right")
nil