The answer depends on where it is in the SC's queue, which is unknowable. I'd create a PR for the PEP ASAP and add a message to the SC tracker (in the existing issue for this PEP) alerting the SC members to it. A link to this email thread might also be useful there. On Mon, Oct 25, 2021 at 8:58 AM Matthew Rahtz <mrahtz@google.com> wrote:
Thanks for the examples, Pradeep! This has convinced me too.
Guido, what's the best thing to do here given that PEP 646 is currently in review by the SC? Should we submit a PR to the peps repo now, or wait until they've delivered their verdict?
On Mon, 18 Oct 2021 at 21:16, Guido van Rossum <guido@python.org> wrote:
Okay, you've got me convinced. Using `*args: *blah` makes sense.
On Mon, Oct 18, 2021 at 12:39 PM S Pradeep Kumar <gohanpra@gmail.com> wrote:
I can't think of any use cases either. It's super unusual in Python to have a variable number arguments followed by a fixed argument, since the function being called would have to slice args to get at it.
Guido: I agree that it's unusual in regular Python code. However, it's not rare at all in Tensor code.
Here's a real-world example: Tensor.view [1] infers a dimension that is specified as -1.
```
x = torch.randn(2, 3, 4, 5, 6) y = x.view(3, 5, 2, -1) y.size # the -1 gets replaced by (2 * 3 * 4 * 5 * 6) / (3 * 5 * 2) = 24 # => (3, 5, 2, 24)
We write this as:
def view( self: Tensor[DType, *Rs], *shape: *Tuple[*Rs2, L[-1]] ) -> Tensor[ DType, *Rs2, Divide[Product[*Rs], Product[*Rs2]] ]: ... ```
Full stub for Tensor.view: [2]. Another example is `torch.reshape`, which again takes `*args`.
More importantly, as Eric pointed out, this is an incompleteness in function signatures. `Callable [[int, *Ts, str], None]` is allowed. But there is no way in the PEP to specify a function of that type. Thus we need to allow:
``` def foo(*args: *Tuple[int, *Ts, str]) -> None: ... ```
The type-checker implementer actually has to use the same data structure to represent the type of both the Callable type and the function `foo`, so Pyre already supports both. Whenever Pyre sees a Callable containing `*Ts`, such as `Callable[[int, *Ts, str], None]`, it basically treats it as a function with `*args: *Tuple[int, *Ts, str]`.
If the objection is that we would be calling `*` on something other than a `Ts`, note that in the future we'll also need to unpack Map, Broadcast, etc. The consistent solution here is to allow unpacking of any tuple.
[1]: Tensor.view: https://pytorch.org/docs/stable/generated/torch.Tensor.view.html?highlight=v... [2]: https://github.com/pradeep90/pytorch_examples/blob/master/stubs/torch/__init...
(The actual question on python-dev was about Map, as Guido said, but that and Eric's question to me earlier brought up the above point.)
On Thu, Oct 14, 2021 at 12:12 PM Guido van Rossum <guido@python.org> wrote:
I can't think of any use cases either. It's super unusual in Python to have a variable number arguments followed by a fixed argument, since the function being called would have to slice args to get at it.
But I thought the post on python-dev that you linked to was about something else?
It uses the builtin function enumerate(), which takes an iterable of values like ['x', 'y', 'z'] and turns it into a stream of pairs (0, 'x'), (1, 'y'), (2, 'z').
It then defines a function enumerate_args(f, args) which calls f(*enumerate(args)), i.e. if args has length N, then f takes N arguments, each of which has type Tuple[int, T] where T is the item type of args.
In my example ['x', 'y', 'z'], N is 3 and T is str, so enumerate_args(f, ['x', 'y', 'z']) will end up calling f((0, 'x'), (1, 'y'), (2, 'z'). I guess the type of f is then (*args: Tuple[int, T]) ->None but this doesn't capture the length of args, N. The response on python-dev indicates that the solution requires Map[].
But maybe I've missed the connection between that post and your idea of allowing *args, *Tuple[*Ts, int] in a parameter list?
On Thu, Oct 14, 2021 at 5:05 AM Matthew Rahtz via Typing-sig < typing-sig@python.org> wrote:
In the last tensor typing meeting we realised there's something missing from PEP 646: the ability to split off suffixes from *args. (Thanks for Pradeep for pointing this out, based on discussions with Eric Traut and this question on python-dev https://mail.python.org/archives/list/python-dev@python.org/thread/K76H6C5JW... )
*Intro*
To recap, PEP 646 allows us to bind the types of *args into a TypeVarTuple:
Ts = TypeVarTuple('Ts') def foo(*args: *Ts): ... foo(1, '2') # Ts is bound to Tuple[int, str]
But what if we wanted to do something like the following?
def foo( *args: [types of all positional args but the last bound to Ts; type of last positional arg should be int] ) -> Tuple[*Ts]: ...
x = foo(True, 1.0, 2) # x should have type Tuple[bool, float]
*The problem*
You might think the answer is:
def foo( *args: *Ts, final_arg: int, ) -> Tuple[*Ts]: ...
But note that this means something different; all arguments after *args must be specified as keywords:
foo(True, 1.0, 2) TypeError: foo() missing 1 required keyword-only argument: 'final_arg'
Note that this is an only an issue for suffixes of variadic argument lists; prefixes are fine:
def foo( first_arg: int, *args: *Ts, ) -> Tuple[*Ts]: ...
*Proposed solution*
We think the way of encoding what we want most consistent with the rest of PEP 646 would be something like:
def foo( *args: *Tuple[*Ts, int], ) -> Tuple[*Ts]: ...
Why? Well, when we say `*args: *Ts`, we're saying "Put all the types of `args` into the thing that comes after the starred part of the annotation". So here, we're saying "Put all the types of `args` into Tuple[*Ts, int]" - that is, doing some pattern matching, "Put all types but the last into `Ts`".
*Use cases?*
This is (for me) an unintuitive enough construction that I think our first question should be, does this have plausible enough use cases to support the complexity?
The only one I can think off of the top of my head is something like:
def log_with_logger(*args: *Tuple[*Ts, Logger]): ... log_with_logger('foo', 'bar', logger_inst)
But here the issue could be worked around by just moving the position of the logger argument:
def log_with_logger(logger: Logger, *args: *Ts): ...
So I'm not sure I really believe this example.
Can anyone else think of any use cases for this?
---
Thanks! Matthew _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: guido@python.org
-- --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...> _______________________________________________ Typing-sig mailing list -- typing-sig@python.org To unsubscribe send an email to typing-sig-leave@python.org https://mail.python.org/mailman3/lists/typing-sig.python.org/ Member address: gohanpra@gmail.com
-- S Pradeep Kumar
-- --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 (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...>