Multiple and Predicative Dispatch
Project description
Multiple and Predicative Dispatch
This package enables extensible and context-sensitive dispatch to different code implementations that depend both on the annotated type of arguments and on predicates that are fulfilled by arguments.
Specifically, these dispatch decisions are arranged in a manner different than
with blocks of if/elif or match/case statements, and also differently from
inheritance hierarchies that resolve to a narrowest descendant type containing
a given method.
This approach is often better than other paradigms both because of the clarity of implementation signatures and because of its flexible and simple extensibility.
A "dispatcher" is a namespace in which multiple callable names may live, and calling each one makes a runtime dispatch decision. These namespaces (classes, behind the scenes) can associate related functionality, and the collection of names and implementations in a namespace can all be imported by importing the one namespace object.
A default dispatcher named Dispatcher can be imported directly, but normally
a factory function will generate new ones. The advantage of having a namespace
object that maintains dispatchable implementations is that that object itself
is indefinitely extensible.
Usage Example
In the API example below, the namespace created is called nums (e.g. for
numeric functions with multiple implementations), but you can equally create
others called, e.g. events or datasets or customers. A full application
might utilize many namespace objects.
Within your application code that imports, e.g., the num namespace object,
you can add many new function names and/or implementations for the already
defined names.
from __future__ import annotations
from math import sqrt
from dispatch.dispatch import get_dispatcher
from primes import akw_primality, mr_primality, primes_16bit
nums = get_dispatcher("Numbers")
@nums
def is_prime(n: int & 0 < n < 2**16) -> bool:
"Check primes from pre-computed list"
return n in primes_16bit
@nums
def is_prime(n: 0 < n < 2**32) -> bool:
"Check prime factors for n < √2³²"
ceil = sqrt(n)
for prime in primes_16bit:
if prime > ceil:
return True
if n % prime == 0:
return False
return True
@nums(name="is_prime")
def miller_rabin(
n: int & n >= 2**32,
confidence: float = 0.999_999,
) -> bool:
"Use Miller-Rabin pseudo-primality test"
return mr_primality(n, confidence)
@nums(name="is_prime")
def agrawal_kayal_saxena(
n: int & n >= 2**32,
confidence: float & confidence == 1.0,
) -> bool:
"Use Agrawal-Kayal-Saxena deterministic primality test"
return aks_primality(n)
# Bind to the Gaussian prime function (which _has_ a type annotation)
nums(name="is_prime")(gaussian_prime)
@nums
def is_twin_prime(n: int):
"Check if n is part of a twin prime pair"
return nums.is_prime(n) and (nums.is_prime(n + 2) or nums.is_prime(n - 2))
print(nums) # -->
# Numbers with 2 function bound to 6 implementations (0 extra types)
nums.describe() # -->
# Numbers bound implementations:
# (0) is_prime
# n: int ∩ 0 < n < 2 ** 16
# (1) is_prime
# n: Any ∩ n < 2 ** 32
# (2) is_prime (re-bound 'miller_rabin')
# n: int ∩ n >= 2 ** 32
# confidence: float ∩ True
# (3) is_prime (re-bound 'agrawal_kayal_saxena')
# n: int ∩ n >= 2 ** 32
# confidence: float ∩ confidence == 1.0
# (0) is_twin_prime
# n: int ∩ True
nums.is_prime(64_489) # True by direct search
nums.is_prime(64_487) # False by direct search
nums.is_prime(262_147) # True by trial division
nums.is_prime(262_143) # False by trial division
nums.is_prime(4_294_967_311) # True by Miller-Rabin test
nums.is_prime(4_294_967_309) # False by Miller-Rabin test
nums.is_prime(4_294_967_311, confidence=1.0) # True by AKS test
nums.is_prime(4_294_967_309, confidence=1.0) # False by AKS test
nums.is_prime(-4 + 5j) # True by Gaussian prime test
nums.is_prime(+4 - 7j) # False by Gaussian prime test
nums.is_twin_prime(617) # True (smaller of two)
nums.is_twin_prime(619) # True (larger of two)
nums.is_twin_prime(621) # False (not a prime)
nums.is_twin_prime(631) # False (not a twin)
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 gnosis_dispatch-1.0.1.tar.gz.
File metadata
- Download URL: gnosis_dispatch-1.0.1.tar.gz
- Upload date:
- Size: 44.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
6fc486c16bfcd1b73987fa88c894af8de35aee988fb71b2e31f9ef0517d75ae6
|
|
| MD5 |
4e9037b999082349c9c975b29c1927ed
|
|
| BLAKE2b-256 |
4deaa8cbf106eb56f1e8dd0c462d89ff1ba0d9538b52a0825aa2febeffc5058b
|
File details
Details for the file gnosis_dispatch-1.0.1-py3-none-any.whl.
File metadata
- Download URL: gnosis_dispatch-1.0.1-py3-none-any.whl
- Upload date:
- Size: 9.2 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
655b54f06bbe1036f394b19bc1faf61a039856f3e8c32c4d1e4ca3ed808974d4
|
|
| MD5 |
1122607240a6dbc93c2b15db6b66c1dc
|
|
| BLAKE2b-256 |
8ad4034561c675f082c1a67849efb680b697ecc6f791d1698f36fb6022416808
|