From 27a56ebb5f60fd85080601144fb78e64dd587905 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 12:25:45 -0700 Subject: [PATCH 1/9] Rewrite Abstract to read more naturally --- peps/pep-0718.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index 370885c2b0e..cf06abf59f0 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -15,12 +15,11 @@ Post-History: `24-Jun-2023 `__, Abstract -------- -This PEP proposes making function objects subscriptable for typing purposes. Doing so -gives developers explicit control over the types produced by the type checker where -bi-directional inference (which allows for the types of parameters of anonymous -functions to be inferred) and other methods than specialisation are insufficient. It -also makes functions consistent with regular classes in their ability to be -subscripted. +This PEP proposes making function objects subscriptable so that developers can +explicitly supply type arguments to generic functions, as in ``func[int](...)``. +This allows explicit specialisation when type inference is insufficient. It also +makes generic functions consistent with generic classes in their support for +subscription. Motivation ---------- From c815977985966cb098fe99fec801bee655e0b2fd Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 12:35:48 -0700 Subject: [PATCH 2/9] Clarify type checker errors vs. runtime exceptions --- peps/pep-0718.rst | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index cf06abf59f0..2b033609750 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -24,6 +24,9 @@ subscription. Motivation ---------- +Comments marked ``Invalid`` in this section indicate errors reported by type +checkers. Runtime exceptions are labelled with their exception type. + Currently, classes allow passing type annotations for generic containers. This is especially useful in common constructors such as ``list``\, ``tuple`` and ``dict`` etc. @@ -49,8 +52,10 @@ code where we want to ensure that keys are ``str`` and values are ``int``: three="3" # Invalid: Literal["3"] is not of type int ) -In spite of the utility of this syntax, when trying to use it with a function, an error -is raised, as functions are not subscriptable. +At runtime this call succeeds and stores the string ``"3"`` in the dictionary. + +Attempting to use this syntax with a function raises ``TypeError`` at runtime, +as functions are not subscriptable. .. code-block:: python @@ -668,7 +673,7 @@ cannot be inferred from the arguments: acc.send(1.5) # OK Lambdas cannot declare type parameters, so they are never generic: subscripting a lambda -is a type error, although it succeeds at runtime. The compiler's existing +is a type checker error, although it succeeds at runtime. The compiler's existing ``SyntaxWarning`` for subscripting a lambda literal is kept. See `Deferred Ideas`_. Backwards Compatibility From 132479f88fdaa0578dae4791dbfa8c009aaab609 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 12:40:33 -0700 Subject: [PATCH 3/9] Explain inference for mixed argument types --- peps/pep-0718.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index 2b033609750..aa16dff9247 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -120,13 +120,21 @@ specialised usages: NameNumberDict = dict[str, int] NameNumberDict(one=1, two=2, three="3") # Invalid: Literal["3"] is not of type int -This is not currently possible for functions but if allowed we could easily -specialise operations in certain sections of the codebase: +A generic function can accept arguments of different types by inferring a common +supertype for its type parameter. For example, mypy accepts the following call +and infers ``object`` as its result type: .. code-block:: python def constrained_addition[T](a: T, b: T) -> T: ... + reveal_type(constrained_addition(1, "1")) # mypy: object + +Explicitly specialising the function would let us require ``int`` for both +arguments in a section of the codebase: + +.. code-block:: python + # where we work exclusively with ints int_addition = constrained_addition[int] int_addition(2, 4+8j) # Invalid: complex is not of type int From f06a942049c3096cf6ef2730bcc0bc2dd81db3d5 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 12:43:38 -0700 Subject: [PATCH 4/9] Clarify which examples use proposed behaviour --- peps/pep-0718.rst | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index aa16dff9247..54329d165d4 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -130,8 +130,8 @@ and infers ``object`` as its result type: reveal_type(constrained_addition(1, "1")) # mypy: object -Explicitly specialising the function would let us require ``int`` for both -arguments in a section of the codebase: +With the proposed subscription syntax, we could require ``int`` for both +arguments: .. code-block:: python @@ -171,6 +171,8 @@ situations similar to: def foo[T](x: list[T]) -> T: ... reveal_type(foo([])) # type checker cannot infer T (yet again) +With this proposal, ``T`` could be supplied explicitly: + .. code-block:: python reveal_type(foo[int]([])) # type is int @@ -188,7 +190,7 @@ they weren't already. Some of this can be emulated with generic classes, but onl extra boilerplate and not in every type checker, so syntactic sugar would be highly welcome. -Due to this, specialising the function and using it as a new factory is fine +The proposed syntax also allows a specialised function to be reused as a factory: .. code-block:: python @@ -258,6 +260,9 @@ If accepted, this syntax will reduce the necessity to learn about :pep:`747`'s Specification ------------- +Examples in this section illustrate the proposed behaviour unless explicitly +described as current behaviour. + In this PEP, "function objects" refers to ``types.FunctionType``\ , ``types.MethodType``\ , ``types.BuiltinFunctionType``\ , ``types.BuiltinMethodType`` and ``types.MethodWrapperType``\ . @@ -701,7 +706,8 @@ relying on the ``TypeError``. Runtime Performance ------------------- -Each use of ``func[T]`` allocates a ``types.FunctionGenericAlias`` object on the heap. +Under this proposal, each use of ``func[T]`` allocates a +``types.FunctionGenericAlias`` object on the heap. When the subscript is immediately called (``func[T](args)``), the alias is constructed, called, and then discarded — it is a transient allocation per call site. Class subscriptions such as ``list[int]()`` pay a similar cost, and are in fact slower than From 3bbaf96d7397822a416ddd29c4e14e51b5007a5c Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 13:22:26 -0700 Subject: [PATCH 5/9] Emphasize the definition of function objects --- peps/pep-0718.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index 54329d165d4..e1c55a9540e 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -263,9 +263,12 @@ Specification Examples in this section illustrate the proposed behaviour unless explicitly described as current behaviour. -In this PEP, "function objects" refers to ``types.FunctionType``\ , -``types.MethodType``\ , ``types.BuiltinFunctionType``\ , ``types.BuiltinMethodType`` -and ``types.MethodWrapperType``\ . +.. note:: + + In this PEP, "function objects" means instances of ``types.FunctionType``, + ``types.MethodType``, ``types.BuiltinFunctionType``, ``types.BuiltinMethodType`` + and ``types.MethodWrapperType``. Other callable objects, such as instances of + classes defining ``__call__``, are outside this definition. For ``MethodType`` you should be able to write: From 34c6cea1b648f1ccdf2d10a42f22492af9453200 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 13:25:07 -0700 Subject: [PATCH 6/9] Relate TypeVar ordering to generic classes --- peps/pep-0718.rst | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index e1c55a9540e..ca1f3fa94a1 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -364,10 +364,13 @@ parameter defaults. Subscripting a function that has no type parameters is an er For functions using old-style ``TypeVar``\ s, which have no declared type parameter list, the order is the order in which the type variables first appear in the parameter -annotations, and then in the return annotation. This makes that order part of a -function's interface: reordering the parameters of such a function changes the meaning -of a subscription, so stubs should prefer :pep:`695` syntax for functions intended to be -subscripted. +annotations, and then in the return annotation. This is the same first-appearance rule +used by generic classes with old-style type variables when their order is not +explicitly given by a ``Generic[...]`` or ``Protocol[...]`` base. + +This makes that order part of a function's interface: reordering the parameters of such +a function changes the meaning of a subscription, so stubs should prefer :pep:`695` +syntax for functions intended to be subscripted. .. code-block:: python From 6708a6e6e772a47e67e6710528d6bffd1e60c58e Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 13:28:39 -0700 Subject: [PATCH 7/9] Declare the type parameter in the make example --- peps/pep-0718.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index ca1f3fa94a1..ee6147dc319 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -468,7 +468,7 @@ instance of a callable class. This PEP accepts that risk: The remaining risk mostly comes from decorators annotated as returning a ``Callable`` whose type variables type checkers scope to the returned callable, for example -``def make(func) -> Callable[[T], T]`` returning an instance of a callable class. The +``def make[T](func) -> Callable[[T], T]`` returning an instance of a callable class. The decorated name then has a generic callable type and can be subscripted for the type checker, but raises ``TypeError`` at runtime. From 315fe0122010068f396b97c8fc10ceff51a14a19 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 13:34:27 -0700 Subject: [PATCH 8/9] Wrap the TypeVarTuple example comment --- peps/pep-0718.rst | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index ee6147dc319..49a2923864d 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -627,8 +627,11 @@ variables cannot be resolved unambiguously with the current syntax. .. code-block:: python - foo[int, str, bool, complex](Bar(), Baz()) # Invalid: cannot determine which parameters are passed to *T and *U. Explicitly parameterise the instances individually - spam[int, str, bool, complex](Bar()) # OK + foo[int, str, bool, complex](Bar(), Baz()) # Invalid: cannot determine which + # parameters are passed to *T and + # *U. Explicitly parameterise the + # instances individually. + spam[int, str, bool, complex](Bar()) # OK As for generic classes, a single ``TypeVarTuple`` may be bound using an unpacked tuple type: From 50a432101483ab76792ab515efad8a4b17173d89 Mon Sep 17 00:00:00 2001 From: Guido van Rossum Date: Wed, 21 Oct 2026 17:20:21 -0700 Subject: [PATCH 9/9] Add a mini-tutorial (co-written with ChatGPT) --- peps/pep-0718.rst | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/peps/pep-0718.rst b/peps/pep-0718.rst index 49a2923864d..dc2afe53947 100644 --- a/peps/pep-0718.rst +++ b/peps/pep-0718.rst @@ -759,6 +759,76 @@ outside the loop: Adding ``__getitem__`` to the function object types does not change how functions are called, so calls that do not use subscription are not expected to become slower. +How to Teach This +----------------- + +Prerequisites +^^^^^^^^^^^^^ + +The user needs to be familiar with generic classes and functions using the +:pep:`695` syntax, and with specializing a generic class: for example, +``Box[int]`` for a class declared as ``class Box[T]: ...``. + +Basics +^^^^^^ + +Take a generic function, for example: + +.. code-block:: python + + def func[T](a: T, b: T) -> T: + return a + +The new feature allows writing e.g. ``func[int]`` to specialize the function +with a particular type argument for ``T``. Type checkers substitute ``int`` for +``T`` when checking calls through this specialization. For example, +``func[int](1, 2)`` is valid, but ``func[int](1, "")`` is rejected by the type +checker. + +The rules for matching type arguments to type parameters follow those for +generic classes (see `Type checking subscriptions`_). Type checkers reject +specialization of functions that are not generic. + +A specialization can be saved and reused: + +.. code-block:: python + + int_func = func[int] + int_func(1, 2) # OK + int_func(1, "") # Type checker error + +Specialization affects type checking. It does not enforce types at runtime or +make the supplied type arguments available inside the function. + +When inference is used +^^^^^^^^^^^^^^^^^^^^^^ + +Explicit specialization binds the function's own type parameters, using +defaults for omitted arguments. Without specialization, normal type inference +applies. + +Caveats +^^^^^^^ + +Functions wrapped by ``functools.cache`` or ``functools.lru_cache``, and +objects created by ``functools.partial(...)``, do not support specialization. +See `Callable types`_ for details about decorators and other callable objects. + +When to use this, and when not +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Usually, type inference is sufficient. Explicit type arguments are useful when +inference does not express the intended types. + +It can be useful for selecting an overload, specifying element types for empty +containers, or inferring parameter types for lambda arguments to a generic +function. + +Another use is checking arguments against a chosen type rather than allowing +inference to select a broader one. For example, ``func[int](x, y)`` requires +arguments assignable to ``int``; mypy instead infers ``T = object`` for +``func(1, "")``. + Reference Implementation ------------------------ @@ -865,9 +935,9 @@ for some motivating examples. Change History -------------- -* 24-Apr-2026: Added examples, rationale and binding rules. * 21-Oct-2026: Addressed discussion feedback, clarified runtime and typing rules, and added use cases, performance notes and rejected ideas. +* 24-Apr-2026: Added examples, rationale and binding rules. Copyright ---------