result-py
A functional programming library for Python.
Features
- Type-Safe Error Handling: Errors are values, not exceptions. Your type checker knows exactly what can go wrong.
- Railway-Oriented Programming: Chain operations elegantly with
.pipe()and handle both success and failure paths. - Rich Collection Operations:
map,filter,reduce,flat_map, and more—all within the Either context. - Modern Python: Built for Python 3.14+ with full type annotations and generics.
- Functional Composition: Build complex pipelines from simple, testable functions.
Installation
pip install fn-result-py
Or with Poetry:
poetry add fn-result-py
Quick Start
Basic Usage
from result_py import Either
# Create success and failure values
success = Either.right(42) # Right = success
failure = Either.left("Error") # Left = failure
# Chain operations - failures short-circuit automatically
result = (
Either.right(10)
.pipe(lambda x: x * 2) # 20
.pipe(lambda x: x + 5) # 25
)
print(result) # Either(_left=None, _right=25)
Error Handling Made Explicit
from result_py import Either
def divide(a: float, b: float) -> Either[str, float]:
if b == 0:
return Either.left("Division by zero!")
return Either.right(a / b)
def sqrt(x: float) -> Either[str, float]:
if x < 0:
return Either.left("Cannot take sqrt of negative number!")
return Either.right(x ** 0.5)
# Chain operations - first error stops the pipeline
result = (
Either.right(16.0)
.pipe(lambda x: divide(x, 2)) # Right(8.0)
.pipe(sqrt) # Right(2.83...)
)
# Handle both cases with match
message = result.match(
left=lambda err: f"Failed: {err}",
right=lambda val: f"Result: {val:.2f}"
)
print(message) # "Result: 2.83"
Working with Collections
from result_py import Either
# Transform collections within Either context
result = (
Either.right([1, 2, 3, 4, 5])
.filter(lambda x: x % 2 == 0) # [2, 4]
.map(lambda x: x * 10) # [20, 40]
.to_list()
)
print(result) # Either(_left=None, _right=[20, 40])
# Filter and transform in one step
result = (
Either.right([1, 2, 3, 4, 5])
.filter_map(lambda x: x * 2 if x > 2 else None)
.to_list()
)
print(result) # Either(_left=None, _right=[6, 8, 10])
Wrapping External Code
Use wrap_external to safely wrap functions that might throw exceptions:
from result_py import wrap_external
import json
# Wrap a function that might raise exceptions
safe_json_loads = wrap_external(json.loads, json.JSONDecodeError)
result = safe_json_loads('{"valid": "json"}')
print(result) # Either(_left=None, _right={'valid': 'json'})
result = safe_json_loads('not valid json')
print(result) # Either(_left=JSONDecodeError(...), _right=None)
Using the @throws Decorator
Declare which exceptions your function might throw, and have them automatically converted to Either.left:
from result_py import Either, throws
@throws(ValueError, KeyError)
def risky_operation(data: dict, key: str) -> Either[ValueError | KeyError, int]:
value = data[key] # Might raise KeyError
if value < 0:
raise ValueError("Value must be positive")
return Either.right(value * 2)
result = risky_operation({"x": 10}, "x")
print(result) # Either(_left=None, _right=20)
result = risky_operation({}, "x")
print(result) # Either(_left=KeyError('x'), _right=None)
API Reference
Creating Either Values
| Method | Description |
|---|---|
Either.right(value) |
Create a success value |
Either.left(error) |
Create a failure value |
Either.success(value) |
Alias for right |
Either.failure(error) |
Alias for left |
Transformations
| Method | Description |
|---|---|
.pipe(f) |
Apply function to right value, supports both T -> U and T -> Either[E, U] |
.and_then(f) |
Monadic bind: chain T -> Either[E2, U] functions (alias-like to pipe for Either-returning fns) |
.map(f) |
Apply function to each item in an iterable |
.map_left(f) |
Transform the left (error) value |
.filter(f) |
Filter items in an iterable |
.filter_map(f) |
Filter and map in one step (None values filtered out) |
.flat_map(f) |
Map and flatten nested iterables |
.flatten() |
Flatten nested iterables |
Tuple Unpacking Variants
| Method | Description |
|---|---|
.n_pipe(f) |
Unpack tuple and apply multi-argument function |
.n_map(f) |
Map with tuple unpacking (lazy generator) |
.n_filter_map(f) |
Filter-map with tuple unpacking (lazy generator) |
Aggregations
| Method | Description |
|---|---|
.reduce(f, initial) |
Reduce iterable to single value |
.map_reduce(f, initial) |
Map then reduce |
.to_list() |
Convert iterable to list |
.to_set() |
Convert iterable to set |
.to_counter() |
Count occurrences of items |
Combining & Matching
| Method | Description |
|---|---|
.zip(other) |
Combine two Eithers into tuple |
.then(other) |
Chain to next Either if current is Right |
.or_else(f) |
Recover from error with E -> Either[E2, T] function |
.match(left, right) |
Pattern match on Left/Right |
.unwrap_or(default) |
Get right value or default |
Properties
| Property | Description |
|---|---|
.is_right |
True if this is a Right value |
.is_left |
True if this is a Left value |
Utilities
| Method | Description |
|---|---|
.partition(f) |
Split iterable into two based on predicate |
.to_json() |
Convert right value to JSON string |
.write_json_out(path) |
Write Pydantic model to JSON file |
.ctx_pipe(f) |
Apply side-effect function, keep original value |
Release files for fn-result-py 0.2.2
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| fn_result_py-0.2.2.tar.gz | 11.8 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| fn_result_py-0.2.2-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 22.2 kB
Release files / fn_result_py-0.2.2.tar.gz
| Download URL | fn_result_py-0.2.2.tar.gz |
|---|---|
| Size | 11.8 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
080c88b8589a6b8c3b9cdc235573fc3c046ec7f5a8e64722db1230f17dc3df21
|
|
BLAKE2b-256 checksum How to use checksums |
ec6f74d50ed12f265d4b528da6d2cd41b9c7468c174e83359c862ebc4eb3c217
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.
Transparency logRelease files / fn_result_py-0.2.2-py3-none-any.whl
| Download URL | fn_result_py-0.2.2-py3-none-any.whl |
|---|---|
| Size | 10.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
662bffb9f9e60b17e451aa5becce1b63a62dc79c6087377889bdd0f3862c1b5a
|
|
BLAKE2b-256 checksum How to use checksums |
5528a6205140259acd0a366e9008db51bd4bb5f06e41fba800551308e64f8ab8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.7
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Feb 24, 2026.
Transparency log