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.4.tar.gz (26.5 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.4-py3-none-any.whl (26.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: thyrse-0.1.4.tar.gz
  • Upload date:
  • Size: 26.5 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.4.tar.gz
Algorithm Hash digest
SHA256 397a1744b5f12b8a02f5f4d4bf5f9034d34df5a6d0b7dd84eb7fc35ac0373e5a
MD5 bd187c9203342ab2d7b33a2031f13e15
BLAKE2b-256 186cc8b8667dc222099b2e0919a98cebc859157031d262cb5a56aa291c71ce36

See more details on using hashes here.

Provenance

The following attestation bundles were made for thyrse-0.1.4.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.4-py3-none-any.whl.

File metadata

  • Download URL: thyrse-0.1.4-py3-none-any.whl
  • Upload date:
  • Size: 26.0 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.4-py3-none-any.whl
Algorithm Hash digest
SHA256 774861c594a12c7d43c3c11c0015ff5f81157676d35880a0d16b8ff38993bf71
MD5 20f7b0ceab59d633d56e8e9dff93ddbf
BLAKE2b-256 912d4452a0f5ada99dcb8f8b7d6a10fd5f20944f757aaea763cc8672b1d456f4

See more details on using hashes here.

Provenance

The following attestation bundles were made for thyrse-0.1.4-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