Most of the prior art to this tries to take too much from other languages (e.g. using match as you show here).
For example they create their own DSL for matching that either introduces the magic function for matching, which doesn’t completely support Python classes (e.g what if I want my tagged union to also have methods?).
I am not completely against the compactness of ML - however ML approaches usually imply either heavy customisation or support from the language itself.
So I view my implementation as a pragmatic approach to implementing fully-featured tagged unions today: - support introspection in PyCharm - typecheckable with mypy - you can subclass from them - clear semantics
It’s also super-lightweight!
On Fri, 22 May 2020 at 23:03, Paul Sokolovsky firstname.lastname@example.org wrote:
On Fri, 22 May 2020 21:01:03 +0100 Andrey Cizov email@example.com wrote:
Sorry I forgot to add the URL: https://pypi.org/project/tagged-dataclasses/
How does this compare with many other implementations spread over the interwebs?
As a quick comment, looks verbose comparing to ML ;-).
For comparison, I found an algebraic2.py in ~/my-python-hacks/, no dataclasses or particular IDEs involved:
from collections import namedtuple
# Ideal syntax: # #Type = ( # Int(i: int) | # Str(s: str) | # Plus(l: Type, r: Type) #)
def V(name, **args): return namedtuple(name, args.keys())
def __init__(self, *variants): self.variants = list(variants) def add(self, *variants): self.variants.extend(variants) def __instancecheck__(self, inst): return isinstance(inst, tuple(self.variants))
def match(val, typ): if isinstance(val, typ): # Have to return scalar instead of a tuple due to CPython # deficiency with := operator return val
Can be used as:
UnaryExpr = Union( Int := V("Int", i=int), Str := V("Str", s=str), )
# Recursive variants should be added after initial definition UnaryExpr.add( Inc := V("Inc", e=UnaryExpr), Dec := V("Dec", e=UnaryExpr), )
def eval(var: UnaryExpr): if i := match(var, Int): return i elif s := match(var, Str): return s elif e := match(var, Inc): return eval(e) + 1 elif e := match(var, Dec): return eval(e) - 1
expr = Dec(Int(123)) print(isinstance(expr, UnaryExpr)) print(eval(expr))
It's sad "UnaryExpr" (instead of e.g. BinaryExpr) because of a known deficiency of CPython:
$ python3.8 -c "( (a, b) := (1, 2) )" File "<string>", line 1 SyntaxError: cannot use assignment expressions with tuple
Btw, does anybody know a Python implementation which has this bug fixed?
On Fri, 22 May 2020 at 20:25, Andrey Cizov firstname.lastname@example.org wrote:
I have developed a library to introduce tagged unions to python that uses dataclasses to define disjoint members of the tagged union (by defining them as Optional fields). With some additional validation I make sure that only one of the fields is not None.
I find that it also fits well with the existing analysis tools and IDEs (e.g. PyCharm) and doesn’t require any additional work in order to be supported.
I would like to see some criticism and whether that could potentially be a good candidate for python standard library in the future.
-- Best regards, Paul mailto:email@example.com