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

Suggestion: Allow free type variables in type variable bounds that permeate beyond #1311

Open
schuelermine opened this issue Dec 15, 2022 · 1 comment
Labels
topic: feature Discussions about new features for Python's type annotations

Comments

@schuelermine
Copy link

schuelermine commented Dec 15, 2022

Suppose we have the following code:

T_co = TypeVar("T_co", covariant=True)

class CanProduce(Protocol[T_co]):
    def produce(self) -> T_co:
        ...

T_in = TypeVar("T_in")
U = TypeVar("U")

@dataclass
class Container(Generic[T_in]):
    value: T_in

    def produce_from_value(self: Container[CanProduce[U]]) -> U:
        return self.value.produce()

This code has an undesirable property: Since T_in is invariant, produce_from_value can only be called on instances of Container whose type parameter is exactly CanProduce[U], for some U:

class IntProducer(CanProduce[int]):
    def produce(self) -> int:
        return 42

c: Container[IntProducer] = Container(IntProducer())
c.produce_from_value()  # this produces a type error

It would be ideal if we could communicate that in this method (which need not be an instance method, it could be a discrete function also), the type parameter behaves as though it were covariant, where any subtype of CanProduce[U] is valid.

In fact, there exists a mechanism for doing this in other situations. Consider if we have the following non-generic protocol:

class SupportsIndex(Protocol):
    def __index__(self) -> int:
        ...

We can then make a method index_from_value that works with all subtypes of SupportsIndex:

SI = TypeVar("SI", bound=SupportsIndex)

@dataclass
class Container(Generic[T_in]):
    value: T_in
    
    def index_from_value(self: Container[SI]):
        return self.value.__index__

Now, the following is valid:

c: Container[int] = Container(27)
c.index_from_value()

Unfortunately, this method cannot be used with generic protocols, because the following is invalid:

U = TypeVar("U")
CPU = TypeVar("CPU", bound=CanProduce[U])  # this produces a type error

I suggest altering the restriction to allow this trick to work.

@schuelermine schuelermine added the topic: feature Discussions about new features for Python's type annotations label Dec 15, 2022
@smheidrich
Copy link

Related: #1226

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
topic: feature Discussions about new features for Python's type annotations
Projects
None yet
Development

No branches or pull requests

2 participants