Result Pattern
A library designed to implement the "Errors as Values" pattern in Python 3.14+. It bridges the gap between pure functional safety and the pragmatic realities of the exception-heavy Python ecosystem.
Installation
Help
See documentation for more details.
Core Concept: Result (Sum Type)
A Result is a Sum Type representing a state of being either completely successful (Ok) or completely failed (Err).
1. Basic Usage
from result import Ok, Err, Result, is_ok
def divide(a: int, b: int) -> Result[float, str]:
if b == 0:
return Err("Cannot divide by zero")
return Ok(a / b)
res = divide(10, 2)
if is_ok(res):
# res is narrowed to Ok[float]
print(f"Success: {res.ok()}")
2. Modern Pattern Matching
Leverage Python 3.10+ native matching with optimized __match_args__:
3. Functional Chaining
Avoid nested if statements with linear pipelines:
Interoperability: The Catch System
Bridge the procedural world of exceptions to the functional world of Results.
1. @catch Decorator
Lift exception-throwing code into Result containers with zero boilerplate.
from result import catch
@catch(ValueError)
def parse_int(s: str) -> int:
return int(s)
parse_int("10") # Ok(10)
parse_int("abc") # Err(ValueError(...))
2. Exception Mapping (map_to & Enums)
Immediately sanitize wild exceptions into strictly typed Domain Enums.
from enum import StrEnum
from result import catch
class ErrorCode(StrEnum):
INVALID = "invalid_input"
# Single mapping
@catch(ValueError, map_to=ErrorCode.INVALID)
def risky_op(s: str): ...
# Multiple mapping dictionary
@catch({ValueError: ErrorCode.INVALID, KeyError: "missing"})
def complex_op(x): ...
3. catch_call Inline
Execute standard library functions inline without opening context blocks.
from result import catch_call
import json
res = catch_call(json.JSONDecodeError, json.loads, '{"key": "value"}')
4. catch Context Manager
Lasso an entire block of wild Python code and capture the result safely.
from result import catch
import json
with catch(json.JSONDecodeError) as safe_block:
data = json.loads(payload)
# Perform complex logic...
safe_block.set(data["nested"]["key"])
# result is narrowed to Result[T, JSONDecodeError]
print(safe_block.result)
Monadic Orchestration: Do-Notation
Write procedural-looking code that automatically handles short-circuiting logic.
from result import do_notation, Do
@do_notation
def compile_pipeline(source: str) -> Do[str, Exception]:
tokens = yield tokenize(source) # Returns list[Token] or short-circuits Err
ast = yield parse(tokens) # Returns AST or short-circuits Err
code = yield generate(ast)
return code # Automatically wrapped in Ok
Combinators: Advanced Utilities
| Utility | Description |
|---|---|
validate() |
Applicative: Accumulates all errors instead of failing fast. |
traverse() |
Maps a fallible function over an iterable, failing fast. |
flow() |
A sequential pipeline macro for piped data transformations. |
succeeds() |
Filters a collection of Results, returning only the success values. |
partition_exceptions() |
Splits a mixed list of [Value, Exception] into [Ok, Err]. |
Outcome: Product Type (Go/Odin Style)
While Result is mathematically pure, real-world systems often require fault tolerance. Outcome[T, E] is a Product Type that holds both a partial success value and diagnostic baggage simultaneously.
Advantages:
- Native Unpacking: Inherits from
NamedTupleforval, err = do_work()syntax. - Fault Tolerance: Retain your AST or data payload even if warnings/errors occurred.
- Odin Style: Use
.to_result()inside a@do_notationblock to simulate Odin'sor_return.
Usage:
from result import Outcome
def parse_with_diagnostics(source: str) -> Outcome[AST, list[str]]:
# build AST and accumulate errors...
return Outcome(ast, accumulated_errors)
# 1. Procedural Unpacking (Go/Odin style)
ast, errors = parse_with_diagnostics(src)
if errors:
print(f"Warnings: {errors}")
# 2. Accumulation
new_outcome = Outcome(node, "e1").push_err("e2").merge(other_outcome)
# 3. Transition to Strict (or_return)
@do_notation
def strict_flow():
# .to_result() halts execution if any errors exist
ast = yield parse_with_diagnostics(src).to_result()
return emit(ast)
API Philosophy
Zero-Escape Safety
Panics are isolated in the .unsafe namespace. Direct .unwrap() access is disabled at the root level to encourage safe functional patterns.
True Covariance
Full support for inheritance subtyping (e.g., Ok[Dog] is assignable to Result[Animal, Any]).