Algebraic Data Types via Class Decorators
Project description
Overview
This package provides a class decorator for defining
Algebraic Data Types (ADTs) as known from
Haskell (data
),
OCaml (type
), and
Rust (enum
).
Features
-
Simplicity. This package exports only a single definition: the
adt
class decorator:from adt import adt
-
Concision. Constructors are specified via class annotations, allowing for syntax comparable to Rust's
enum
s:@adt class Event: MouseClick: [int, int] KeyPress: {'key': str, 'modifiers': list[str]}
-
Pattern Matching. Python's pattern matching via
match
is fully supported (Python >= 3.10).event = Event.KeyPress(key='a', modifiers=['shift']) match event: case Event.MouseClick(x, y): print(f"Clicked at ({x}, {y}).") case Event.KeyPress(key, mods): print(f"Pressed key {key}.")
-
Support for both named and unnamed constructor fields. Constructors with
- named fields, like
KeyPress
, are specified as adict[str, type]
; - unnamed fields, like
MouseClick
, are specified as alist[type]
; - a single unnamed field, can also be specified as a
type
; - no fields, are specified as the empty list.
- named fields, like
-
Getters, Setters, and Instance-Checking. As an alternative to pattern matching, getter, setter, and instance-checking methods are derived, e.g.
if event.is_mouse_click(): print(f"Clicked at ({event._1}, {event._2}).") elif event.is_key_press(): print(f"Pressed key {event.key}.mods}.")
-
Constructors are derived as subclasses of the decorated type and are themselves decorated with
dataclass
. This derives lots of useful method implementations, e.g. structural equality, string-conversion, and ordering. -
Constructors are namespaced into the ADT class, but can also be additionally exported:
@adt(export=True) # <-- No more `Event.` prefixing required. class Event: MouseClick: [int, int] KeyPress: {'key': str, 'modifiers': list[str]} def print(self): match self: MouseClick(x, y): ... # <-- As promised, no `Event.MouseClick`! KeyPress(key, mods): ... # <-- As promised, no `Event.KeyPress`!
Translation
The code generated in the above example by the adt
decorator for the
Event
ADT behaves equivalent to the following code, with the
exception that the constructor classes are constructed anonymously, so
the global namespace is not even temporarily polluted unless
@adt(export=True)
is used.
from dataclasses import dataclass
class Event:
def __init__(self, *args, **kwargs):
raise TypeError(
"Tried to construct an ADT instead of one of it's constructors.")
def is_mouse_click(self) -> bool:
return isinstance(self, MouseClick)
def is_key_press(self) -> bool:
return isinstance(self, KeyPress)
@dataclass
class MouseClick(Expr):
_1: int
_2: int
@dataclass
class KeyPress(Expr):
key: str
modifiers: list[str]
Expr.MouseClick = MouseClick
Expr.KeyPress = KeyPress
if not export:
del MouseClick
del KeyPress
Related packages
The following compares this package to packages which aim to provide similar functionality:
-
algebraic-data-types
also describes ADTs via class decorators and annotations, but does not support pattern matching viamatch
, as it is aimed at older python versions. Also the package does not support named constructor parameters. -
algebraic-data-type
andUxADT
does not support a concise definition via decorators and does not support pattern matching viamatch
. -
choicetypes
implements similar functionality, but instead of having subclasses for the constructors, the__init__
-method of the main ADT-Class takes a named argument for each constructor variant, which is more verbose, error-prone and does not have a straightforward way to support named constructor arguments. -
match-variant
supports pattern matching viamatch
and realizes ADTs by inheriting from a base class calledVariant
that seems to process the annotations. It does not seem to support named constructor parameters and methods that check if the ADT is a certain constructor. -
py-foldadt
comes without any documentation and has unclear functionality. It defines various algebraic structures like semirings with unclear connection to ADTs.
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.