(Last response for the night.)

On Mon, Jan 25, 2021 at 10:58 AM S Pradeep Kumar <gohanpra@gmail.com> wrote:
(5) What if there is an arity mismatch?

Consider the following case (the same case as Eric pointed out :) ).

def foo(xs: Tuple[*Ts], ys: Tuple[*Ts]) -> Tuple[*Ts]: ...

tuple_int_str: Tuple[int, str]
tuple_bool: Tuple[bool]
foo(tuple_int_str, tuple_bool)

We might expect this to error because the argument types have different lengths.

However, Ts = Union[Tuple[int, str], Tuple[bool]] is a valid solution, since `Tuple` is covariant.

`foo` gets treated as:

def foo(xs: Union[Tuple[int, str], Tuple[bool]], ys: Union[Tuple[int, str], Tuple[bool]]) -> Union[Tuple[int, str], Tuple[bool]]: ...

Users might be expecting this to error and might be taken aback, as I was when I tried it out.

I experimented with disallowing a variadic `Ts` from being inferred as having two different lengths and that seemed somewhat more intuitive than the above. Opinions appreciated.

The issue here is in general if you want to solve to Union or not. In mypy we generally don't, but then we end up solving to object. However here we can't solve to object (it must at least be a Tuple) so I like the error.  

# Open questions

(1) What to do about `*Tuple[Any, ...]`?

During the last tensor meeting, we discussed allowing `Tensor[Any, ...]` (and the equivalent `Tensor`) in order to aid gradual typing.

Existing code annotated as `t: Tensor` would treat `Tensor` without parameters as `Tensor[Any, ...]`. That would be a Tensor with arbitrary rank and `Any` as the dimension type. This way, changing `class Tensor` to be a variadic wouldn't immediately break existing code.

I'm yet to implement this, so I'll look into how this affects type inference.

The same goes for `*Iterable[int]`, if indeed that is feasible.

As I wrote, the default is actually as many copies of Any as are needed to make the type valid. But the `...` notation is *only* valid for Tuple, not for any other generic classes, so that syntax is not literally valid. However, I agree that this is what omitter parameters should be taken to mean.
(2) What to do about `*Union[...]`?

If `Ts` is a type variable bound by `tuple`, then `Ts = Union[Tuple[int, str], Tuple[bool]]` is a valid assignment. We then have to consider what unpacking that means.

TypeScript allows this:

> When the type argument for T is a union type, the union is spread over the tuple type. For example, [A, ...T, B] instantiated with X | Y | Z as the type argument for T yields a union of instantiations of [A, ...T, B] with X, Y and Z as the type argument for T respectively.

Okay, so it means `[A, ...X, B] | [A, ...Y, B] | [A, ...Z, B]`. And I guess your (slightly cryptic, or condensed) question was about the instantiation of Ts from a union of tuples. It makes sense that a distributive law applies here.

--Guido van Rossum (python.org/~guido)