PEP 646 and PEP 612 concatenation
Hello all, There's some overlap between PEP 612 and PEP 646 and I thought it would be good to start a discussion. Sorry if this has already been covered, just trying to catch up! :-) The current draft of PEP 646 includes this example: ``` Ts = TypeVarTuple('Ts') def f(func: Callable[[int, *Ts], Any]) -> Tuple[*Ts]: ... def foo(int, str, float): ... def bar(str, int, float): ... f(foo) # Valid; inferred type is Tuple[str, float] f(bar) # Not valid ``` This is similar to the Concatenate operator PEP 612 introduces. Compare: ``` P = ParamSpec('P') def f1(func: Callable[Concatenate[int, P], Any]) -> Callable[P, Any]: ... Ts = TypeVarTuple('Ts') def f2(func: Callable[[int, *Ts], Any]) -> Callable[[*Ts], Any]: ... ``` Question 1: Do f1 and f2 differ? I'm assuming f2 won't preserve keyword arguments like ParamSpec does and that a function that takes keyword-only arguments would be invalid if matched against a TypeVarTuple. But some language in the PEP mentioning that would be good. Question 2: Should ParamSpec support splatting? Should Concatenate exist at all? That is, in addition to (or instead of) `Callable[Concatenate[int, P], Any]` we spell it `Callable[[int, *P], Any]`. There shouldn't be any parsing / runtime concerns for ParamSpec splatting, since it would just be normal sequence unpacking. If Concatenate is destined to be vestigial, it's not too late to get rid of it! :-) If Concatenate should exist, should `Concatenate[x, Ts]` generally be a synonym for `[x, *Ts]`? Note that Concatenate can't serve as a replacement for PEP 646's proposed Unpack, since you can't spell `def f(*args: Unpack[Ts])` and stuff like `[int, *Ts, float]` becomes awkward. (But as far as I can tell, Unpack / splatting could do everything Concatenate would). Thanks, Shantanu
Hey Shantanu, Thanks for bringing this up! Some history which might be relevant: we actually started PEP 646 using `Concatenate` for our concatenation purposes too, and only realised the unpacking approach using the star operator might be shorter/more intuitive when someone else (Lucio) suggested it. That is, the reason we're not using `Concatenate` isn't because of how it behaves, but more because of its aesthetics. I'm assuming f2 won't preserve keyword arguments like ParamSpec does and
that a function that takes keyword-only arguments would be invalid if matched against a TypeVarTuple. But some language in the PEP mentioning that would be good.
That's right - `TypeVarTuple` currently doesn't deal with keyword arguments. I'll clarify this in the current draft of the PEP. As for whether the unpacking approach could replace `Concatenate` - my current feeling is that it shouldn't, because of the semantics of how unpacking normally works: a single star is reserved for unpacking to positional arguments, while a double star is needed for keyword arguments. As I understand it, `Concatenate` can handle both positional and keyword arguments, whereas `*P`, suggests to me unpacking of a sequence of positional arguments. To put it another way: a TypeVarTuple behaves like a tuple, whereas a ParamSpec behaves like a dict. Mark, what do you think? On Mon, 18 Jan 2021 at 03:15, Shantanu Jain <hauntsaninja@gmail.com> wrote:
Hello all,
There's some overlap between PEP 612 and PEP 646 and I thought it would be good to start a discussion. Sorry if this has already been covered, just trying to catch up! :-)
The current draft of PEP 646 includes this example:
``` Ts = TypeVarTuple('Ts')
def f(func: Callable[[int, *Ts], Any]) -> Tuple[*Ts]: ...
def foo(int, str, float): ... def bar(str, int, float): ...
f(foo) # Valid; inferred type is Tuple[str, float] f(bar) # Not valid ```
This is similar to the Concatenate operator PEP 612 introduces. Compare:
``` P = ParamSpec('P')
def f1(func: Callable[Concatenate[int, P], Any]) -> Callable[P, Any]: ...
Ts = TypeVarTuple('Ts')
def f2(func: Callable[[int, *Ts], Any]) -> Callable[[*Ts], Any]: ... ```
Question 1: Do f1 and f2 differ?
I'm assuming f2 won't preserve keyword arguments like ParamSpec does and that a function that takes keyword-only arguments would be invalid if matched against a TypeVarTuple. But some language in the PEP mentioning that would be good.
Question 2: Should ParamSpec support splatting? Should Concatenate exist at all?
That is, in addition to (or instead of) `Callable[Concatenate[int, P], Any]` we spell it `Callable[[int, *P], Any]`. There shouldn't be any parsing / runtime concerns for ParamSpec splatting, since it would just be normal sequence unpacking. If Concatenate is destined to be vestigial, it's not too late to get rid of it! :-)
If Concatenate should exist, should `Concatenate[x, Ts]` generally be a synonym for `[x, *Ts]`? Note that Concatenate can't serve as a replacement for PEP 646's proposed Unpack, since you can't spell `def f(*args: Unpack[Ts])` and stuff like `[int, *Ts, float]` becomes awkward. (But as far as I can tell, Unpack / splatting could do everything Concatenate would).
Thanks, Shantanu _______________________________________________ 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: mrahtz@google.com
I agree with Matthew's response -- we should keep these separate because ParamSpec and Concatenate work on the combined package of positional and keyword parameters. I'd rather keep them separate; the use cases are pretty different IMO. On Mon, Jan 18, 2021 at 2:35 AM Matthew Rahtz via Typing-sig < typing-sig@python.org> wrote:
Hey Shantanu,
Thanks for bringing this up!
Some history which might be relevant: we actually started PEP 646 using `Concatenate` for our concatenation purposes too, and only realised the unpacking approach using the star operator might be shorter/more intuitive when someone else (Lucio) suggested it. That is, the reason we're not using `Concatenate` isn't because of how it behaves, but more because of its aesthetics.
I'm assuming f2 won't preserve keyword arguments like ParamSpec does and
that a function that takes keyword-only arguments would be invalid if matched against a TypeVarTuple. But some language in the PEP mentioning that would be good.
That's right - `TypeVarTuple` currently doesn't deal with keyword arguments. I'll clarify this in the current draft of the PEP.
As for whether the unpacking approach could replace `Concatenate` - my current feeling is that it shouldn't, because of the semantics of how unpacking normally works: a single star is reserved for unpacking to positional arguments, while a double star is needed for keyword arguments. As I understand it, `Concatenate` can handle both positional and keyword arguments, whereas `*P`, suggests to me unpacking of a sequence of positional arguments. To put it another way: a TypeVarTuple behaves like a tuple, whereas a ParamSpec behaves like a dict.
Mark, what do you think?
On Mon, 18 Jan 2021 at 03:15, Shantanu Jain <hauntsaninja@gmail.com> wrote:
Hello all,
There's some overlap between PEP 612 and PEP 646 and I thought it would be good to start a discussion. Sorry if this has already been covered, just trying to catch up! :-)
The current draft of PEP 646 includes this example:
``` Ts = TypeVarTuple('Ts')
def f(func: Callable[[int, *Ts], Any]) -> Tuple[*Ts]: ...
def foo(int, str, float): ... def bar(str, int, float): ...
f(foo) # Valid; inferred type is Tuple[str, float] f(bar) # Not valid ```
This is similar to the Concatenate operator PEP 612 introduces. Compare:
``` P = ParamSpec('P')
def f1(func: Callable[Concatenate[int, P], Any]) -> Callable[P, Any]: ...
Ts = TypeVarTuple('Ts')
def f2(func: Callable[[int, *Ts], Any]) -> Callable[[*Ts], Any]: ... ```
Question 1: Do f1 and f2 differ?
I'm assuming f2 won't preserve keyword arguments like ParamSpec does and that a function that takes keyword-only arguments would be invalid if matched against a TypeVarTuple. But some language in the PEP mentioning that would be good.
Question 2: Should ParamSpec support splatting? Should Concatenate exist at all?
That is, in addition to (or instead of) `Callable[Concatenate[int, P], Any]` we spell it `Callable[[int, *P], Any]`. There shouldn't be any parsing / runtime concerns for ParamSpec splatting, since it would just be normal sequence unpacking. If Concatenate is destined to be vestigial, it's not too late to get rid of it! :-)
If Concatenate should exist, should `Concatenate[x, Ts]` generally be a synonym for `[x, *Ts]`? Note that Concatenate can't serve as a replacement for PEP 646's proposed Unpack, since you can't spell `def f(*args: Unpack[Ts])` and stuff like `[int, *Ts, float]` becomes awkward. (But as far as I can tell, Unpack / splatting could do everything Concatenate would).
Thanks, Shantanu _______________________________________________ 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: mrahtz@google.com
_______________________________________________ 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-change-the-world/>
participants (3)
-
Guido van Rossum
-
Matthew Rahtz
-
Shantanu Jain