Algebraic Data Types via Class Decorators
Project description
Overview
This package provides class-decorators for defining Algebraic Data Types (ADTs).
ADTs are mostly known from
Working with ADTs is most pleasant in the presence of pattern matching, so we recommend to use Python >= 3.10.
This package exports exactly two definitions:
- the
adt
decorator introduces the constructor classes in the namespace of the decorated class; and - the
adt_export
decorator introduces the constructor classes in the global namespace.
Related packages
There are a few packages which aim to provide similar functionality:
-
algebraic-data-types
also describes ADTs via class decorators and annotations, but is aimed at older python version and does not work with pattern matching. -
algebraic-data-type
does not support a concise definition via decorators.
Example
The following example defines the syntax tree for the
lambda calculus
as an ADT Expr
together with a __str__
-method, which pretty prints
the syntax tree.
from adt import adt
@adt
class Expr:
Var: str # Constructor with one unnamed argument
Abs: [str, 'Expr'] # Constructor with two unnamed arguments
App: {'e1': 'Expr', 'e2': 'Expr'} # Constructor with two named arguments
def __str__(self) -> str:
match self:
case Expr.Var(x): return x
case Expr.Abs(x, e): return f"(λ{x}. {e})"
case Expr.App(e1, e2): return f"({e1} {e2})"
id_expr = Expr.Abs("x", Expr.Var("x"))
assert str(id_expr) == '(λx. x)'
As we used adt
instead of adt_export
,
the constructors Var
, Abs
, and App
are defined in the namespace
of the base class Expr
, and hence have to be accessed as Expr.Var
, Expr.Abs
, Expr.App
.
The code generated by the adt
decorator in the above example is equivalent to:
from dataclasses import dataclass
class Expr:
def __init__(self, *args, **kwargs):
raise TypeError(
"Tried to construct an ADT instead of one of it's constructors.")
def __str__(self) -> str:
match self:
case Expr.Var(x): return x
case Expr.Abs(x, e): return f"(λ{x}. {e})"
case Expr.App(e1, e2): return f"({e1} {e2})"
def is_var(self) -> bool:
return isinstance(self, Var)
def is_abs(self) -> bool:
return isinstance(self, Abs)
def is_app(self) -> bool:
return isinstance(self, App)
@dataclass
class Var(Expr):
_1: str
@dataclass
class Abs(Expr):
_1: str
_2: 'Expr'
@dataclass
class App(Expr):
e1: str
e2: 'Expr'
# Those statements are not really executed, as the `Var`, `Abs` and
# `App` classes are created anonymously, so in the real implementation
# the global namespace is *not* even temporarily polluted.
Expr.Var = Var
Expr.Abs = Abs
Expr.App = App
del Var
del Abs
del App
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.