Err

Hex.pm Docs

Err is a tiny library that makes working with tagged {:ok, value} and {:error, reason} tagged tuples more ergonomic and expressive in Elixir.

It follows a simple design to permit using it in existing codebases without changing existing code:

Inspired by Rust's Result/Option and Gleam's result/option.

Features

Installation

Add err to your list of dependencies in mix.exs:

def deps do
[
{:err, "~> 0.2"}
]
end

Usage

Wrap values

iex> Err.ok(42)
{:ok, 42}
iex> Err.error(:timeout)
{:error, :timeout}

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"

Lazy unwrapping (function only called when needed)

iex> Err.unwrap_or_lazy({:error, :enoent}, fn reason -> "Error: #{reason}" end)
"Error: enoent"

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}

Flatten 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

Err.or_else_lazy({:error, :cache_miss}, fn _reason ->
{:ok, load_from_disk()}
end)

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
# handle ok
end

Check if result is error

def process(result) when Err.is_err(result) do
# handle error
end

Real-World Example

def fetch_user_profile(user_id) do
user_id
|> fetch_user()
|> Err.and_then(&load_profile/1)
|> Err.and_then(&enrich_with_stats/1)
|> Err.or_else_lazy(fn _error ->
{:ok, %{name: "Guest", stats: %{}}}
end)
end