Skip to main content

Fast and elegant functional programming toolkit for Python

Project description

Thyrse

License: CC0 1.0 Universal Python >= 3.8 PyPI

A fast and elegant functional programming toolkit for Python

Thyrse is a comprehensive toolkit for functional programming in Python. It provides lambda calculus combinators, higher-order functions, algebraic data types, lenses, and many other tools to help you write elegant, functional Python code.

Features

  • Pure Functional: Based on lambda calculus and functional programming principles
  • Combinators: Classic S, K, I combinators and more for function abstraction
  • Function Utilities: Composition, piping, currying, memoization, and more
  • Algebraic Data Types: Optional, Result, and other ADTs for safer code
  • Async Support: First-class support for async/await patterns
  • Type Hints: Full type annotation support for better IDE experience
  • Type Safe: Optional, Result, and Safe types for handling errors elegantly
  • Zero Dependencies: Pure Python implementation, no external dependencies
  • Pythonic: Leverages Python's language features naturally

Installation

pip install thyrse

Quick Start

Basic Combinators

from thyrse import I, K, S, curry, pipe, tap

# Identity combinator
assert I(42) == 42

# Constant combinator  
greet = K("Hello")
assert greet("World") == "Hello"

# Function currying and composition
add = curry(lambda x, y: x + y)
add5 = add(5)
assert add5(3) == 8

# Pipeline data processing
pipeline = pipe(
    [1, 2, 3],
    lambda x: map(lambda i: i * 2, x),
    list,
)
assert pipeline == [2, 4, 6]

# Debug output with tap
result = pipe(
    42,
    tap(print),           # prints 42
    lambda x: x + 1,      # returns 43
)

Working with Optional and Result

from thyrse import Some, Nothing, Ok, Err

# Optional type
opt_value = Some(5)
result = opt_value.map(lambda x: x * 2).map(lambda x: x + 1)
# Result: Some(11)

# Result type  
computation = Ok(10).map(lambda x: x / 2).map(lambda x: x - 1)
# Result: Ok(4.0)

# Chain operations safely
def safe_divide(x, y):
    return Ok(x / y) if y != 0 else Err("Division by zero")

result = (
    Ok(20)
    .flat_map(lambda x: safe_divide(x, 2))
    .flat_map(lambda x: safe_divide(x, 4))
)
# Result: Ok(2.5)

Function Composition

from thyrse import compose, curry

# Compose functions right-to-left
add_one = lambda x: x + 1
double = lambda x: x * 2
triple = lambda x: x * 3

# Result is: triple(double(add_one(x)))
f = compose(triple, double, add_one)
assert f(2) == 18  # triple(double(3)) = triple(6) = 18

# Or use pipe for left-to-right
from thyrse import pipe
assert pipe(2, add_one, double, triple) == 18

Predicates and Data Access

from thyrse import prop, get, get_in, has, is_type

data = {"user": {"name": "Alice", "age": 30}}

# Property access
age = get_in(data, ["user", "age"])
assert age == 30

# Type checking  
is_user = is_type(dict)
assert is_user(data) == True

# Property extraction
get_name = prop("name")
assert get_name({"name": "Bob", "age": 25}) == "Bob"

Lenses for Data Manipulation

from thyrse import lens, view, set_, over

# Create a lens for a nested property
user_age = lens().user.age

data = {"user": {"name": "Alice", "age": 30}}

# View through the lens
assert view(user_age, data) == 30

# Set a new value
new_data = set_(user_age, 31, data)
assert view(user_age, new_data) == 31

# Transform with a function
incremented = over(user_age, lambda x: x + 1, data)
assert view(user_age, incremented) == 31

Async Support

import asyncio
from thyrse import async_pipe, async_compose

async def fetch_user(user_id):
    # Simulated async operation
    await asyncio.sleep(0.1)
    return {"id": user_id, "name": "Alice"}

async def fetch_posts(user):
    # Simulated async operation
    await asyncio.sleep(0.1)
    return [{"title": "Post 1", "author_id": user["id"]}]

async def main():
    # Compose async functions
    get_user_posts = async_pipe(
        fetch_user,
        lambda user: fetch_posts(user),
    )
    
    posts = await get_user_posts(1)
    print(posts)  # [{"title": "Post 1", "author_id": 1}]

asyncio.run(main())

Core Modules

Combinators (thyrse.combinators)

Classic lambda calculus combinators for building higher-order abstractions:

  • I: Identity combinator I(x) = x
  • K: Constant combinator K(x)(y) = x
  • S: Substitution combinator S(f)(g)(x) = f(x)(g(x))
  • B: Composition combinator B(f)(g)(x) = f(g(x))
  • C: Flip combinator C(f)(x)(y) = f(y)(x)
  • W: Duplicate combinator W(f)(x) = f(x)(x)
  • Y: Fixed-point combinator (for recursion)
  • Omega: Self-replicating combinator

Function Utilities (thyrse.func)

