Skip to main content

Algebraic effects for Python - dependency injection and typed error handling inspired by Koka

Project description

Koka

Algebraic Effects for Python - A lightweight library for effect-based programming inspired by the Koka language.

Koka enables:

  • 🎯 Type-safe dependency injection using effect handlers
  • 🚨 Typed error handling with pattern matching (no exceptions!)
  • 🔗 Effect composition - effects can call other effects
  • 🎨 Functional style - immutable handlers, pure functions

Requires Python 3.13+ (uses modern type parameter syntax)

Quick Start

from koka import Dep, Err, Koka

# Define a dependency
class Database:
    def get_user(self, id: str):
        return {"id": id, "name": "Alice"}
# Define an error type
class UserNotFound(Exception): pass

# Write an effect that uses dependencies and can fail
def get_user_name(user_id: str):
    if not user_id:
        return (yield from Err(UserNotFound("Empty ID")))
    db = yield from Dep(Database)  # Request dependency
    user = db.get_user(user_id)
    return user["name"]

# Set up dependencies and run the effect
result = Koka().provide(Database()).run(get_user_name("123"))

# Pattern match is statically typed
match result:
    case UserNotFound() as e:
        print(f"Error: {e}")
    case name:
        print(f"User: {name}")  # Output: User: Alice

Installation

# Note: Not yet published to PyPI
# For now, install from source:
git clone https://github.com/HerringtonDarkholme/koka
cd koka
uv sync --all-extras

Core Concepts

Effects

An effect is a generator function that can yield from effect objects to request capabilities:

def my_effect():
    config = yield from Dep(Config)  # Request a dependency
    if config.invalid:
        return (yield from Err(ValidationError()))  # Signal an error
    return config.value

Effect Types

Dep[T] - Dependency injection effect

db = yield from Dep(Database)  # Request a Database instance

Err[E] - Error effect (alternative to exceptions)

return (yield from Err(NotFoundError("Resource not found")))

Effect Handlers

The Koka class provides and handles effects:

result = (
    Koka()
    .provide(Database())      # Provide a Database instance
    .provide(AuthService())   # Provide an AuthService instance
    .run(my_effect())        # Run the effect
)

Returns either the success value or an error, which you can pattern match:

match result:
    case ValidationError():
        print("Validation failed")
    case NotFoundError():
        print("Not found")
    case value:
        print(f"Success: {value}")

Examples

See the examples/ directory for complete examples:

Run examples:

uv run python examples/01_basic_di.py

Development Setup

This project uses uv for package management.

# Install dependencies
uv sync --all-extras

# Activate virtual environment
source .venv/bin/activate  # On Unix/macOS
# or
.venv\Scripts\activate  # On Windows

Development Commands

Linting

# Run linting checks
uv run ruff check .

# Auto-fix linting issues
uv run ruff check --fix .

Formatting

# Format code
uv run ruff format .

# Check formatting without making changes
uv run ruff format --check .

Type Checking

# Run type checking
uv run pyright

# Run type checking on specific files
uv run pyright src/koka/example.py

# Check types in watch mode
uv run pyright --watch

Testing

# Run tests
uv run pytest

# Run tests with coverage report
uv run pytest --cov=koka --cov-report=html

# Run specific test file
uv run pytest tests/test_example.py

Run All Checks

# Run all checks (linting, type checking, and tests)
uv run ruff check . && uv run pyright && uv run pytest

Why Koka?

The answer can be found in effect.ts homepage. To summarize, make as much type checked and compiler managed as possible.

Project Structure

koka/
├── src/
│   └── koka/           # Main package
│       └── __init__.py # Core effect system
├── tests/              # Test files
│   ├── test_koka.py    # Integration example
│   └── test_unit.py    # Unit tests
├── examples/           # Example scripts
│   ├── 01_basic_di.py
│   ├── 02_error_handling.py
│   └── 03_composition.py
├── pyproject.toml      # Project configuration
├── LICENSE
└── README.md

API Reference

Koka[K]

Effect handler runtime.

Methods:

  • provide[N](instance: N) -> Koka[Dep[N] | K] - Register a dependency
  • run[E, T](eff: Eff[K | E, T]) -> T | E - Execute an effect computation

Dep[T]

Dependency injection effect.

Usage: value = yield from Dep(MyClass)

Err[E: Exception]

Error effect for typed error handling.

Usage: return (yield from Err(MyError()))

Eff[K, R]

Type alias for effect computations: Generator[K, Never, R]

Inspiration & Credits

This project draws inspiration from several excellent projects in the algebraic effects and functional programming space:

🌟 Primary Inspirations

  • Koka Language - The pioneering research language for algebraic effect handlers by Daan Leijen. Koka's elegant design of effect types and handlers is the foundation for this library's approach to effects in Python.

  • Effect-TS - A powerful effect system for TypeScript that brings functional programming patterns to the JavaScript ecosystem. Effect-TS demonstrates how algebraic effects can be practical and ergonomic in mainstream languages.

💡 Related Projects

  • koka-ts - TypeScript implementation of Koka-style effect handlers, showing how these concepts can be adapted to languages with different type systems.

  • stateless - A Python library for building type-safe, composable state machines and effects. Demonstrates how generator-based effects can work elegantly in Python.

🙏 Acknowledgments

Special thanks to:

  • Daan Leijen for the groundbreaking research on algebraic effects in Koka
  • The Effect-TS team for showing how effects can be practical and developer-friendly
  • The maintainers of koka-ts and stateless for exploring effect systems in TypeScript and Python

This project stands on the shoulders of giants. While it's a toy implementation for learning and fun, it aims to bring some of the elegance of Koka-style effects to modern Python.

Contributing

This is a toy project for fun and learning! Feel free to:

  • Open issues with questions or ideas
  • Submit PRs with improvements
  • Fork and experiment

License

MIT

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

koka-0.0.1.tar.gz (6.4 kB view details)

Uploaded Source

Built Distribution

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

koka-0.0.1-py3-none-any.whl (7.0 kB view details)

Uploaded Python 3

File details

Details for the file koka-0.0.1.tar.gz.

File metadata

  • Download URL: koka-0.0.1.tar.gz
  • Upload date:
  • Size: 6.4 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.10

File hashes

Hashes for koka-0.0.1.tar.gz
Algorithm Hash digest
SHA256 446194e98d03193c779120ca6f296ba3935e898f720ce868efda0881cb411db9
MD5 b206fae97274554cd052e11d28717cca
BLAKE2b-256 02eb50ba98fa9dd8199ae9f5a421fad69ec5dc6b4a7db806207fe4c7dbba150f

See more details on using hashes here.

File details

Details for the file koka-0.0.1-py3-none-any.whl.

File metadata

  • Download URL: koka-0.0.1-py3-none-any.whl
  • Upload date:
  • Size: 7.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.6.10

File hashes

Hashes for koka-0.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 dbeb18567c4f6ad2d1a717789809067bfed617ee2c84f938e35563c1988d0865
MD5 3b60f52474bb60a264b01f4b4cb763e7
BLAKE2b-256 890d3ff3d3ef9ecf87178836a6b0a2854a72d12078499e3fa1179ec0a66aa90c

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