> I just realised that a related question is how unparameterized aliases should behave.
> Float32Array = Array[np.float32, *Shape]

A variadic class without parameters will bind any `Ts` to `Tuple[Any, ...]`.

So, in the above example, `x: Float32Array` will resolve to `Array[np.float32, *Tuple[Any, ...]]`.

The same goes for `class Array(Generic[T, *Ts]):` where the user says `x: Tensor`. We will resolve to `x: Tensor[Any, *Tuple[Any, ...]]`.

> IntTuple = Tuple[int, *Ts]

Like above, this will be `Tuple[int, *Tuple[Any, ...]]`.

This cleanly preserves the existing semantics and allows us to support gradual typing, without having to introduce new concepts like `Tuple[int, Any, ...]`. Given that `Tuple[int, *Tuple[Any, ...]]` expresses what we want, I don't think that we need to add syntax for `Tuple[int, Any, ...]`.

Note that this is analogous to TypeScript's syntax:

```typescript
declare function accepts_T_followed_by_any<T>(x: [T, ...any[]]): T;
const x10: number = accepts_T_followed_by_any([1, "hello", 3]);
```

> I pushed a PR that fixes these, and Guido already approved and merged the change.

Ah, thanks for noticing those, Eric. We had fixed some of these issues in our running Google Doc (https://docs.google.com/document/d/1oXWyAtnv0-pbyJud8H5wkpIk8aajbkX-leJ8JXsE318/edit?usp=sharing) but not yet updated the PR. Will try to keep the PR more up-to-date.

> The PEP specifies that `Shape` should be interpreted as `Shape[Any, ...]` when the type arguments are omitted, but there's no way to actually write this as a way to indicate "I know what I'm doing, I didn't simply forget to add the type arguments!". The proposal is effectively saying "the type system supports open-ended variadic generics, but the only way to specify them is to use a syntax that we want to discourage and will be considered an error in some type checkers. That strikes me as problematic.
> 2. We support the [T, ...] notation for generic types and type aliases that use variadic type vars.

I've been ok with first-class support of unbounded tuples as valid Ts bindings. In any case, we need to implement it internally for supporting gradual typing.

The only question had been whether we would surface this syntax to users. The main reason for not allowing users to write `Tensor[int, *Tuple[Any, ...]]` was that it might be confusing. If that is not a big concern, we can support this syntax explicitly. (Note that this is what TypeScript does.)

I favor `Tensor[int, *Tuple[Any, ...]]` over new syntax like `Tensor[int, ...]` because the former allows for more nuanced types like `Tensor[int, str, *Tuple[Any, ...], T]` whereas the latter doesn't. It's also a clear analogy that we are replacing `Ts` with `Tuple[Any, ...]`.

Thoughts?


On Sat, Feb 20, 2021 at 4:30 PM Eric Traut <eric@traut.com> wrote:
The latest draft is looking really good.

I ran all of the samples through pyright and uncovered a few small errors. I pushed a PR that fixes these, and Guido already approved and merged the change.

I noticed that the updated PEP includes a proposal for unparameterized generic type aliases. It currently indicates that a missing type argument for a variadic type var should be interpreted as a zero-length tuple. This interpretation would be inconsistent with every other case where type arguments are omitted, so I don't think that's the right answer.

In my opinion, omitting a type argument for a variadic type parameter should imply `Tuple[Any, ...]`. That's consistent with how `Tuple` works.

If the type alias is concatenating other types with the variadic type var into a tuple, then the entire tuple should become a `Tuple[Any ...]`. (Alternatively, we could extend PEP 484 to support open-ended tuples that specify one or more required entry types as Guido mentions above, but I think we should do that as a separate PEP if there is desire to do so.)

Here's how my proposal would work:
```python
SimpleTuple = Tuple[*Ts]
IntTuple = Tuple[int, *Ts]

# Missing type argument implies *Tuple[Any, ...]
x1: SimpleTuple
reveal_type(x1) # Tuple[Any, ...]

# Missing type argument implies *Tuple[Any, ...]
x2: IntTuple
reveal_type(x2) # Tuple[Any, ...]

# Explicit type argument with zero-length tuple
x3: IntTuple[()]
reveal_type(x3) # Tuple[int]
```

This is consistent with how pyright handles unpacked open-ended tuples in other situations today:

```python
def func(*args):
    x1 = (*args, )
    reveal_type(x1) # Tuple[Any, ...]

    x2 = (3, *args)
    reveal_type(x2) # Tuple[Any, ...]

    x3 = (3, *())
    reveal_type(x3) # Tuple[Literal[3]]
```

(Mypy reveals the types of `x1` and  `x2` as `builtins.tuple[Any]`. That looks like a bug to me. It should be `builtins.tuple[Any, ...]`.)

There's one other thing that still troubles me with the latest draft. It shows up in this line:
```python
ShapeType = TypeVar('ShapeType', Ndim, Shape)
```

In its strictest mode, pyright complains about both `Ndim` and `Shape` missing type arguments. It's a common mistake for developers to forget type arguments on generic types, so this is an important error. You can fix the problem with `Ndim` by changing it to `Ndim[Any]`, but you can't do the same with `Shape` because `Shape[Any]` would imply a single dimension for the variadic.

The PEP specifies that `Shape` should be interpreted as `Shape[Any, ...]` when the type arguments are omitted, but there's no way to actually write this as a way to indicate "I know what I'm doing, I didn't simply forget to add the type arguments!". The proposal is effectively saying "the type system supports open-ended variadic generics, but the only way to specify them is to use a syntax that we want to discourage and will be considered an error in some type checkers. That strikes me as problematic.

I can see two ways to fix this:
1. We make it illegal to omit type arguments for generic types and type aliases that use variadic type vars.
2. We support the [T, ...] notation for generic types and type aliases that use variadic type vars.

We previously rejected option 1 because it's inconsistent with the precedent set in PEP 484 — and we wanted to allow developers to incrementally add types.

We rejected option 2 because (if I remember correctly) Matthew had some ideas for other ways that the ellipsis could be used for variadic generic classes in the future.

Given our choices, I'd like to push for option 2. I think there are other ways to accommodate future extensions without relying on ellipsis syntax.

If we make this change, we could also bring back support for binding TypeVarTuples to open-ended tuples.

--
Eric Traut
Contributor to Pyright & Pylance
Microsoft Corp.
_______________________________________________
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: gohanpra@gmail.com


--
S Pradeep Kumar