Questions about using PEP 637 syntax in PEP 646

Hello, This is a great PEP. It turns out to be applicable in a variety of scenarios. Case in point: Matthew Rahtz and I are working on PEP 646: Variadic Generics (https://www.python.org/dev/peps/pep-0646/; discussion on typing-sig). It is a type system feature that allows specifying an arbitrary tuple of type variables instead of a single type variable. We plan to use your proposed syntax to represent unpacking a tuple type. This would be analogous to `*` for unpacking a tuple value: + `Tensor[int, *Ts, str]` and `Tensor[*Ts1, *Ts2]` + such variadic classes would be declared as `class Tensor(Generic[T, *Ts, T2]):` Questions: 1. Does PEP 637 support unpacking multiple `*` arguments? - e.g., Tensor[int, *Ts, str, *Ts2] 2. Does PEP 637 allow a positional argument after a `*`? - e.g., Generic[T, *Ts, T2] PEP 637 says "Sequence unpacking is allowed inside subscripts", so it looks like these should be allowed (just as in function calls). But I wanted to confirm it explicitly since this is our core use case and there was no example with multiple sequences being unpacked. 3. We also wanted to ask - how's your implementation going? We'll be starting implementation in typing.py soon. Since there's some overlap we wanted to make sure we're not duplicating your work, and that there won't be any merge conflicts later. Do you have a fork we might be able to get early access to? We're also targeting the 3.10 release for our implementation. I'd be happy to provide additional details if needed. Best, Pradeep Kumar Srinivasan Matthew Rahtz

On Tue, 19 Jan 2021 at 13:51, <gohanpra@gmail.com> wrote:
Yes, technically yes, and it does in our experimental branch. _but_ Brandt and I are unsure about the semantics of it, specifically the corner case of star unpacking a tuple with one element. The current PEP says that the following two are equivalent: a[1] and a[*(1,)]. (calls __getitem__ with index = 1) However, the way it's implemented now, these two are equivalent a[1,] and a[*(1,)] (calls __getitem__ with index = (1,)) I think we would both love a discussion over it.
2. Does PEP 637 allow a positional argument after a `*`? - e.g., Generic[T, *Ts, T2]
Yes.
PEP 637 says "Sequence unpacking is allowed inside subscripts", so it looks like these should be allowed (just as in function calls). But I wanted to confirm it explicitly since this is our core use case and there was no example with multiple sequences being unpacked.
They are.
3. We also wanted to ask - how's your implementation going?
It's ready, but not reviewed, and not matching the PEP for the point above. https://github.com/stefanoborini/cpython/tree/PEP-637-implementation-attempt... I'll update against master tonight.
We'll be starting implementation in typing.py soon. Since there's some overlap we wanted to make sure we're not duplicating your work, and that there won't be any merge conflicts later. Do you have a fork we might be able to get early access to? We're also targeting the 3.10 release for our implementation.
Contact me directly if you have any issues with it. -- Kind regards, Stefano Borini

On Fri, Jan 29, 2021 at 2:28 AM Stefano Borini <stefano.borini@gmail.com> wrote:
I think for PEP 646 it doesn't matter. That PEP is mostly concerned with static checks and the static checker can assign this any meaning it wants as long as it is syntactically allowed. Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
Excellent. -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

Guido van Rossum wrote:
Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
Yup, I agree. Not only because it feels more intuitive to me (which is probably why I thoughtlessly implemented the parser this way before Stefano pointed out to me that it contradicted the PEP), but also because it's much simpler to reason about and understand. We don't have to worry about the type of the index varying at runtime based on the length of the unpacked args (empty tuple vs single object vs two-or-more-element tuple). I'm pretty sure that's a big win for static analysis, and I *know* it's a big win for simplifying the implementation. The "rule" for this is simple. If there's a star-unpacking in the positional arguments, it will always pass a tuple, regardless of the resulting length. Brandt

On Sat, 30 Jan 2021 at 00:10, Guido van Rossum <guido@python.org> wrote:
I think for PEP 646 it doesn't matter. That PEP is mostly concerned with static checks and the static checker can assign this any meaning it wants as long as it is syntactically allowed.
Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
All right, but then I have to fix the PEP and the test that is currently failing. I'll do it tonight. Thanks -- Kind regards, Stefano Borini

On Tue, 19 Jan 2021 at 13:51, <gohanpra@gmail.com> wrote:
Yes, technically yes, and it does in our experimental branch. _but_ Brandt and I are unsure about the semantics of it, specifically the corner case of star unpacking a tuple with one element. The current PEP says that the following two are equivalent: a[1] and a[*(1,)]. (calls __getitem__ with index = 1) However, the way it's implemented now, these two are equivalent a[1,] and a[*(1,)] (calls __getitem__ with index = (1,)) I think we would both love a discussion over it.
2. Does PEP 637 allow a positional argument after a `*`? - e.g., Generic[T, *Ts, T2]
Yes.
PEP 637 says "Sequence unpacking is allowed inside subscripts", so it looks like these should be allowed (just as in function calls). But I wanted to confirm it explicitly since this is our core use case and there was no example with multiple sequences being unpacked.
They are.
3. We also wanted to ask - how's your implementation going?
It's ready, but not reviewed, and not matching the PEP for the point above. https://github.com/stefanoborini/cpython/tree/PEP-637-implementation-attempt... I'll update against master tonight.
We'll be starting implementation in typing.py soon. Since there's some overlap we wanted to make sure we're not duplicating your work, and that there won't be any merge conflicts later. Do you have a fork we might be able to get early access to? We're also targeting the 3.10 release for our implementation.
Contact me directly if you have any issues with it. -- Kind regards, Stefano Borini

On Fri, Jan 29, 2021 at 2:28 AM Stefano Borini <stefano.borini@gmail.com> wrote:
I think for PEP 646 it doesn't matter. That PEP is mostly concerned with static checks and the static checker can assign this any meaning it wants as long as it is syntactically allowed. Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
Excellent. -- --Guido van Rossum (python.org/~guido) *Pronouns: he/him **(why is my pronoun here?)* <http://feministing.com/2015/02/03/how-using-they-as-a-singular-pronoun-can-c...>

Guido van Rossum wrote:
Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
Yup, I agree. Not only because it feels more intuitive to me (which is probably why I thoughtlessly implemented the parser this way before Stefano pointed out to me that it contradicted the PEP), but also because it's much simpler to reason about and understand. We don't have to worry about the type of the index varying at runtime based on the length of the unpacked args (empty tuple vs single object vs two-or-more-element tuple). I'm pretty sure that's a big win for static analysis, and I *know* it's a big win for simplifying the implementation. The "rule" for this is simple. If there's a star-unpacking in the positional arguments, it will always pass a tuple, regardless of the resulting length. Brandt

On Sat, 30 Jan 2021 at 00:10, Guido van Rossum <guido@python.org> wrote:
I think for PEP 646 it doesn't matter. That PEP is mostly concerned with static checks and the static checker can assign this any meaning it wants as long as it is syntactically allowed.
Honestly I think it's fine the way you have implemented it -- since there is a difference between a[1] and a[1,], a[*t] where len(t) == 1 has to make a choice, and it's fine if this always passes a tuple.
All right, but then I have to fix the PEP and the test that is currently failing. I'll do it tonight. Thanks -- Kind regards, Stefano Borini
participants (4)
-
Brandt Bucher
-
gohanpra@gmail.com
-
Guido van Rossum
-
Stefano Borini