PEP 718: subscriptable functions

I’d like to follow up on this part, because I don’t think the text in the current draft addresses the question sufficiently.

Consider this example from the typeshed:

class str:
    @staticmethod
    @overload
    def maketrans(x: dict[int, _T] | dict[str, _T] | dict[str | int, _T], /) -> dict[int, _T]: ...

    @staticmethod
    @overload
    def maketrans(x: str, y: str, /) -> dict[int, int]: ...

    @staticmethod
    @overload
    def maketrans(x: str, y: str, z: str, /) -> dict[int, int | None]: ...

The first overload is generic with a single typevar _T . The other two overloads are not generic. What would str.maketrans[int] mean, according to this PEP?

There’s a clear runtime behavior, since the overloads don’t actually “show up” at runtime. The definitions shadow each other, and so str.maketrans only actually refers to the last definition. (Which would presumably be the implementation in a non-stub file.)

But to a type-checker, str.maketrans actually refers to all of the overload definitions (plus any implementation) collectively. And the explicit specialization is only valid for the first overload.

So I think there’s only two possible interpretations:

  • Disallow explicit specialization for all overloaded functions, full stop.
  • Only allow the specialization if every overload is generic, and the specialization is consistent with each of them. (Arity matches, taking into account any typevar defaults; each specialized type satisfies any bounds/constraint of the respective typevars)
1 Like