Raxol.Core.ErrorHandling (Raxol v2.0.1)
View SourceFunctional error handling patterns for Raxol.
This module provides composable error handling utilities to replace try/catch blocks with more functional patterns. It implements Result and Option types along with safe execution functions.
Philosophy
Instead of using try/catch for error handling, we use:
Result types ({:ok, value} | {:error, reason})
- Safe execution wrappers
- Pipeline-friendly error handling
- Explicit error propagation
Examples
# Instead of try/catch
result = safe_call(fn -> risky_operation() end)
# Chain operations safely
with {:ok, data} <- fetch_data(),
{:ok, processed} <- process_data(data),
{:ok, result} <- save_result(processed) do
{:ok, result}
end
# Safe binary operations
safe_deserialize(binary_data)
Summary
Functions
Ensures cleanup is called regardless of success or failure.
FlatMaps over a Result type.
Maps over a Result type.
Safely calls a module function if it's exported.
Safely performs arithmetic with a fallback for nil values.
Safely executes multiple operations, collecting all results.
Safely executes a function and returns a Result type.
Safely executes a function with a fallback value on error.
Safely executes a function and returns error details with stacktrace for re-raising.
Safely executes a function with error logging.
Safely calls an optional callback on a module. Returns {:ok, nil} if the callback doesn't exist.
Safely deserializes Erlang terms from binary data.
Safely makes a GenServer call with proper error handling.
Safely reads and deserializes a file.
Executes operations until one fails.
Safely serializes a term to binary.
Safely writes a term to a file.
Unwraps a Result or returns a default value.
Unwraps a Result or calls a function to get default.
Ensures a cleanup function is called even if the main function fails.
Types
@type result(ok) :: {:ok, ok} | {:error, term()}
@type result(ok, error) :: {:ok, ok} | {:error, error}
Functions
@spec ensure_cleanup((-> any()), (-> any())) :: {:ok, any()} | {:error, Exception.t() | {:exit, term()} | {:throw, term()} | {atom(), term()}}
Ensures cleanup is called regardless of success or failure.
FlatMaps over a Result type.
Examples
{:ok, 5}
|> flat_map(fn x -> {:ok, x * 2} end)
# => {:ok, 10}
Maps over a Result type.
Examples
{:ok, 5}
|> map(fn x -> x * 2 end)
# => {:ok, 10}
Safely calls a module function if it's exported.
Examples
safe_apply(MyModule, :init, [])
Safely performs arithmetic with a fallback for nil values.
Examples
safe_arithmetic(fn x -> x + 10 end, nil, 0)
# => 10 (uses fallback 0, then adds 10)
Safely executes multiple operations, collecting all results.
Examples
safe_batch([
fn -> operation1() end,
fn -> operation2() end,
fn -> operation3() end
])
# => [{:ok, result1}, {:error, error2}, {:ok, result3}]
@spec safe_call((-> any())) :: {:ok, any()} | {:error, Exception.t() | {:exit, term()} | {:throw, term()} | {atom(), term()}}
Safely executes a function and returns a Result type.
Examples
iex> safe_call(fn -> 1 + 1 end)
{:ok, 2}
iex> safe_call(fn -> raise "oops" end)
{:error, %RuntimeError{message: "oops"}}
Safely executes a function with a fallback value on error.
Examples
iex> safe_call_with_default(fn -> raise "oops" end, 42)
42
Safely executes a function and returns error details with stacktrace for re-raising.
Examples
iex> safe_call_with_info(fn -> 42 end)
{:ok, 42}
iex> safe_call_with_info(fn -> raise "oops" end)
{:error, {:error, %RuntimeError{message: "oops"}, [...]}}
Safely executes a function with error logging.
Examples
safe_call_with_logging(fn -> process() end, "Processing failed")
@spec safe_callback(module(), atom(), list()) :: {:ok, any()} | {:error, Exception.t() | {:exit, term()} | {:throw, term()} | {atom(), term()}}
Safely calls an optional callback on a module. Returns {:ok, nil} if the callback doesn't exist.
Safely deserializes Erlang terms from binary data.
Examples
iex> binary = :erlang.term_to_binary({:ok, "data"})
iex> safe_deserialize(binary)
{:ok, {:ok, "data"}}
iex> safe_deserialize("invalid")
{:error, :invalid_binary}
@spec safe_genserver_call(GenServer.server(), any(), timeout()) :: result(any())
Safely makes a GenServer call with proper error handling.
Examples
safe_genserver_call(MyServer, :get_state)
Safely reads and deserializes a file.
Examples
safe_read_term("/path/to/file")
Executes operations until one fails.
Safely serializes a term to binary.
Safely writes a term to a file.
Unwraps a Result or returns a default value.
Examples
unwrap_or({:ok, 42}, 0) # => 42
unwrap_or({:error, _}, 0) # => 0
Unwraps a Result or calls a function to get default.
Examples
unwrap_or_else({:error, :not_found}, fn -> fetch_default() end)
Ensures a cleanup function is called even if the main function fails.
Examples
with_cleanup(
fn -> open_resource() end,
fn resource -> close_resource(resource) end
)