Likely we don't want to be able to take Sequence[T] as an argument either. My initial PR doesn't address this, and I'm currently working on other covariance/contravariance which needs the same type of recursion, so I think it makes sense to address that (more complicated) issue later.
I'm actually not sure whether we would reject List[T] as an argument or not.
I don't understand why this should be rejected or what to do in its place.
T=TypeVar('T', covariant=True)
classA(Generic[T]):
deffoo(self, x: T) ->None: ... # Can't use as argument type! Return type is okay.
There is nothing wrong with wanting to take a T argument in a method of a covariant generic class[T], it's only that the function type of foo shouldn't be covariant over its input variable.
As far as I can see, you'd either need a way to define new type variables that are bounded on another type variable or maybe the variance part of the type variable should only apply to the first type constructor it is used in.
I can see it, but it's a combination of Covariance && Inheritance && Changing Variance in the child. I suppose we'll have to wait until PEP 591 to allow this?
Mypy already supports final classes and methods, so there's no need to wait for PEP 591.
I think that it may be fine to allow that for final classes and methods, but I'd like to see some real use case first that would benefit from it. @ilevkivskyi What do you think about this?
I think there is a confusion between two things: and obvious bug, and a missing low-priority feature. The bug is that this code is clearly safe (because the variable S is not bound by class C):
fromtypingimportGeneric, TypeVarS=TypeVar('S', covariant=True)
T=TypeVar('T', covariant=True)
classC(Generic[T]):
defmeth(self, x: S) ->S: # mypy complains here about variance
...
The workaround is trivial, just define another type variable (without covariant=True) if S is used elsewhere. About the missing feature, I agree with
I'd like to see some real use case first that would benefit from it
here, Some is supposed to be an immutable type and it should be possible to make it covariant.
A bit closer to typical python, a hypothetical FrozenDict[K, V] type could be covariant over V and have a get method: def get(self, key: K, default: V) -> V. This is very similar to the unwrap_or in that it has an argument that flows directly into the output and isn't stored. In both cases it's a fallback for something null-like, but that's coincidental when it comes to the variance question.
Based on a cursory inspection, the Optional example seems reasonable, as long as Some is final and the _value attribute is final. The details of how to support the example without compromising type safety in other use cases would still require some careful thought.
Activity
A bit of enforcing of (co/contra)variance.
[2/2] Some enforcing of (co/contra)variance.
jhance commented on Oct 11, 2015
Likely we don't want to be able to take Sequence[T] as an argument either. My initial PR doesn't address this, and I'm currently working on other covariance/contravariance which needs the same type of recursion, so I think it makes sense to address that (more complicated) issue later.
I'm actually not sure whether we would reject List[T] as an argument or not.
[2/2] Some enforcing of (co/contra)variance.
JukkaL commented on Oct 12, 2015
I think that List[T] as an argument should be rejected. Consider this example:
CV = TypeVar('CV', covariant=True) IV = TypeVar('IV') class A(Generic[CV]): def get(self) -> CV: ... def f(self, a: List[CV]) -> None: ... class B(A[IV], Generic[IV]): def __init__(self, x: IV) -> None: self.x = x def get(self) -> IV: return self.x def f(self, a: List[IV]) -> None: self.x = a[0] b = B(1) a = b # type: A[object] a.f([object()]) b.get() # ouch, object(), but should be intMerge pull request #898 from jhance/cov-contra
JukkaL commented on Oct 12, 2015
Reopening because the List[T] case is still open.
Emerentius commented on Oct 9, 2019
I don't understand why this should be rejected or what to do in its place.
There is nothing wrong with wanting to take a T argument in a method of a covariant generic class[T], it's only that the function type of foo shouldn't be covariant over its input variable.
As far as I can see, you'd either need a way to define new type variables that are bounded on another type variable or maybe the variance part of the type variable should only apply to the first type constructor it is used in.
JukkaL commented on Oct 9, 2019
@Emerentius It can result in type unsafety if there is a subclass of A[T] that is invariant.
Emerentius commented on Oct 9, 2019
I can see it, but it's a combination of Covariance && Inheritance && Changing Variance in the child. I suppose we'll have to wait until PEP 591 to allow this?
JukkaL commented on Oct 10, 2019
Mypy already supports final classes and methods, so there's no need to wait for PEP 591.
I think that it may be fine to allow that for final classes and methods, but I'd like to see some real use case first that would benefit from it. @ilevkivskyi What do you think about this?
ilevkivskyi commented on Oct 10, 2019
I think there is a confusion between two things: and obvious bug, and a missing low-priority feature. The bug is that this code is clearly safe (because the variable S is not bound by class C):
The workaround is trivial, just define another type variable (without covariant=True) if S is used elsewhere. About the missing feature, I agree with
Emerentius commented on Oct 10, 2019
My usecase is recreating something akin to Rust's Result type.
For this minimal example I'm using the Option type which has the same problem with fewer distracting type variables:
here, Some is supposed to be an immutable type and it should be possible to make it covariant.
A bit closer to typical python, a hypothetical FrozenDict[K, V] type could be covariant over V and have a get method: def get(self, key: K, default: V) -> V. This is very similar to the unwrap_or in that it has an argument that flows directly into the output and isn't stored. In both cases it's a fallback for something null-like, but that's coincidental when it comes to the variance question.
JukkaL commented on Oct 11, 2019
Based on a cursory inspection, the Optional example seems reasonable, as long as Some is final and the _value attribute is final. The details of how to support the example without compromising type safety in other use cases would still require some careful thought.
sobolevn commented on Aug 16, 2020
@Emerentius this might be helpful to you: https://github.com/dry-python/returns/blob/master/returns/maybe.py
KotlinIsland commented on Sep 26, 2023
@JukkaL Given that the example in the OP is now correctly handled by mypy, could it be updated to a case that is not? e.g.