Last active
November 5, 2024 11:09
-
-
Save aarondewindt/1cb0af45f05c0da03bfbec6f050f5b58 to your computer and use it in GitHub Desktop.
Experimental Algebraic Data Types implementation in Python 3.10
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from dataclasses import dataclass | |
class ADTMeta(type): | |
def __new__(mcs, name, bases, namespace: dict): | |
adtc_class = super().__new__(mcs, name, bases, namespace) | |
if "__is_adt_variant__" in namespace: | |
if namespace["__is_adt_variant__"]: | |
return adtc_class | |
for member_name, member in namespace.items(): | |
if isinstance(member, type): | |
variant_class = dataclass(type(member_name, (adtc_class,), { | |
"__qualname__": f"{adtc_class.__qualname__}.{member_name}", | |
"__is_adt_variant__": True, | |
"__annotations__": member.__annotations__ | |
})) | |
setattr(adtc_class, member_name, variant_class) | |
annotations = namespace.pop("__annotations__", {}) | |
for variant_name, variant_annotation in annotations.items(): | |
variant_class = type(variant_name, (adtc_class,), { | |
"__qualname__": f"{adtc_class.__qualname__}.{variant_name}", | |
"__is_adt_variant__": True | |
}) | |
if (variant_annotation is Ellipsis) or (variant_annotation is None): | |
variant_class.__repr__ = lambda self: f"<{self.__class__.__qualname__}>" | |
else: | |
variant_class.__init__ = create_init(variant_class, variant_annotation) | |
variant_class.__repr__ = lambda self: f"<{self.__class__.__qualname__} {repr(self.value)}>" | |
variant_class.__match_args__ = ("value",) | |
setattr(adtc_class, variant_name, variant_class) | |
return adtc_class | |
def create_init(klass, annotation): | |
def __init__(self: klass, value: annotation) -> None: | |
self.value = value | |
return __init__ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from adt import ADTMeta | |
class Foo(metaclass=ADTMeta): | |
a: ... | |
b: tuple[int, str, int] | |
c: str | |
class d: | |
da: str | |
db: int | |
dc: tuple[float, float] | |
assert issubclass(Foo.a, Foo) | |
assert issubclass(Foo.b, Foo) | |
assert issubclass(Foo.c, Foo) | |
assert issubclass(Foo.d, Foo) | |
value = Foo.a() | |
value = Foo.b((1, "Hello", 1234)) | |
value = Foo.b((1, "World", 42)) | |
# value = Foo.c("Bar") | |
# value = Foo.d("Baz", 69, (1.2, 3.5)) | |
# value = Foo.d("positional", 69, (1.2, 3.5)) | |
match value: | |
case Foo.a(): | |
print("Matched Foo.a") | |
case Foo.b((val_int_1, val_str, 42)): | |
print(f"Matched Foo.b with {val_int_1} {val_str} and the answer of life, the universe and everything") | |
case Foo.b((val_int_1, val_str, val_int_2)): | |
print(f"Matched Foo.b with {val_int_1} {val_str} {val_int_2}") | |
case Foo.c(val_str): | |
print(f"Matched Foo.c with {val_str}") | |
case Foo.d("positional", val_int, (f1, f2)): | |
print(f"Matched d with positional pattern db={val_int} dc=({f1}, {f2})") | |
case Foo.d(da=val_str, db=val_int, dc=(f1, f2)): | |
print(f"Matched d with da={val_str} db={val_int} dc=({f1}, {f2})") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from adt import ADTMeta | |
# As simple Enum | |
class MouseButton(metaclass=ADTMeta): | |
Left: ... | |
Middle: ... | |
Right: ... | |
# As enum with parameters | |
class UserInput(metaclass=ADTMeta): | |
Nil: ... | |
KeyPress: str | |
KeyRelease: str | |
MouseClick: tuple[MouseButton, int, int] | |
def get_user_input() -> UserInput: | |
# You play around with this return value. | |
return UserInput.MouseClick((MouseButton.Middle, 42, 24)) | |
match get_user_input(): | |
case UserInput.Nil: | |
print("No input") | |
case UserInput.KeyPress(key): | |
print(f"Key {key} was pressed") | |
case UserInput.KeyRelease(key): | |
print(f"Key {key} was released") | |
case UserInput.MouseClick((MouseButton.Middle, x, y)): | |
print(f"The middle mouse button was clicked at ({x}, {y})") | |
case UserInput.MouseClick((button, x, y)): | |
print(f"Mouse clicked ({button}, {x}, {y})") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment