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.3.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.3-py3-none-any.whl (21.8 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: fp_ops-0.1.3.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.3.tar.gz
Algorithm Hash digest
SHA256 19c104efdbb3d6e579470f6452c26450d074ed704bdb96d79245a496974a3400
MD5 fe56a67149648c842fc776d0fa6fbcd8
BLAKE2b-256 2b34543e2666fae3b477b84781a46a73a4ae05025bbf913a017ad77f021996cd

See more details on using hashes here.

File details

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

File metadata

  • Download URL: fp_ops-0.1.3-py3-none-any.whl
  • Upload date:
  • Size: 21.8 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.3-py3-none-any.whl
Algorithm Hash digest
SHA256 0bf1d715e69cb2826813e0599269702a3a46bbc8be0b824b0884ee682b1d24de
MD5 be165554244656b9e90f1788df84cebe
BLAKE2b-256 275f65f215539fec0295e5896709bf36365e6e516236080f37a66b2c78869944

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