Explicitly specialising a generic function call is often not necessary for a type checker since they can usually infer the types of the generic type parameters from the given arguments. Unlike functions, classes often need their type parameters explicitly written since their constructors are essentially functions that return generic objects but don’t take generic parameters in which the type parameters can be inferred from.
In the given example, there are no generic parameters for construct()
in which the type parameter can be inferred, so explicit specialisation is actually required here. But then it’s hard to write a meaningful implementation for some method if there are no generic arguments and you can’t access the type of the type parameters at runtime.
It’s usually a mistake to use a type parameter in a function signature if it only appears once, unless the type parameter is constrained, in which case you’re probably using the type parameter to help work around the fact that Python doesn’t have proper overloading. And if you’re doing overloading in Python, the ability to inspect the type parameter type at runtime is important.
I was just recently looking into how the the look of generic functions can be faked in Python in order to overload a method by return type. It can already be done nicely.
from typing import TypeVar, Protocol, cast
Y = TypeVar('Y', int, str)
class Foo:
class GenericOverload(Protocol[Y]):
def __call__(self) -> list[Y]: ...
def __call__(self) -> float:
return 1.5
def __getitem__(self, key: type[Y]) -> GenericOverload[Y]:
try:
v = {
int: self.y_int,
str: self.y_str,
}[key]
return cast("__class__.GenericOverload[Y]", cast(object, v))
except KeyError as e:
raise TypeError from e
def y_int(self) -> list[int]:
return [1, 2, 3]
def y_str(self) -> list[str]:
return ['a', 'b', 'c']
foo = Foo()
print(foo()) # 1.5
print(foo[int]()) # [1, 2, 3]
print(foo[str]()) # ['a', 'b', 'c']
I don’t mind the suggestion but I doubt it’s going to be used very much. It could be useful if you for some reason wanted to indicate that the concrete type parameter type for a particular function call is important and should not be changed, i.e., accidentally.