Skip to main content

an ergonomic wrapper around the expression library

Project description

FP-Ops: Functional Programming Operations for Python

PyPI version Python versions codecov License Code style: black Type check: mypy

FP-Ops is a functional programming library for Python that lets you convert you functions into composable operations.

Features

  • Composition as a First-class Citizen: Build complex pipelines using simple operators like >>, &, and |
  • Context Awareness: Pass context through operation chains with automatic validation
  • Async-First: Designed for asynchronous operations from the ground up
  • Type Safety: Comprehensive type hints for better IDE support and code safety
  • Functional Patterns: Implements common functional programming patterns like map, filter, and reduce

Installation

pip install fp-ops

Getting Started

Here's a simple example to get you started:

from fp_ops.operator import operation
import asyncio

# Define some operations
@operation
async def get_user(user_id: int) -> dict:
    # Simulate API call
    return {"id": user_id, "name": "John Doe", "age": 30}

@operation
async def format_user(user: dict) -> str:
    return f"User {user['name']} is {user['age']} years old"

# Compose operations
get_and_format = get_user >> format_user

get_and_format(1)

Key Concepts

Operations

The core concept in FP-Ops is the Operation class. An operation wraps an async function and provides methods for composition using operators:

  • >> (pipeline): Passes the result of one operation to the next
  • & (parallel): Executes operations in parallel and returns all results
  • | (alternative): Tries the first operation and falls back to the second if it fails

Placeholders

You can use the placeholder _ to specify where the result of a previous operation should be inserted:

from fp_ops.placeholder import _

# Define operations
@operation
async def double(x: int) -> int:
    return x * 2

@operation
async def add(x: int, y: int) -> int:
    return x + y

# These are equivalent:
pipeline1 = double >> (lambda x: add(x, 10))
pipeline2 = double >> add(_, 10)

Context Awareness

Operations can be context-aware, allowing you to pass contextual information through the pipeline:

from fp_ops.operator import operation
from fp_ops.context import BaseContext
from pydantic import BaseModel

class UserContext(BaseContext):
    auth_token: str
    user_id: int

@operation(context=True, context_type=UserContext)
async def get_user_data(context: UserContext) -> dict:
    return {"id": context.user_id, "name": "Jane Doe"}

# Initialize context
context = UserContext(auth_token="abc123", user_id=42)

# Execute with context
result = await get_user_data(context=context)

Advanced Usage

Error Handling

FP-Ops uses the Result type for robust error handling:

@operation
async def divide(a: int, b: int) -> int:
    if b == 0:
        raise ValueError("Division by zero")
    return a / b

# Handle errors with default values
safe_divide = divide.default_value(0)

# Or with custom error handling
safe_divide = divide.catch(lambda e: 0 if isinstance(e, ValueError) else -1)

Composition Functions

Besides operators, FP-Ops provides various composition functions:

from fp_ops.composition import sequence, pipe, parallel, fallback

# Run operations in sequence and collect all results
results = await sequence(op1, op2, op3)

# Complex pipelines with conditional logic
pipeline = pipe(
    op1,
    lambda x: op2 if x > 10 else op3,
    op4
)

# Run operations in parallel
combined = await parallel(op1, op2, op3)

# Try operations until one succeeds
result = await fallback(op1, op2, op3)

Higher-Order Flow Operations

FP-Ops provides utilities for creating higher-order operations:

from fp_ops.flow import branch, attempt, retry, wait, loop_until

# Conditional branching
conditional = branch(
    lambda x: x > 0,
    positive_op,
    negative_op
)

# Retry an operation
resilient_op = retry(flaky_operation, max_retries=3, delay=0.5)

# Loop until a condition is met
counter = loop_until(
    lambda x: x >= 10,
    lambda x: x + 1,
    max_iterations=20
)

API Reference

Core Classes

  • Operation: The main class representing a composable asynchronous operation
  • BaseContext: Base class for all operation contexts
  • Placeholder: Used to represent where a previous result should be inserted

Decorators

  • @operation: Convert a function to an Operation
  • @operation(context=True, context_type=MyContext): Create a context-aware operation

Operators

  • op1 >> op2: Pipeline composition
  • op1 & op2: Parallel execution
  • op1 | op2: Alternative execution

Methods

  • operation.map(func): Apply a transformation to the output
  • operation.filter(predicate): Filter the result using a predicate
  • operation.bind(binder): Bind to another operation
  • operation.catch(handler): Add error handling
  • operation.default_value(default): Provide a default value for errors
  • operation.retry(attempts, delay): Retry the operation
  • operation.tap(side_effect): Apply a side effect without changing the value

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

License

This project is licensed under the MIT License - see the LICENSE file for details.

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

fp_ops-0.1.2.tar.gz (20.7 kB view details)

Uploaded Source

Built Distribution

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

fp_ops-0.1.2-py3-none-any.whl (21.7 kB view details)

Uploaded Python 3

File details

Details for the file fp_ops-0.1.2.tar.gz.

File metadata

  • Download URL: fp_ops-0.1.2.tar.gz
  • Upload date:
  • Size: 20.7 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for fp_ops-0.1.2.tar.gz
Algorithm Hash digest
SHA256 226012140e71a38aad0a54097e37e2ed765f676f295648c4b476c770b5a9de25
MD5 5b1d5cd618a45c784f18ce3d0f51a802
BLAKE2b-256 683fa46cd6d5b0153191caca944ad15fedd0e69dd0ffe8d20c956af6ea8a1c26

See more details on using hashes here.

File details

Details for the file fp_ops-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: fp_ops-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 21.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.1.0 CPython/3.12.9

File hashes

Hashes for fp_ops-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 323934d42ec568db6405c07e1be149874698732c4c9489b5cb6ab678b3513b53
MD5 660c695c47221751e9c4ee26a84af730
BLAKE2b-256 927903a7290e00d3c3d14c49e131ac2b7bd0b839ea0a35247bfc41d28a2a3732

See more details on using hashes here.

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