Skip to main content

A functional programming library for Python that brings type-safe error handling through the Result monad pattern

Project description

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

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

fn_result_py-0.2.2.tar.gz (11.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

fn_result_py-0.2.2-py3-none-any.whl (10.3 kB view details)

Uploaded Python 3

File details

Details for the file fn_result_py-0.2.2.tar.gz.

File metadata

  • Download URL: fn_result_py-0.2.2.tar.gz
  • Upload date:
  • Size: 11.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fn_result_py-0.2.2.tar.gz
Algorithm Hash digest
SHA256 080c88b8589a6b8c3b9cdc235573fc3c046ec7f5a8e64722db1230f17dc3df21
MD5 7c0282e58fcdb175cc21de6f08b0df95
BLAKE2b-256 ec6f74d50ed12f265d4b528da6d2cd41b9c7468c174e83359c862ebc4eb3c217

See more details on using hashes here.

Provenance

The following attestation bundles were made for fn_result_py-0.2.2.tar.gz:

Publisher: publish.yaml on KaiErikNiermann/result-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file fn_result_py-0.2.2-py3-none-any.whl.

File metadata

  • Download URL: fn_result_py-0.2.2-py3-none-any.whl
  • Upload date:
  • Size: 10.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for fn_result_py-0.2.2-py3-none-any.whl
Algorithm Hash digest
SHA256 662bffb9f9e60b17e451aa5becce1b63a62dc79c6087377889bdd0f3862c1b5a
MD5 2b77e35b136d1c7a0daf5d9d43df427e
BLAKE2b-256 5528a6205140259acd0a366e9008db51bd4bb5f06e41fba800551308e64f8ab8

See more details on using hashes here.

Provenance

The following attestation bundles were made for fn_result_py-0.2.2-py3-none-any.whl:

Publisher: publish.yaml on KaiErikNiermann/result-py

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page