On Thu, Jan 13, 2022 at 7:23 AM Matthew Rahtz <mrahtz@google.com> wrote:
Thanks for this feedback, Petr!

First point (indexing assignment)

Great catch; we hadn't thought about this. I agree it would be better to keep these in sync.

I just tested this in our current CPython implementation, and can confirm it looks like this already works fine. So as much as I agree with Guido in preferring not to make too many more updates to the PEP, I guess we can indeed just fix this with a small clarification. I'll also add some tests for this to our CPython implementation.

Agreed. I just misremembered this, my bad! Please do the clarification etc.
 
Second point (multiple TypeVarTuples)

In terms of the wording in the PEP - Guido, our intention actually was to prohibit even the straightforward cases for now. Iirc, our reasoning was that we thought the decision boundary between "straightforward to infer type assignment" and "nontrivial to infer type assignment" (and of course "no unique type assignment") was tricky enough that we shouldn't complicate the already-long PEP by trying to describe all the cases where it was and wasn't ok.

Petr, do I understand that the crux for you is basically that we should commit to multiple-stars at the syntax level - the main implication being to make sure we've properly documented and tested it? I'm happy with this; we plan to follow up with another PEP that does talk about when multiple unpackings are ok anyway. I guess we should just a) clarify in the PEP that allowing multiple unpackings in the grammar isn't accidental, and b) test this in our CPython implementation?

I would love it for the cases where it's *not* ambiguous to just work (once type checkers support it). I'd like the PEP to be written so as not to disallow those. Type checkers that only support a single star even when it's unambiguous should be considered out of compliance. (But could still be fully compliant otherwise.) User pressure will then sort things out.

That seems better than requiring a follow-up PEP just to allow non-ambiguous cases that follow the PEP's semantics.

FWIW I still feel we don't have a simple enough example to distinguish the situations.

This should clearly be disallowed:

def f(*args: *tuple[*Ts1, *Ts2])...

This (and similar variants) should also be disallowed:

def f(*args: *tuple[*Ts, *Ts])...

This should be allowed:

def f(*args: *tuple[int, *Ts, str, T])...

But not this:

def f(*args: *tuple[*Ts1, str, *Ts2])...

(Even though there might be cases where it's unambiguous, like f(1, 2, 3, "", 4, 5).)

It seems you are saying that this would be disallowed, even though there's no ambiguity?

def f(a: Array[*Ts1], b: Array[*Ts2])...

I'm failing to see the point. (Other than that perhaps Pyre doesn't support it? :-) The two positional (non-variadic!) arguments have different types, each of which is an Array with variadic length. I can see nothing ambiguous about this. If you indeed want to disallow this, can you show a scenario where there's an ambiguity?

Also, in call sites I feel that multiple stars shouldn't be an issue. Example:

def f(*args: *Ts)...

f(*Array[int, int], *Array[str, str])  # Ts becomes [int, int, str, str]

 
Third point (aliases)

This is actually a great point - I had to think about this myself.

Since PEP 484 doesn't explicitly spell out how assignment of type to type variables in generic aliases works either, I had to try some things with mypy playground to figure out what the current rules are. I think the logic is, if we have an alias like

Foo = tuple[T1, T1, T2]
Foo[int, str]

then we construct a list of unique type variables in the alias (here [T1, T2]), then assign types to those variables by going left to right through the type argument list when the alias is instantiated. Guido, can you remember from your time with mypy whether this is correct? Pradeep, I guess you'll also know about this?

Yes, that's how it works. Also, Foo[int] would be an error in that case.
 
Based on that precedent, I believe that:

SplitDataset = Tuple[Array[*Ts], Array[*Ts]]
SplitDataset[Height]  # Valid; equivalent Tuple[Array[Height], Array[Height]]

(indeed, I just tested this with Pyre, and that matches our implementation there)

Yeah, looks fine.

As a corollary, this would also be correct, right?

SplitDataSet[Height, Width]

and it would make Ts be (Height, Width). Right?
 
For this one:

TwoArrays = Tuple[Array[*Ts1], Array[*Ts2]]
TwoArrays[Height]

Given the previous example and my corollary, this seems ambiguous, just like `def f(*tuple[*Ts1, *Ts2])`.
 
Since we we allow a TypeVarTuple to be bound to an empty list of types, when we try to assign type arguments [Height] to the unique list of type variables [Ts1, Ts2], I think we should end up with Ts1 containing Height and Ts2 being empty; thus:

TwoArrays[Height]  # Valid; equivalent to Tuple[Array[Height], Array[()]]

I hope not.
 
But I actually can't get this to type check correctly in Pyre. Pradeep, this is the test I was using: https://gist.github.com/mrahtz/cc86c29538de1d4a80a2e8958ae71c5a Am I doing something wrong?

Also, by that logic, in this situation all the type arguments would be assigned to Ts1, and Ts2 would always end up being empty. That seems a bit weird but...shrug it also seems correct.

No?
 
In any case, this is definitely something we should explain better in the PEP. I'll make a TODO for myself to write something on this once Pradeep and Guido have confirmed whether my understanding is correct.


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