Ah, sorry, still getting my head around what 'soundness' means.

Isn't the first option unsound in the same way that normal type variables are unsound, though? E.g. one could do the following:

Mypy is fine with this, even though it wouldn't work at runtime if `Tensor` were given a complete implementation (since the argument `x` is zero-rank, so we shouldn't be able to index it).```T = TypeVar('T')class Tensor(Generic[T]):def __getitem__(self, value):return 0 # Dummy valuedef foo(x: Tensor):return x[0]x: Tensor[()] = Tensor()foo(x)```

Option one would be unsound, but at least it would be unsound in a consistent way.

On Tue, 2 Feb 2021 at 21:49, S Pradeep Kumar <gohanpra@gmail.com> wrote:

--On Tue, Feb 2, 2021 at 1:08 PM Matthew Rahtz via Typing-sig <typing-sig@python.org> wrote:**Unbounded tuples**Pradeep, do you think we should include support for unbounded tuples in the PEP? I'd prefer to hold offI'm ok with just allowing `Tensor` to be the only unbounded variadic allowed. It would be implicitly treated as `Tensor[*Tuple[Any, ...]]`. So, we wouldn't allow explicitly using `Tensor[*Tuple[int, ...]]`.**Arbitrary-rank tensors**Oh, man, super well-caught! You're right, committing to invariance by default does put us in a tricky situation.But then - trying this with a regular `TypeVar`, mypy seems to be happy with the following:```from typing import Generic, TypeVarT = TypeVar('T')class Tensor(Generic[T]):passdef expects_arbitrary_tensor(x: Tensor):passdef expects_concrete_tensor(x: Tensor[int]):passx: Tensor = Tensor()expects_concrete_tensor(x)y: Tensor[int] = Tensor()expects_arbitrary_tensor(y)```Any idea why that works?A non-variadic generic class `Foo` without parameters resolves to `Foo[Any]`. As I'd mentioned, we consider `List[Any]` to be compatible with `List[int]` and vice versa, despite invariance.To work around this, we could either

(i) allow Tuple[Any, ...] in general to be compatible with Tuple[int, str], or

(ii) special-case variadic classes like Tensor so that `Tensor` is compatible with `Tensor[int, str]` and vice versa.> Both are unsound.I'd actually be much more strongly in favour of the option where `Tuple[Any, ...]` is compatible with `Tuple[int, str]`. `TypeVar` is invariant by default too, but in order to support gradual typing doesn't it *have* to behave such that `Tuple[int]` is compatible with `Tuple[Any]`?Could you expand on why that first option is unsound?> Both are unsound. The tuple or tensor we pass in may have zero elements and may thus cause a runtime error. Or its element may be a type that can't be used as an `int` or `str`, which is again a runtime error.Both are unsound for the same reasons. As I'd mentioned, we might pass an empty tuple to something that expects `Tuple[int, str]`, which would be a runtime error. For example, `x: Tuple[Any, ...] = (); foo(x)` where `def foo(x: Tuple[int, str]) -> None: x[0] + 1`. Or it might be a tuple with a non-int class as the dimension, which again would be a runtime error if used as an `int` or a `str`. For example, `x: Tuple[Any, ...] = ("hello",); foo(x)`.The first option is not backward-compatible because we would have to change existing errors about `Tuple[Any, ...]` not being compatible with `Tuple[int, str]`.But, yeah, I'd appreciate opinions on the above choice.S Pradeep Kumar