Clojure-style classless map-based multimethods and dispatch system for Python
Project description
mapdispatch
A minimalist library to bring Clojure-style multimethods, method combinations, and tag-based polymorphism into Python — without using classes.
Features
- Single and multiple argument dispatch
- Hierarchical dispatch using
deriveandisa - Extensible method registration with
defmethod - Compositional hooks with
:before,:after, and:around - Class-free, functional programming style
Installation
Just clone or copy the mapdispatch/ folder into your project.
# if you still use `pip`:
pip install mapdispatch
# but we highly recommend to use `uv`:
uv add mapdispatch
# and after installation via uv you can run the examples by:
mapdispatch-zoo
Quickstart
Define animals as dictionaries
simba = {"name": "Simba", "type": "lion"}
dumbo = {"name": "Dumbo", "type": "elephant"}
Create a hierarchy
from mapdispatch import Hierarchy, MultiMethod
h = Hierarchy()
h.derive("lion", "big-cat")
h.derive("big-cat", "animal")
h.derive("elephant", "animal")
Define a multimethod
feed = MultiMethod(lambda a: a["type"], hierarchy=h)
Register methods
def lion_feed(a):
print(f"{a['name']} eats meat")
def elephant_feed(a):
print(f"{a['name']} eats bananas")
feed.defmethod("lion", lion_feed)
feed.defmethod("elephant", elephant_feed)
Use it
feed(simba) # Simba eats meat
feed(dumbo) # Dumbo eats bananas
Real Multiple Dispatch
You can dispatch on multiple arguments by making the dispatch function return a tuple:
interact = MultiMethod(lambda a, b: (a["type"], b["type"]), hierarchy=h)
def lion_vs_elephant(a, b):
print(f"{a['name']} chases {b['name']}")
interact.defmethod(("lion", "elephant"), lion_vs_elephant)
interact(simba, dumbo) # Simba chases Dumbo
Method Combinations
:before – run logic before the main method
def open_cage(a):
print(f"Opening cage for {a['name']}")
feed.defmethod("lion", open_cage, kind=":before")
:after – run logic after the main method
def cleanup(a):
print(f"Cleaning up after {a['name']}")
feed.defmethod("lion", cleanup, kind=":after")
:around – wrap the primary method
def log_feed(inner_fn, a):
print(f"[LOG] Start {a['name']}")
result = inner_fn(a)
print(f"[LOG] End {a['name']}")
return result
feed.defmethod("lion", log_feed, kind=":around")
Method Registration via Decorators
You can also define methods using decorators instead of manual registration.
Primary method:
@feed.defmethod("lion")
def lion_feed(a):
print(f"{a['name']} eats meat")
With hooks:
@feed.defmethod("lion", kind=":before")
def open_cage(a):
print(f"Opening cage for {a['name']}")
@feed.defmethod("lion", kind=":after")
def cleanup(a):
print(f"Cleaning cage after {a['name']}")
@feed.defmethod("lion", kind=":around")
def logger(inner_fn, a):
print("[LOG] Start")
result = inner_fn(a)
print("[LOG] End")
return result
this is fully equivalent to manually registering via feed.defmethod(...) and is great for organizeing logic clearly and idiomatically.
Why mapdispatch?
This library helps you:
- Write open, extensible, polymorphic code
- Avoid deep class hierarchies
- Dispatch based on dynamic rules
- Cleanly separate data from behavior
Inspired by:
Example Output
Opening cage for Simba
[LOG] Start Simba
Simba eats meat
[LOG] End Simba
Cleaning up after Simba
Coming Soon
- Decorator-based syntax (
@defmethod) - Predicate-based dispatch
- Package on PyPI
- Integration with dataclasses or Pydantic (optional)
Philosophy
“Data is just data. Behavior is just behavior. Dispatch is glue.”
– Not Rich Hickey, but close enough.
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 mapdispatch-0.1.1.tar.gz.
File metadata
- Download URL: mapdispatch-0.1.1.tar.gz
- Upload date:
- Size: 6.1 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
3e1bd92c680e6b7ead5e7a9c71bd871dcc01c8d9c7fab6774ce48ba1ad213dde
|
|
| MD5 |
b37556cddf1111761a6566620c764751
|
|
| BLAKE2b-256 |
02b75cec1ebaf05b025c3ef8f597c7ca1e64a83fbfde6758015fb8490e395dc1
|
File details
Details for the file mapdispatch-0.1.1-py3-none-any.whl.
File metadata
- Download URL: mapdispatch-0.1.1-py3-none-any.whl
- Upload date:
- Size: 6.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.6.4
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
af8a30238dd0993b00b5409ec499022722ef4811be7a7054c5651350d5f2bb79
|
|
| MD5 |
5382f0bc46900e10a5e21cd18438de72
|
|
| BLAKE2b-256 |
aee9fcb5ef521a91cfa67222ae16b8a8da62ef7c9d1e2f7f0dc77e251c4eed01
|