From 29d526217cd103a5b5339e620dfffaf4f3939f8a Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sat, 12 Nov 2022 21:16:10 +0000 Subject: [PATCH 1/4] PEP 646: Add section on type substitution in generic aliases --- pep-0646.rst | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 186 insertions(+) diff --git a/pep-0646.rst b/pep-0646.rst index a289b7a7cf0..b4ecda99e45 100644 --- a/pep-0646.rst +++ b/pep-0646.rst @@ -722,6 +722,187 @@ Normal ``TypeVar`` instances can also be used in such aliases: # T bound to Any, Ts to an Tuple[Any, ...] Foo + +Substitution in Aliases +----------------------- + +In the previous section, we only discussed simple usage of generic aliases +in which the type arguments were just simple types. However, a number of +more exotic constructions are also possible. + + +Type Arguments can be Variadic +'''''''''''''''''''''''''''''' + +First, type arguments to generic aliases can be variadic. For example, a +``TypeVarTuple`` can be used as a type argument: + +:: + + Ts1 = TypeVar('Ts1') + Ts2 = TypeVar('Ts2') + + IntTuple = Tuple[int, *Ts1] + IntFloatTuple = IntTuple[float, *Ts2] # Valid + +Here, ``*Ts1`` in the ``IntTuple`` alias is bound to ``Tuple[float, *Ts2]``, +resulting in an alias ``IntFloatTuple`` equivalent to +``Tuple[int, float, *Ts2]``. + +Unpacked arbitrary-length tuples can also be used as type arguments, with +similar effects: + +:: + + IntFloatsTuple = IntTuple[*Tuple[float, ...]] # Valid + +Here, ``*Ts1`` is bound to ``*Tuple[float, ...]``, resulting in +``IntFloatsTuple`` being equivalent to ``Tuple[int, *Tuple[float, ...]]``: a tuple +consisting of an ``int`` then zero or more ``float``\s. + + +Variadic Arguments Require Variadic Aliases +''''''''''''''''''''''''''''''''''''''''''' + +Variadic type arguments can only be used with generic aliases that are +themselves variadic. For example: + +:: + + T = TypeVar('T') + + IntTuple = Tuple[int, T] + + IntTuple[str] # Valid + IntTuple[*Ts] # NOT valid + IntTuple[*Tuple[float, ...]] # NOT valid + +Here, ``IntTuple`` is a *non*-variadic generic alias that takes exactly one +type argument. Hence, it cannot accept ``*Ts`` or ``*Tuple[float, ...]`` as type +arguments, because they represent an arbitrary number of types. + + +Aliases with Both TypeVars and TypeVarTuples +'''''''''''''''''''''''''''''''''''''''''''' + +In `Aliases`_, we briefly mentioned that aliases can be generic in both +``TypeVar``\s and ``TypeVarTuple``\s: + +:: + + T = TypeVar('T') + Foo = Tuple[T, *Ts] + + Foo[str, int] # T bound to str, Ts to Tuple[int] + Foo[str, int, float] # T bound to str, Ts to Tuple[int, float] + +In accordance with `Multiple Type Variable Tuples: Not Allowed`_, at most one +``TypeVarTuple`` may appear in the type parameters to an alias. However, a +``TypeVarTuple`` can be combined with an arbitrary number of ``TypeVar``\s, +both before and after: + +:: + + T1 = TypeVar('T1') + T2 = TypeVar('T2') + T3 = TypeVar('T3') + + Tuple[*Ts, T1, T2] # Valid + Tuple[T1, T2, *Ts] # Valid + Tuple[T1, *Ts, T2, T3] # Valid + +In order to substitute these type variables with supplied type arguments, +any type variables at the beginning or end of the type parameter list first +consume type arguments, and then any remaining type arguments are bound +to the ``TypeVarTuple``: + +:: + + Shrubbery = Tuple[*Ts, T1, T2] + + Shrubbery[str, bool] # T2=bool, T1=str, Ts=Tuple[()] + Shrubbery[str, bool, float] # T2=float, T1=bool, Ts=Tuple[str] + Shrubbery[str, bool, float, int] # T2=int, T1=float, Ts=Tuple[str, bool] + + Ptang = Tuple[T1, *T2, T2, T3] + + Ptang[str, bool, float] # T1=str, T3=float, T2=bool, Ts=Tuple[()] + Ptang[str, bool, float, int] # T1=str, T3=int, T2=float, Ts=Tuple[bool] + +Note that the minimum number of type arguments in such cases is set by +the number of ``TypeVar``\s: + +:: + + Shrubbery[int] # Not valid; Shrubbery needs at least two type arguments + + +Splitting Arbitrary-Length Tuples +''''''''''''''''''''''''''''''''' + +A final complication occurs when an unpacked arbitrary-length tuple is used +as a type argument to an alias consisting of both ``TypeVar``\s and a +``TypeVarTuple``: + +:: + + Elderberries = Tuple[*Ts, T1] + Hamster = Elderberries[*Tuple[int, ...]] # valid + +In such cases, the arbitrary-length tuple is split between the ``TypeVar``\s +and the ``TypeVarTuple``. We assume the arbitrary-length tuple contains +at least as many items as there are ``TypeVar``\s, such that individual +instances of the inner type - here ``int`` - are bound to any ``TypeVar``\s +present. The 'rest' of the arbitrary-length tuple - here ``*Tuple[int, ...]``, +since a tuple of arbitrary length minus two items is still arbitrary-length - +is bound to the ``TypeVarTuple``. + +Here, therefore, ``Hamster`` is equivalent to ``Tuple[*Tuple[int, ...], int]``: +a tuple consisting of zero or more ``int``\s, then a final ``int``. + +Of course, such splitting only occurs if necessary. For example, if we instead +did: + +:: + + Elderberries[*Tuple[int, ...], str] + +Then splitting would not occur; ``T1`` would be bound to ``str``, and +``Ts`` to ``*Tuple[int, ...]``. + +In particularly awkward cases, a ``TypeVarTuple`` may consume both a type +*and* a part of an arbitrary-length tuple type: + +:: + + Elderberries[str, *Tuple[int, ...]] + +Here, ``T1`` is bound to ``int``, and ``Ts`` is bound to +``Tuple[str, *Tuple[int, ...]]``. This expression is therefore equivalent to +``Tuple[str, *Tuple[int, ...], int]``: a tuple consisting of a ``str``, then +zero or more ``int``\s, ending with an ``int``. + + +TypeVarTuples Cannot be Split +''''''''''''''''''''''''''''' + +Finally, although any arbitrary-length tuples in the type argument list can be +split between the type variables and the type variable tuple, the same is not +true of ``TypeVarTuple``\s in the argument list: + +:: + + Ts1 = TypeVarTuple('Ts1') + Ts2 = TypeVarTuple('Ts2') + + Camelot = Tuple[T, *Ts1] + Camelot[*Ts2] # NOT valid + +This is not possible because, unlike in the case of an unpacked arbitrary-length +tuple, there is no way to 'peer inside' the ``TypeVarTuple`` to see what its +individual types are. + + Overloads for Accessing Individual Types ---------------------------------------- @@ -1459,6 +1640,8 @@ Expanding on these ideas, **Mark Mendoza** and **Vincent Siles** gave a presenta 'Variadic Type Variables for Decorators and Tensors' [#variadic-type-variables]_ at the 2019 Python Typing Summit. +Discussion over how type substitution in generic aliases should behave took place in [#cpython91162]_. + References ========== @@ -1502,6 +1685,9 @@ References .. [#dan-endorsement] https://mail.python.org/archives/list/python-dev@python.org/message/HTCARTYYCHETAMHB6OVRNR5EW5T2CP4J/ +.. [#cpython91162] CPython issue #91162: + https://github.com/python/cpython/issues/91162 + Copyright ========= From 3f8d906234334b0fccf45644554d16bc95274753 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 13 Nov 2022 16:40:31 +0000 Subject: [PATCH 2/4] Link directly to cpython#91162 Co-authored-by: C.A.M. Gerlach --- pep-0646.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pep-0646.rst b/pep-0646.rst index b4ecda99e45..05034e42db7 100644 --- a/pep-0646.rst +++ b/pep-0646.rst @@ -1640,7 +1640,8 @@ Expanding on these ideas, **Mark Mendoza** and **Vincent Siles** gave a presenta 'Variadic Type Variables for Decorators and Tensors' [#variadic-type-variables]_ at the 2019 Python Typing Summit. -Discussion over how type substitution in generic aliases should behave took place in [#cpython91162]_. +Discussion over how type substitution in generic aliases should behave +took place in `cpython#91162`_. References From 04d20f431511dc6b842f283f8459428279a303e6 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 13 Nov 2022 16:40:56 +0000 Subject: [PATCH 3/4] Update cpython#91162 reference Co-authored-by: C.A.M. Gerlach --- pep-0646.rst | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pep-0646.rst b/pep-0646.rst index 05034e42db7..3fd81d4aaeb 100644 --- a/pep-0646.rst +++ b/pep-0646.rst @@ -1686,8 +1686,7 @@ References .. [#dan-endorsement] https://mail.python.org/archives/list/python-dev@python.org/message/HTCARTYYCHETAMHB6OVRNR5EW5T2CP4J/ -.. [#cpython91162] CPython issue #91162: - https://github.com/python/cpython/issues/91162 +.. _cpython#91162: https://github.com/python/cpython/issues/91162 Copyright ========= From 0c1e177bbb28adb774c632d4752327dc38f52317 Mon Sep 17 00:00:00 2001 From: Matthew Rahtz Date: Sun, 13 Nov 2022 16:41:13 +0000 Subject: [PATCH 4/4] =?UTF-8?q?Fix=20typo:=20T2=20=E2=86=92=20Ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: C.A.M. Gerlach --- pep-0646.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pep-0646.rst b/pep-0646.rst index 3fd81d4aaeb..20770d1b65c 100644 --- a/pep-0646.rst +++ b/pep-0646.rst @@ -824,7 +824,7 @@ to the ``TypeVarTuple``: Shrubbery[str, bool, float] # T2=float, T1=bool, Ts=Tuple[str] Shrubbery[str, bool, float, int] # T2=int, T1=float, Ts=Tuple[str, bool] - Ptang = Tuple[T1, *T2, T2, T3] + Ptang = Tuple[T1, *Ts, T2, T3] Ptang[str, bool, float] # T1=str, T3=float, T2=bool, Ts=Tuple[()] Ptang[str, bool, float, int] # T1=str, T3=int, T2=float, Ts=Tuple[bool]