Skip to main content

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)

Source distribution for fn-result-py 0.2.2
File Size Uploaded
fn_result_py-0.2.2.tar.gz 11.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fn-result-py 0.2.2
File Interpreter ABI Platform
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 log

Release 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

Release history Release notifications | RSS feed

This release

0.2.2 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page