_TSource in Option should be covariant
Describe the bug _TSource type in Option should be covariant.
class A:
pass
class B(A):
pass
x: Option[A] = Some(B())
Now since the _TSource is invariant, pyright will complain that B is not the same as A, but actually Option type should be covariant. Is Some[Dog] a Some[Animal]? I think you'll agree that the answer is yes. Also Option type is not actually mutable, so there is no way to assign a Some[Dog] to a variable typed Some[Aninmal] and then make it become Some[Cat]
Additional context Add any other context about the problem here.
- OS Linux
- Expression version 4.2.4
- Python version 3.11
I didn't see the above failing the type check in either mypy or pyright but the following did fail:
from expression import Some, Option
class A:
pass
class B(A):
pass
def test_covariance() -> None:
x: Option[B] = Some(B())
y: Option[A] = x
This isn't so easy to fix because covariant types can't be passed as parameters to other functions.
It also looks good to use infer_variance, which eliminates the need to consider covariant.
ex:
from typing_extensions import TypeVar
_T = TypeVar("_T", infer_variance=True)
This parameter is supported by typing-extensions>=4.4.0 and is the default behavior in python>=3.12.
This looks really interesting. Will try. Look forward to when we can eventually be >= Python 3.12 😀
My understanding of infer_variance is that it only aids in not having to specify the variance of the TypeVar. See the related spec docs:
type checker should use inference to determine whether the type variable is invariant, covariant or contravariant
So I don't think it helps here. The problem still exists that covariant types can't be passed as parameters to other functions.
I locally updated typing-extensions to 4.8.0 and tried with infer_variance. My above code example still fails the type checker.
I've tried to make some progress on this one by making methods that takes _TSource static methods and use another type when generating e.g:
@staticmethod
def Some(value: _T1) -> Option[_T1]:
"""Create a Some option."""
return Option(some=value)
Then for some more tricky methods like default_value I return a union with the default arg:
def default_value(self, value: _T1) -> _TSource | _T1:
I'm just unsure if the rest is ok, or false negatives. E.g:
def default_with(self, getter: Callable[[], _TSource]) -> _TSource:
It type checks, but is it ok to use _TSource in an argument that is a callable that returns _TSource? I could do the same trick as with default_value but then there's all the other methods like map, bind, but they are probably ok since they return a differnet type in the callable?
I locally updated
typing-extensionsto4.8.0and tried withinfer_variance. My above code example still fails the type checker.
From what i understand, I don't think we can have input paramters in python type hints as covariant. i think they usually are contravariant while returns are covariant. The type hints don't seem to let you know this and just has a generic error.