High-order functions and functional programming utilities:

  • curry / uncurry: Currying and uncurrying functions
  • compose / pipe: Function composition (right-to-left/left-to-right)
  • tap: Side-effect debugging (pass-through)
  • peek: Like tap, but for inspection
  • always: Constant value factory
  • flip: Swap argument order
  • complement: Logical negation of predicates
  • retry: Function wrapper with retry logic
  • memoize: Caching function results
  • log: Logging wrapper
  • partition / groupby: Collection operations
  • async_compose / async_pipe: Async function composition
  • fork / branch: Parallel processing of functions

Predicates (thyrse.predicates)

Type checking and data access predicates:

  • prop: Property accessor
  • get / get_in: Safe property access
  • has: Check property existence
  • is_type: Type checking predicates
  • and_ / or_ / not_: Logical predicates
  • truthy / falsy: Truthiness checks

Algebraic Data Types (thyrse.adts)

Type-safe error handling with algebraic data types:

  • Optional / Some / Nothing: Optional values
  • Result / Ok / Err: Result type for error handling
  • AsyncResult / AsyncOk / AsyncErr: Async results
  • All support map(), flat_map(), and other functional operations

Lenses (thyrse.lens)

Composable getters/setters for nested data structures:

  • lens(): Create lenses for nested access
  • view(): Get value through lens
  • set_(): Set value through lens
  • over(): Transform value through lens
  • Works with any nested data structure

Lazy Evaluation (thyrse.lazy)

Deferred computation and memoization:

  • Lazy: Lazy computations with caching
  • AsyncLazy: Async lazy evaluation
  • thunk() / force(): Quick lazy evaluation

Control Flow (thyrse.declarative_flow, thyrse.inline_flow)

Declarative and inline control flow constructs:

  • iif: Inline if-then-else
  • attempt: Try-catch wrapper
  • match: Pattern matching
  • using: Resource management

Safe Evaluation (thyrse.safe)

Safe function wrapping and execution:

  • Safe: Safe function wrapper
  • safe: Decorator for safe function execution

API Documentation

For detailed API documentation, visit the GitHub repository.

Examples

Data Processing Pipeline

from thyrse import pipe, partition, groupby, prop, map

users = [
    {"name": "Alice", "age": 30, "active": True},
    {"name": "Bob", "age": 25, "active": False},
    {"name": "Carol", "age": 35, "active": True},
]

# Group active users by age and get their names
active_users = pipe(
    users,
    lambda us: filter(prop("active"), us),
    groupby(prop("age")),
    lambda groups: {age: [u["name"] for u in users] for age, users in groups.items()},
)

print(active_users)
# {30: ["Alice"], 35: ["Carol"]}

Error Handling

from thyrse import Ok, Err, Result

def parse_int(s):
    try:
        return Ok(int(s))
    except ValueError:
        return Err(f"Cannot parse '{s}' as integer")

def divide(x, y):
    if y == 0:
        return Err("Division by zero")
    return Ok(x / y)

result = (
    parse_int("20")
    .flat_map(lambda x: divide(x, 4))
    .map(lambda x: round(x, 2))
)

print(result)  # Ok(5.0)

Contributing

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

License

This project is licensed under the CC0 1.0 Universal License - See the LICENSE file for details.

CC0 1.0 Universal means this work is in the public domain. You can copy, modify, distribute and use the work, even for commercial purposes, all without asking permission.

Changelog

0.1.0 (2026-03-16)

  • Initial release
  • Core combinators (I, K, S, B, C, W, Y, Omega)
  • Function utilities (compose, pipe, curry, etc.)
  • Algebraic data types (Optional, Result)
  • Lenses for data manipulation
  • Async support
  • Lazy evaluation
  • Predicates and property access
  • Error handling with Result type

Support

For issues, questions, or suggestions, please open an issue on the GitHub repository.

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

thyrse-0.1.0.tar.gz (25.9 kB view details)

Uploaded Source

Built Distribution

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

thyrse-0.1.0-py3-none-any.whl (25.6 kB view details)

Uploaded Python 3

File details

Details for the file thyrse-0.1.0.tar.gz.

File metadata

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

File hashes

Hashes for thyrse-0.1.0.tar.gz
Algorithm Hash digest
SHA256 dccf3b49396c2c40d1c804fc1bd32135db333c526a67ec3d1f0bec3458676c9b
MD5 477ed416ba928374f9019ccda592ac52
BLAKE2b-256 2318593a3e515b523b02645413284880eb9c1094f4ac346848b35d825d0788a1

See more details on using hashes here.

Provenance

The following attestation bundles were made for thyrse-0.1.0.tar.gz:

Publisher: python-publish.yml on kagami-meika/thyrse

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

File details

Details for the file thyrse-0.1.0-py3-none-any.whl.

File metadata

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

File hashes

Hashes for thyrse-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 3b7f33851ac3a8be9f3e9e3ccf4173796bcdbd219fa8cc99b5a839853916b71a
MD5 3a2844bade9c9bac4d3441e42a39e991
BLAKE2b-256 fa996163843448a3d0268292ec128f8c6175afd631481aa667514eea72c22729

See more details on using hashes here.

Provenance

The following attestation bundles were made for thyrse-0.1.0-py3-none-any.whl:

Publisher: python-publish.yml on kagami-meika/thyrse

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