Algebraic effects for Python — deep and shallow, stateful, composable, multi-shot handlers
Project description
aleff
Algebraic effects for Python — deep and shallow, stateful, composable, multi-shot handlers.
Features
- Deep handlers — effects propagate through nested function calls without annotation
- Shallow handlers — handle an effect once, then delegate re-installation to the handler function; enables state machines, shift0/reset0, and strategy changes between invocations
- Stateful handlers — handler functions can execute code after
resume, enabling patterns like transactions and reverse-mode AD - Multi-shot continuations —
resumecan be called multiple times in a single handler, enabling backtracking search, non-determinism, and other advanced patterns - Sync and async — both synchronous (
Handler) and asynchronous (AsyncHandler) handlers are supported, with transparent bridging between the two - Effect composition — handler functions can perform effects themselves, dispatched to enclosing handlers; enables layered architectures and modular effect stacking
- Effect annotation —
@effect(step1, step2)collects effect sets transitively from decorated functions - Introspection —
effects(fn)andunhandled_effects(fn, h)for querying and validating effect coverage - Typed — effect parameters and return types are checked by type checkers (pyright, ty)
- No macros, no code generation — pure Python library built on greenlet and a small CPython C extension
Requirements
- CPython >=3.12
- Tested versions:
- 3.12.13
- 3.13.12
- 3.14.3
- 3.14.3t (free-threaded)
- Tested versions:
- greenlet >= 3.3.2
- Linux / macOS / Windows
Installation
# uv
uv add aleff
# pip
pip install aleff
Quick start
from aleff import effect, Effect, Resume, create_handler
# Define effects
read: Effect[[], str] = effect("read")
write: Effect[[str], int] = effect("write")
# Write business logic using effects
def run():
s = read()
return write(s)
# Provide handler implementations
h = create_handler(read, write)
@h.on(read)
def _read(k: Resume[str, int]):
return k("file contents")
@h.on(write)
def _write(k: Resume[int, int], contents: str):
print(f"writing: {contents}")
return k(len(contents))
result = h(run)
print(result) # 13
Multi-shot example
from aleff import effect, Effect, Handler, Resume, create_handler
choose: Effect[[int], int] = effect("choose")
h: Handler[list[int]] = create_handler(choose)
@h.on(choose)
def _choose(k: Resume[int, list[int]], n: int):
# Resume once for each choice and collect all results
results: list[int] = []
for i in range(n):
results += k(i)
return results
def computation():
x = choose(3) # 0, 1, or 2
y = choose(2) # 0 or 1
return [x * 10 + y]
result = h(computation)
print(result) # [0, 1, 10, 11, 20, 21]
Effect composition
Handler functions can perform effects that are handled by enclosing handlers:
from aleff import effect, Effect, Resume, create_handler
log: Effect[[str], None] = effect("log")
parse: Effect[[str], int] = effect("parse")
# Outer handler: logging
h_log = create_handler(log)
@h_log.on(log)
def _log(k: Resume[None, int], msg: str):
print(f"[LOG] {msg}")
return k(None)
# Inner handler: parsing with logging
h_parse = create_handler(parse)
@h_parse.on(parse)
def _parse(k: Resume[int, int], s: str):
log(f"parsing: {s}") # handled by the outer handler
return k(int(s))
result = h_log(lambda: h_parse(lambda: parse("42") + 1))
# prints: [LOG] parsing: 42
print(result) # 43
How it works
Effects are declared as typed values and invoked like regular function calls. A Handler intercepts these calls via greenlet-based context switching:
- Business logic runs in a greenlet
- When an effect is invoked, control switches to the handler
- The handler processes the effect and calls
resume(value)to return a value - If
resumeis called multiple times, each call restores a snapshot of the continuation's frames (multi-shot) - If the handler returns without calling
resume, the computation is aborted (early exit)
Because handlers use greenlets (not exceptions), the control flow is:
- Transparent — no
yield,await, or special syntax in business logic - Stateful — code after
resumeruns after the rest of the computation completes, enabling reverse-order execution (useful for backpropagation, transactions, etc.)
Multi-shot continuations are implemented via a CPython C extension (aleff._multishot.v1._aleff) that snapshots and restores interpreter frame chains.
Package structure
| Package | Description |
|---|---|
aleff |
Default: re-exports aleff.multishot (multi-shot handlers) |
aleff.multishot |
Multi-shot handlers with frame snapshot/restore |
aleff.oneshot |
One-shot handlers (no C extension required) |
Examples
See examples/ for demonstrations:
- N-Queens — backtracking search via multi-shot continuations
- Amb / Logic puzzle — Scheme-style
amboperator and constraint solving (SICP Exercise 4.42) - Probability — exact discrete probability distributions via weighted multi-shot
- Dependency injection — swap DB/email/logging implementations
- Record/Replay — record effect results, replay without side effects
- Transactions — buffer writes, commit on success, rollback on failure
- Automatic differentiation — forward-mode (dual numbers) and reverse-mode (backpropagation) with the same math expressions
- Shallow state machine — mutable state (get/put) and traffic light controller via shallow handler re-installation
- shift/reset, shift0/reset0 — delimited continuations: deep = shift/reset, shallow = shift0/reset0, with generator example
API reference
| Function / Class | Description |
|---|---|
effect("name") |
Create a new Effect |
@effect(e1, e2, ...) |
Decorate a function to declare its effects |
create_handler(*effects, shallow=False) |
Create a synchronous handler |
create_async_handler(*effects, shallow=False) |
Create an asynchronous handler |
h.on(effect) |
Register a handler function (decorator) |
h(caller) |
Run caller with the handler active |
effects(fn) |
Get the declared effect set of a function |
unhandled_effects(fn, *handlers) |
Get effects not covered by the given handlers |
Effect[P, R] |
Effect protocol (parameters P, return type R) |
Handler[V] |
Sync handler protocol |
AsyncHandler[V] |
Async handler protocol |
Resume[R, V] |
Sync continuation (k(value) -> V) |
ResumeAsync[R, V] |
Async continuation (await k(value) -> V) |
Development
From source:
git clone https://github.com/hnmr293/aleff.git
cd aleff
uv sync
# Run tests
uv run pytest
# Run tests on all supported Python versions
./run_tests.sh
# Format
uv run ruff format
# Lint
uv run pyright
License
Apache-2.0
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
Built Distributions
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 aleff-0.2.0.tar.gz.
File metadata
- Download URL: aleff-0.2.0.tar.gz
- Upload date:
- Size: 60.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbf81fc5bc7f68db73eb76d711f9eab58d40dd831d6bf03723272ad81e6835be
|
|
| MD5 |
3d5300d7f93656b865ac25f26ee21aa0
|
|
| BLAKE2b-256 |
0b89766b234febd1a750036f4ffee0937976b85ecfc540d79386df7498714350
|
Provenance
The following attestation bundles were made for aleff-0.2.0.tar.gz:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0.tar.gz -
Subject digest:
cbf81fc5bc7f68db73eb76d711f9eab58d40dd831d6bf03723272ad81e6835be - Sigstore transparency entry: 1256797619
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp313-cp313-win_amd64.whl.
File metadata
- Download URL: aleff-0.2.0-cp313-cp313-win_amd64.whl
- Upload date:
- Size: 72.3 kB
- Tags: CPython 3.13, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
963bd1bdb17c66340be25e315a078d05382d3baca8d26a40d8df93e944fd1c0e
|
|
| MD5 |
fcc9da2005b8a7f9b7f98e7913f29afe
|
|
| BLAKE2b-256 |
bb50f7c9227eb5fef3f409be72f9e765cbc4fad7219917f419f743941922eba7
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp313-cp313-win_amd64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp313-cp313-win_amd64.whl -
Subject digest:
963bd1bdb17c66340be25e315a078d05382d3baca8d26a40d8df93e944fd1c0e - Sigstore transparency entry: 1256798028
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: aleff-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 97.7 kB
- Tags: CPython 3.13, manylinux: glibc 2.17+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cbde5e7f44db6122b7d682d8691e29cd3103e4fefa5cce7375adb056deb8f954
|
|
| MD5 |
532ea542e0ed7d8b38f38871d0140553
|
|
| BLAKE2b-256 |
ad40af8454b2bd9f6253abe6d35f746bba5cd3c43a1958e69dc6c03535decbd2
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
cbde5e7f44db6122b7d682d8691e29cd3103e4fefa5cce7375adb056deb8f954 - Sigstore transparency entry: 1256798508
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp313-cp313-macosx_11_0_arm64.whl.
File metadata
- Download URL: aleff-0.2.0-cp313-cp313-macosx_11_0_arm64.whl
- Upload date:
- Size: 74.9 kB
- Tags: CPython 3.13, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2a7d7fa3c231be3a67f51d243b2e99f231a40ba96dcc423b74941616d716f0ac
|
|
| MD5 |
4bc60825c3b62bb9b07070029ee33a06
|
|
| BLAKE2b-256 |
4c0c5a0c3637e9d0466dd2bd6dc7ca75da6bc92faf37fbf8fead0237eb590162
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp313-cp313-macosx_11_0_arm64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp313-cp313-macosx_11_0_arm64.whl -
Subject digest:
2a7d7fa3c231be3a67f51d243b2e99f231a40ba96dcc423b74941616d716f0ac - Sigstore transparency entry: 1256798851
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl.
File metadata
- Download URL: aleff-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl
- Upload date:
- Size: 74.6 kB
- Tags: CPython 3.13, macOS 10.13+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
efa6301f5bb843fecd751fa0fcee7412d64823dbb5aa90867f9bb09a26d0bc18
|
|
| MD5 |
e0c38186d90206262a313f6c02db9636
|
|
| BLAKE2b-256 |
7cc914b3dcd12f44236ab75e49f6ed5d69fb7409a8cf3c8942b149e288650456
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp313-cp313-macosx_10_13_x86_64.whl -
Subject digest:
efa6301f5bb843fecd751fa0fcee7412d64823dbb5aa90867f9bb09a26d0bc18 - Sigstore transparency entry: 1256797943
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp312-cp312-win_amd64.whl.
File metadata
- Download URL: aleff-0.2.0-cp312-cp312-win_amd64.whl
- Upload date:
- Size: 72.3 kB
- Tags: CPython 3.12, Windows x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1be35088fc68d1da22b69c52fdb333b2642e970f2228bdb08e3c2d19631da2cc
|
|
| MD5 |
1fbec8e89554530ea21f36ef309f6f65
|
|
| BLAKE2b-256 |
5a633d7101d443696cb3f0be1f147a89a6cf6596ce858cdcab59d36e229058c2
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp312-cp312-win_amd64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp312-cp312-win_amd64.whl -
Subject digest:
1be35088fc68d1da22b69c52fdb333b2642e970f2228bdb08e3c2d19631da2cc - Sigstore transparency entry: 1256798947
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl.
File metadata
- Download URL: aleff-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl
- Upload date:
- Size: 90.8 kB
- Tags: CPython 3.12, manylinux: glibc 2.17+ x86-64, manylinux: glibc 2.28+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
441ae704887748a6167a801d626a56ac73cad2eec831bfb080376419be84e2ef
|
|
| MD5 |
74ca2f5104f24ca6da85d15bd601a341
|
|
| BLAKE2b-256 |
82dbd07313515771cba88e5ecbd07b0fbf6c9cb0c41845773ee89fcd63662e7f
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_28_x86_64.whl -
Subject digest:
441ae704887748a6167a801d626a56ac73cad2eec831bfb080376419be84e2ef - Sigstore transparency entry: 1256798167
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp312-cp312-macosx_11_0_arm64.whl.
File metadata
- Download URL: aleff-0.2.0-cp312-cp312-macosx_11_0_arm64.whl
- Upload date:
- Size: 70.9 kB
- Tags: CPython 3.12, macOS 11.0+ ARM64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
ade9b865527f8908303fca1df3cb065aff54cace1e3a93402320d33d77a0711d
|
|
| MD5 |
7c69925f253581f81189dc627b0d27e2
|
|
| BLAKE2b-256 |
0ae6f34a21e3eec245b7ae0664dcda794b5146c66e673c2ff10449e3d0d64ae4
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp312-cp312-macosx_11_0_arm64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp312-cp312-macosx_11_0_arm64.whl -
Subject digest:
ade9b865527f8908303fca1df3cb065aff54cace1e3a93402320d33d77a0711d - Sigstore transparency entry: 1256799416
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type:
File details
Details for the file aleff-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl.
File metadata
- Download URL: aleff-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl
- Upload date:
- Size: 70.7 kB
- Tags: CPython 3.12, macOS 10.13+ x86-64
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5f153152f85e0106040ca7b193890306ab0db55e32b8cabc3c070af5a9375c7f
|
|
| MD5 |
74da23fe159eea820b515614ffc7e6ab
|
|
| BLAKE2b-256 |
2c4eb1f59e97aec57aeb6d86505c301359e49f86180da59d78701ab58d8f206e
|
Provenance
The following attestation bundles were made for aleff-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl:
Publisher:
publish.yml on hnmr293/aleff
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
aleff-0.2.0-cp312-cp312-macosx_10_13_x86_64.whl -
Subject digest:
5f153152f85e0106040ca7b193890306ab0db55e32b8cabc3c070af5a9375c7f - Sigstore transparency entry: 1256799045
- Sigstore integration time:
-
Permalink:
hnmr293/aleff@7ae74e758b32055842d85a2194cf05295a5685a5 -
Branch / Tag:
refs/heads/main - Owner: https://github.com/hnmr293
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@7ae74e758b32055842d85a2194cf05295a5685a5 -
Trigger Event:
push
-
Statement type: