Mypy: sum types, recursive types, algebraic datatypes

Created on 27 Jul 2013  Â·  13Comments  Â·  Source: python/mypy

I was excited by the prospect that mypy would bring algebraic data types to python...

"A general algebraic data type is a possibly recursive sum type of product types."

The tuple type is clearly a product type, and mypy has union types, but the tutorial makes them look like something other than ordinary sum types.

http://www.mypy-lang.org/tutorial.html#uniontypes

Are sum types and recursive types planned?

question

Most helpful comment

The lack of recursive types makes it necessary to hack around any kind of a tree type. For example, it isn't possible to describe the type of the common flatten function (which should be something like Tree[T] = Union[T, List[Tree[T]]] - both recursive and generic).

All 13 comments

Yeah, the planned union types are not really like ML-style sum types. They aren't like C unions either, which makes them a bit confusing.

However, we could have something like case classes in Scala. I'm not sure how to support pattern matching, though. A series of isinstance tests is always possible, but it's more limited.

Case classes (along with sealed abstract classes) in Scala work fine a sum types, though they're a little verbose. But yeah, pattern matching is an essential part of the package. Hmm.

Closing this as there hasn't been any activity in a long time.

It seems like NamedTuples can operate like case classes. Does anything about mypy's Union make it incapable of representing sum types? From the docstring it seems like it would be okay . . .

I see no way of representing recursively defined NamedTuples with python semantics, however, because anything like the following won't compile

Node = NamedTuple("Node", [("value", Node), ("left", Node)])

I wonder if anyone else has any thoughts now that some time has passed?

Yes, a python namedtuple is like a scala case class. But these are product types. A scala sum type is formed when several different case classes extend one sealed base class.

Right. So the mypy equivalent could be:

Blue = NamedTuple("Blue", [])
Green = NamedTuple("Green", [])

Cat = NamedTuple("Cat", [("name", str)])
Fish = NamedTuple("Fish",   [("name", str), ("color", Color)])
Squid = NamedTuple("Squid", [("name", str), ("age", int)])

Pet = Union[Cat,Fish,Squid]
Color = Union[Green,Blue]

def a_pet() -> Pet:   #passes 
    return Cat("neko")
def not_a_pet() -> Pet:  #fails
    return Blue()

I was suggesting that Union was powerful enough to represent sum types.

aside: hmmm, I wonder if it would be possible to have some kind of "pattern-match" exhaustion checks? It seems like the enforced check on Optional is a special case of this.

I just ran into the lack of recursive unions again. Ouch

I wanted to do:


State = Dict[str, Union[BinaryIO, 'State']]

The lack of recursive types makes it necessary to hack around any kind of a tree type. For example, it isn't possible to describe the type of the common flatten function (which should be something like Tree[T] = Union[T, List[Tree[T]]] - both recursive and generic).

Without product types, I have no way of demanding in my API that a function argument is a class that implements two different Mixins, e.g.

def check_sprinter_is_ready(sprinter: Product[PositionMixin, VelocityMixin]):
    pass

Mypy has product types: Tuple and NamedTuple.

On Thu, Apr 19, 2018, 9:15 AM blakehawkins notifications@github.com wrote:

Without product types, I have no way of demanding in my API that a
function argument is a class that implements two different Mixins, e.g.

def check_sprinter_is_ready(sprinter: Product[PositionMixin, VelocityMixin]):
pass

—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/python/mypy/issues/256#issuecomment-382774453, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAJNykREWHiGSAvL4HV8Ro3G5C7sENmSks5tqKocgaJpZM4A2fPB
.

@blakehawkins What you need is an Intersection. There is an old proposal about this, but from my feelings it will be not implemented this year. If those mixins are structural (i.e. you don't care about inheriting from them actually) then you can define them as protocols, then your problem is solved. But for nominal classes, sorry you will need to wait for intersection types (and better don't call them product types to not confuse people).

@ilevkivskyi yep that's exactly what's needed - thanks for the comments.

https://github.com/python/typing/issues/213

I don't use mypy, but happened to come across this thread. Maybe something like the following encoding would work? I'm not sure exactly how mypy's type checker works, I assume it operates on an AST level, so it could maybe handle this.

def Node():
    return NamedTuple("Node", [("value", Node), ("left", Node)])

Or maybe even

Node = lambda: NamedTuple("Node", [("value", Node), ("left", Node)])
Was this page helpful?
0 / 5 - 0 ratings