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:
01_basic_di.py- Basic dependency injection02_error_handling.py- Typed error handling with pattern matching03_composition.py- Composing effects together
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 dependencyrun[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
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
446194e98d03193c779120ca6f296ba3935e898f720ce868efda0881cb411db9
|
|
| MD5 |
b206fae97274554cd052e11d28717cca
|
|
| BLAKE2b-256 |
02eb50ba98fa9dd8199ae9f5a421fad69ec5dc6b4a7db806207fe4c7dbba150f
|
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
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbeb18567c4f6ad2d1a717789809067bfed617ee2c84f938e35563c1988d0865
|
|
| MD5 |
3b60f52474bb60a264b01f4b4cb763e7
|
|
| BLAKE2b-256 |
890d3ff3d3ef9ecf87178836a6b0a2854a72d12078499e3fa1179ec0a66aa90c
|