Sorry to drag out the discussion more here, but I’ve been thinking about this PEP in connection with this recent idea (basically, a DSL for typing): Idea: Simpler and More Expressive Type Annotations and the newly accepted PEP on lazy imports. For both it would be great if the type parameter to a function call could be treated as special by the compiler. This would require specialized syntax and couldn’t just rely on __getitem__.
For the sake of this discussion, let’s assume this syntax (inspired by Rust):
def make_list[T](*args: T) -> list[T]: ...
lst = make_list::[int]()
Relevance to lazy imports
We could then write something like this
lazy from collections.abc import Sequence
list_of_sequences = make_list::[Sequence[int]]()
and the import can be skipped because we would have deferred evaluation for the type parameter.
Relevance to Imogen’s proposal
In Imogen’s proposal, tuple[int, str] can be written as (int, str) as long as it’s in a place where the compiler knows that only type annotations may appear. This would then allow
list_of_tuples = make_list::[(int, str)]()
which couldn’t be supported with the __getitem__ syntax because it can’t be treated differently by the compiler.
As a competitor to PEP 747
PEP 747 enables functions that take complex type expressions as runtime parameters. This PEP could fill the same role if the value of the type parameter could be made available in the function body at runtime. This would then allow writing function like this:
def checkcast[T](value: object) -> T:
typ = T.__value__ # (or something like that)
assert isinstance(value, typ)
return value
which can be called like this
an_int = checkcast::[int](maybe_an_int)
I think this wouldn’t cover all the use cases of PEP 747, but a large majority of them.