PEP 718: subscriptable functions

I’ll add two examples of use cases for this PEP that hopefully help with cases where type checker likely can’t be smarter.

When deserializing data it is pretty common to have function where return type is not possible to know fro m the code, but the writer may expect a certain type. For example,

data = pickle.loads("my_object.pkl")

What type is data? Currently it is Any as there’s no way to know the type. With this PEP it would be possible (optionally) to do,

data = pickle.loads[MyFoo]("my_object.pkl")

and to adjust the type definition of loads (simplified) from,

def loads(contents: str) -> Any:
  ...

to

def loads[T](contents: str) -> T:
  ...

Not specifying the type is still possible like before and will fallback to unsolved type var which is like Any we have today. For simple example it is possible to instead do,

x: MyFoo = pickles.loads("my_object.pkl")

but slight variations that add one more function call in between like,

def process_foo(x: MyFoo) -> int:
  ...

process_foo(pickle.loads("my_object.pkl"))

you can’t really do that trick without splitting code into multiple lines or leaving pickle loading not type checked.

Same idea applies to several other deserialization functions like json.loads where as writer you may expect certain type, but type checker can’t possibly know what it is.

A different kind of example is where right type for type variable is ambiguous.

def foo(x: Sequence[T] | T) -> list[T]:
  ...

foo(b"hello") # What is T here?

Since bytes is Sequence[int] should T be int and return type list[int] or T be bytes and return type list[bytes]? Anytime you have Unions/overloads and type variables it becomes possible to introduce ambiguous cases where type checker has multiple options which type T should be. Type checkers today use heuristics to pick type that try to prefer simplest value of T, but sometimes you want other choice to be picked and I don’t think there is any clear way to always know what user expects for T. This pep would make it easy to call foo[bytes](b"hello") or foo[int](b"hello") allowing writer to be clear what they expect.

3 Likes