**Warning: long stream-of-consciousness email. Tl; dr: `TypeVar(bound=Tuple)` is extremely elegant, but might limit our options later on because of how the variance of `Tuple` works. I'm still leaning towards `TypeVarTuple`, but only just.**
Guido had a really interesting suggestion in the last tensor typing meeting: what if, instead of creating a new constructor `TypeVarTuple`, we just did `TypeVar(bound=Tuple)`?
And reordering some examples in the PEP, it's just fully hit me that this would actually make a whole bunch of sense. The simplest example I could come up with to introduce usage of `TypeVarTuple` was:
```python def identity(x: Ts) -> Ts: ... x: Tuple[int, str] y = identity(x) ```
But I was debating with myself: "OK, but we're going to have to be explicit about this being an example for illustrative purposes only - because otherwise, readers are going to be wondering, 'Why not just use a regular `TypeVar` for `Ts`?'"
And that's the whole point! `Ts` probably _should_ just be a regular `TypeVar`. The only thing special about the example is that we're assuming it's a `TypeVar` that definitely does contain some other types, such that we can potentially use `Unpack` or `Map` later on.
Looking back through the conversation we had about this last time, I think the main doubt we had about whether reusing a regular `TypeVar` was that it could be confusing if the variadic nature of a particular instance was only _implied_ rather than marked specifically. But if we _are_ being explicit by using `bound=Tuple`, I personally feel a lot better about it.
Thinking out loud: one consideration is how this would interact with other arguments to `TypeVar`. For example, suppose we wanted to set up `Tensor` so that things worked like this:
```python class Tensor(Generic[*Shape]): ...
class Batch: pass class TrainBatch(Batch): pass class TestBatch(Batch): pass class Time: pass
def only_accepts_batch(t: Tensor[Batch]): ...
t1: Tensor[TrainBatch] only_accepts_batch(t1) # Valid t2: Tensor[Time] only_accepts_batch(t2) # Error ```
This would work if `Shape` was somehow set up to be covariant in a way such that since `TrainBatch` is a subclass of `Batch`, `Tensor[TrainBatch]` would be considered a subclass of `Tensor[Batch]`).
How would this work if we reused `TypeVar`? It seems like it wouldn't work: if we did `Shape = TypeVar('Shape', bound=Tuple, covariant=True)`...well, the question is: is `Tuple[TrainBatch]` a subclass of `Tuple[Batch]`? That is, is `Tuple` covariant?
Hmm, I actually can't find solid information on this in PEP 483 or 484. https://github.com/python/typing/issues/2 suggests the answer is 'yes', but I guess this is only true in the `Tuple[SomeType, ...]` form; a `Tuple[Child]` doesn't seem like it should be automatically be a subclass of `Tuple[Parent]`.
We _could_ set this up so that, if a variadic type variable is unpacked, the types we check aren't `Tuple[Batch]` and `Tuple[TrainBatch]` but `Batch` and `TrainBatch` directly. But that would create an inconsistency: this wouldn't be possible if, for some reason, the user wishes to use the variadic type variable without unpacking - a use-case that feels like it should have equal rights to using a variadic type variable _with_ unpacking.
Another option would be to change the behaviour of `covariant=True` and `contravariant=True` when `bound=Tuple`. That seems less than ideal; it might not be backwards-compatible.
A third option would be to introduce an extra argument to `TypeVar` which explicitly changed the behaviour - e.g. `tuplevariance=True`. But it would have to only be valid with `bound=Tuple`, so in that case we may as well just go back to `TypeVar('Shape', tuple=True)`.
So far this was all about variance. But also, what about `bound`? What if we wanted to do:
```python class Batch: pass
BatchShape = TypeVar('BatchShape', bound=Tuple[Batch])
class BatchTensor(Generic[BatchShape]): ...
t1: BatchTensor[Batch] # Valid t2: Batchtensor[Time] # Error ```
I guess this one would also hinge on whether `Tuple[TrainBatch]` were considered a subclass of `Tuple[Batch]`. Hmm.
To zoom out, though: one the other hand, we could also argue, "Let's not tie ourselves in knots about hypothetical future features. Let's choose the approach in the present which seems simplest and most elegant. Let's not overcomplicate the solution for the sake of all the things we might hypothetically want to do in the future."
I guess the crux of that debate would be: how likely is it that the features I've sketched above are going to be ones we want? I'm not sure about that yet...
So far I'm still leaning slightly towards creating a new constructor, in order to leave our options open later on. But I do feel mighty conflicted - the elegance of `TypeVar(bound=Tuple)` is undeniable. I'll mull this over a bit more.
On Fri, 1 Jan 2021 at 15:58, David Foster email@example.com wrote:
On 12/29/20 12:24 PM, S Pradeep Kumar wrote:
That seems like an interesting and feasible idea!
Overall, I'd like to defer this for now since it would be fully backward-compatible. It probably belongs in the future type arithmetic
Agreed. The existing PEP is already fairly complex.
On 12/29/20 12:24 PM, S Pradeep Kumar wrote:
One significant change I've (tentatively) made is renaming `Expand`
to `Unpack`, to reflect the terminology we use with regular tuples. I'm surprised no one else has suggested this, so I might be missing something - are there any arguments against calling what we're doing 'unpacking'?
I think that's reasonable.
It looks like there is precedent for use of the term "unpacking" in the existing Python documentation:
- "iterable unpacking", when talking about *args >
- "unpacking argument lists", when talking about *args >
- "dictionary unpacking" when talking about **kwargs >
So even though I personally still like Expand, I agree that Unpack would probably be more consistent with existing documentation (and search engine keywords).
David Foster | Seattle, WA, USA Contributor to TypedDict support for mypy _______________________________________________ Typing-sig mailing list -- firstname.lastname@example.org To unsubscribe send an email to email@example.com https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: firstname.lastname@example.org