Open poscat0x04 opened 5 years ago
Can you elaborate with an example? I'm not going to try and understand the Scala docs, and there's too much discussion in #59 to know what specifically you meant.
from typing import *
T = TypeVar("T", covariant=True)
V = TypeVar("V")
class List(Generic[T]):
def updated(self, v: V) -> List[V]: # V should be a super type of T
"""
Creates a new list with the first element updated
"""
x, *xs = self
return [v] + xs
The argument type cannot be T since it would then occur in the contravariant position.
(A little confusing since List
is a PEP 484 standard type that's invariant, but I guess Sequence
would work just as well, and I guess you come from a different culture.)
OK, so assuming int
is a subtype of float
(mypy treats it that way), If we had x: List[int]
, then x.updated(3.14)
would be a List[float]
, while if we had y: List[float]
then y.updated(42)
should not be treated as a List[int]
because it may still contain floats.
How common do you think this use case would be in real-world Python code? (I'm not interested in alternative realities where Python is a functional language. :-)
This feature appears from time to time in various contexts, see e.g. https://github.com/python/mypy/issues/3737. So I can see the value in it, but I don't think it is a priority ATM.
Yeah, this would be nice every once in a while. This might help with some list
methods, such as copy()
, but only together with F-bounded quantification. I don't understand the updated
example above. Maybe a more complete example with an implementation would make things clearer.
I agree that this is pretty low priority. We'd probably need to change typing.TypeVar
to allow specifying a lower bound.
I just hit this issue while building some custom data structure types on top of the popular pyrsistent library. I was creating custom immutable list and set types that are covariant in the element type. Without lower type bounds, I could not logically implement (at least) the following methods:
append
, prepend
, concat
add
, union
, intersect
reduce
To provide a better example (not using the invariant List
type) let's consider the following example using the built-in Mapping
which is covariant in the value type.
from typing import *
K = TypeVar("K")
V = TypeVar("V", covariant=True)
B = TypeVar("B")
class ImmutableMap(Mapping[K, V]):
def updated(self, key: K, value: B) -> "ImmutableMap[K, ?]":
""" Returns a new `ImmutableMap` where the `key` is associated with a new `value`."""
...
The question is: what is the return type of updated
? There are only a few possible scenarios:
B
is a sub-type of V
and thus the return type would be ImmutableMap[K, V]
.B
is a super-type of V
and thus the return type would be ImmutableMap[K, B]
.B
is some other type with no relationship to V
and thus the return the must fall back on ImmutableMap[K, Any]
.Since we are discussing typed collections here, scenario 3 is very rare and probably irrelevant.
Scenario 1 and 2 are both logical and (in my experience) fairly common. If we could declare B = TypeVar("B", lower_bound=A)
then it would be possible to support scenarios 1 and 2 in a single method signature without losing any type information.
Without lower bounds, we can only support scenario 1 by annotating value: V
which is misaligned with the method's logical semantics.
I understand this might stay low-priority. I just wanted to hopefully clear up some of the motivation and document a specific use case that hit this limitation.
I'm having a similar problem. I've written a dependency injector for python (I know the code is horrible) and I'm adding type hints to its code so that it passes mypy checks. I have a method with the signature like this:
def bind(self, types: Tuple[Type[T], ...], instance: S, name: Optional[str] = None, singleton: bool = False) -> None:
The idea being that types
is a tuple of types that instance
can be represented as (all(isinstance(instance, t) for t in types)
).
Is there any way this relationship between T
and S
can be defined?
Lower bounds are useful when implementing type safe methods of (classes that are covariant with some type) that take argument of some other type. For example, the method
updated
of scala'sSeq
reference: #59