Concatenate with non-generic ParamSpec
`Concatenate` is useful in a non-generic context, to define a callable which takes certain arguments, followed by varargs. For example: from collections.abc import Callable from typing import Concatenate, ParamSpec P = ParamSpec("P") SomeCallableType = Callable[Concatenate[int, P], None] class Foo: some_callable: SomeCallableType[P] But type checkers will generate an error, e.g that `P` is unbound (mypy) or that it's meaningless (Pyright). Would there by any interest in allowing `ParamSpec` to stand alone in `Concatenate[int, P]` for varargs that don't need to be captured? PEP 612 contains this sole example of a (seemingly valid) solitarily `ParamSpec` in `Concatenate`: def expects_int_first(x: Callable[Concatenate[int, P], int]) -> None: ... Was it overlooked or did implementers believe it shouldn't be allowed? Best D Sent with ProtonMail Secure Email.
A generic parameter can be bound in two places: the header of a generic class, or a function parameters body. If we had explicit generic syntax, the example from PEP 612 would look like this: ``` def expects_int_first<P>(x: Callable[Concatenate[int, P], int]) -> None: ... ``` which isn't legal python but makes it clearer what's happening: P is in fact bound by `expects_int_first`, which happens implicitly because `P` appears in the arguments. It's a little unfortunate that we don't have java-like generic syntax in Python because that makes it much clearer where the binding happens. --- In `SomeCallableType`, P can't be bound because only classes and functions bind, `SomeCallableType` is just a type alias. It is *not* a generic. So indexing it by `P` isn't going to make sense; different type checkers will give different errors because there are a few reasons it doesn't make sense, but none of them will accept it. We *can* make a generic callable protocol that behaves this way but it's sort of horrible: class SomeCallableType(Generic[P]): def __call__(self, _x: int, /, *args: P.args, **kwargs: P.kwargs) -> None: ...
While it's not legal to use `P` in the subscript expression in your example, you can use `(...)` instead. This means "a signature that accepts any parameters". This works in conjunction with `Concatenate`. I think that addresses your use case. ```python class Foo: some_callable: SomeCallableType[(...)] ``` -Eric
Thanks both. … did the trick, I’d have never thought to use it there. Best D Sent with ProtonMail Secure Email. ‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐ On Wednesday, December 22nd, 2021 at 19:22, Eric Traut <eric@traut.com> wrote:
While it's not legal to use `P` in the subscript expression in your example, you can use `(...)` instead. This means "a signature that accepts any parameters". This works in conjunction with `Concatenate`. I think that addresses your use case.
class Foo: some_callable: SomeCallableType[(...)]
-Eric
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: layday@protonmail.com
participants (3)
-
Eric Traut -
layday -
Steven Troxler