Skip to content

A Comparable type? #59

Closed
Closed
@gvanrossum

Description

@gvanrossum

I (Guido) wondered how I would compare two arguments whose type is a type variable:

def cmp(a: T, b: T) -> int:
    if a < b: return -1
    if a > b: return 1
    return 0

Jukka wrote:
"""
This is a long-standing issue, and we've discussed this before in some detail (e.g., https://github.com/JukkaL/typing/issues/9). However, it wasn't discussed in typehinting, as far as I can tell. Currently we need to use a cast to use < instead of ==, which is pretty ugly.

We could define a Comparable ABC and support that as an "upper bound" for a type variable (bounded polymorphism). About the simplest useful implementation would be like this:

class Comparable(metaclass=ABCMeta):
    @abstractmethod
    def __gt__(self, other: Any) -> bool: pass
    ... # __lt__ etc. as well

...

CT = TypeVar('CT', bound=Comparable) # This is different from TypeVar('CT', Comparable)!

def min(x: CT, y: CT) -> CT:
    if x < y:
        return x
    else:
        return y

f(1, 2) # ok, return type int
f('x', 'y') # ok, return type str
f('x', 1) # probably ok, return type Comparable, which is not optimal
f(int, int)  # error, types aren't comparable

However, this doesn't verify whether the types can be compared to each other, only that they can be compared to something (because of the Any argument type). This feature would be easy to add to mypy.

The way Java etc. do this is to support a fancier language feature called "F-bounded polymorphism" or "F-bounded quantification". With it we can say that int can be compared with only int (Comparable[int]), etc. For example:

class Comparable(Generic[T]):
    @abstractmethod
    def __gt__(self, other: T) -> bool: pass
    ... # __lt__ etc. as well

...


CT = TypeVar('CT', bound=Comparable['CT'])   # Any type that is comparable with itself

def min(x: CT, y: CT) -> CT:
    if x < y:
        return x
    else:
        return y

f(1, 2) # ok, return type int
f('x', 'y') # ok, return type str
f('x', 1) # error, since these are not comparable to each other
f(int, int)  # error, types aren't comparable at all

This would be more involved to add to mypy. It would probably be one or two days of work for a minimal implementation. I'm not even quite sure that the above would be sufficiently general (Comparable should be contravariant, I think, so that Comparable[object] is a subtype of Comparable[int] :-/ ).

Comparable is probably the only common use case for this complex feature.
"""

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions