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 <> wrote:

On Fri, 22 May 2020 21:01:03 +0100
Andrey Cizov <> wrote:

> Sorry I forgot to add the URL:

How does this compare with many other implementations spread over the

As a quick comment, looks verbose comparing to ML ;-).

For comparison, I found an 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())

class Union:

    def __init__(self, *variants):
        self.variants = list(variants)

    def add(self, *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[0]

Can be used as:

UnaryExpr = Union(
    Int := V("Int", i=int),
    Str := V("Str", s=str),

# Recursive variants should be added after initial definition
    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))

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

> On Fri, 22 May 2020 at 20:25, Andrey Cizov <> 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,
Andrey Cizov