Let me check whether I understand: 1. tuple currently inherits from Sequence[T], where T is a single type. 2. That's a problem, considering that tuple is variadic. To deal with types like Tuple[int, str], type checkers have to special-case the conversion from the e.g. two types specified to the type of the Sequence (e.g. with a Union). 3. If we introduced variadic type variables, we could get rid of that special-casing - either because the conversion from multiple types to a single type is specified explicitly (as in Sequence[Union[*Ts]]), or because the conversion is an explicit part of the specification (as in Sequence[Ts]). If this is right, my preference would be for making the conversion from a list of types to a single type explicit, as in Union[*Ts]. I think that preference is mostly based on the intuition that it's usually better to be explicit, but I'd be curious to hear arguments the other way. --- Eric - to make sure we're on the same page about variadic type variables in general - I was a little confused about this: We could extend variadic (list) type variables to accept an optional
parameter called `arbitrary_len` that indicates whether a variadic type variable supports homogenous, arbitrary-length forms. *By default, variadic type variables wouldn't allow this.* *We'd need to make it illegal to use type variables that support arbitrary lengths with an unpack operator or `Expand` because the result would be undefined.*
I was thinking that variadic type variables *would* be arbitrary-length (and heterogeneous) by default. Is the point that unless we enforce some restrictions here, we can't properly define how to handle the conversion to a single type? Wouldn't the following work, even without those restrictions? Ts = TypeVar('Ts', list=True) # Arbitrary-length, homogeneous or heterogeneous class tuple(Generic[*Ts], Sequence[Union[*Ts]]): ... x: tuple[int] # Ts is bound to [int]; x is a Sequence[int] y: tuple[int, str] # Ts is bound to [int, str]; y is a Sequence[Union[int, str]] z: tuple[int, ...] # Ts is bound to [int, ...]; z is a Sequence[int] Hmm, maybe this is equivalent to your proposal - except here, the "work" is done by the Union. Oh, I think I see what you're saying. So variadic type variables would be arbitrary-length in that they can be bound to a fixed-size type list of any size, but they're *not* arbitrary length in that they *can't* be bound to an arbitrary-sized type list [int, ...]. Is that right? OK, so it seems like the crux of the matter is whether [int, ...] is a valid thing for a variadic type variable to be bound to. Because in order to define tuple using a variadic type variable, [int, ...] *would* have to be a valid thing for a variadic type variable to be bound to. Alright, I'll pause here to check whether I've understood so far :) On Fri, 11 Dec 2020 at 00:24, Eric Traut <eric@traut.com> wrote:
In your example above, you asked "What is the type of b?". I agree that it should be `Union[int, str]`. That's consistent with pyright's current behavior.
Here are a few thoughts about how we could generalize variadic type variables to work with tuple.
We could extend variadic (list) type variables to accept an optional parameter called `arbitrary_len` that indicates whether variadic type variable supports homogenous, arbitrary-length forms. By default, variadic type variables wouldn't allow this. We'd need to make it illegal to use type variables that support arbitrary lengths with an unpack operator or `Expand` because the result would be undefined.
For type variables that support arbitrary length lists, we could define some simple rules for how they "collapse" to traditional (non-list) types. 1. For a variadic TypeVar that is bound to a homogenous, arbitrary-length list, the type list [T, ...] collapses to a type of T. 2. For a variadic TypeVar that is bound to a heterogenous, fixed-length list, the type list collapses to a union of the individual subtypes with literals stripped. For example, [Literal['a'], int] collapses to Union[str, int]. For an explanation of why it's important to strip literals in this case, refer to [this discussion]( https://github.com/microsoft/pyright/issues/1249).
This would allow us to define the `tuple` class and its constructor as follows: ```python _T = TypeVar("_T") _Ts = TypeVar("_Ts", list=True, arbitrary_len=True)
class tuple(Sequence[_Ts]): @overload def __new__(cls: Type[_T], iterable: Tuple[_Ts] = ...) -> _T: ...
@overload def __new__(cls: Type[_T], iterable: Iterable[_Ts] = ...) -> _T: ... ```
Thoughts?
-- Eric Traut Contributor to pyright/pylance _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: mrahtz@google.com