Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

generic methods/generic self don't seem to play with parameterized generics #4395

Closed
bwo opened this issue Dec 19, 2017 · 3 comments
Closed

Comments

@bwo
Copy link
Contributor

bwo commented Dec 19, 2017

(There are a couple of other issues that I think might be the same thing as this question, but I'm not sure, because they're posed at a lower level—apologies if this is actually a duplicate. #3153, maybe?)

The generic methods and generic self docs show how write a class with methods that accept/return arguments of the most precise type, so things like this work as intended:

from typing import Generic, TypeVar

T = TypeVar('T', bound='Modular')


### this works! yay!
class Modular(object):
    modulus = 0  # type: int

    def __init__(self, i):
        # type: (int) -> None
        self.i = i % self.modulus

    def __add__(self, other):
        # type: (T, T) -> T
        return self.__class__((self.i + other.i) % self.modulus)


class Mod5(Modular):
    modulus = 5


class Mod6(Modular):
    modulus = 6


Mod5(3) + Mod6(3)  # statically "disallowed"

mypy won't let me add Mod5s to Mod6s, hooray. But what if the relevant class is itself generic? This doesn't work:

In = TypeVar('In', contravariant=True)
Out = TypeVar('Out', covariant=True)
A = TypeVar('A')
B = TypeVar('B')

F = TypeVar('F', bound='FunctionLike') 

class FunctionLike(Generic[In, Out]):
    def call(self, arg):
        # type: (In) -> Out
        raise NotImplementedError

    def compose(self, other):
        # type: (F[In, Out], F[Out, B]) -> F[In, B]  # can't index `F`, so this doesn't work
        raise NotImplementedError

The type of compose shouldn't be (FunctionLike[In, Out], FunctionLike[Out, B]) -> FunctionLike[In, B], because then any two instances of any two subclasses could be composed together. It can't be (F, F) -> F because then all the type parameters are the same.

Is there a way to express this?

@elazarg
Copy link
Contributor

elazarg commented Dec 19, 2017

Related: #2354

@ilevkivskyi
Copy link
Member

Many aspects of #2354 and #3153 can be fixed (and I actually wanted to do this soon).

But, your particular feature request is too tricky. This is what is known in other languages as higher kinds. A normal generic is something that expects a type (or several types) and "returns" a type. While in your case, FunctionLike.compose is something that itself expects a type constructor (F in your case) that takes and "returns" a type. If you are familiar wit Haskell, then normal generics (supported by mypy) are * -> *, * -> * -> * (two type variables), etc. You want something like (* -> *) -> * -> * (if I fix In and Out for simplicity). Supporting this is more tricky (especially the type inference), so don't expect this in the near future. Fortunately, in Python it is OK to write Any (or other type less precise than you would want).

@msullivan
Copy link
Collaborator

Closing this in favor of the other issues linked

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants