Consider the common splat
function, also known as spread
, which takes a function of n
arguments and wraps it in a function of one n
-element tuple argument:
def splat(f: Callable[???, A]) -> Callable[[???], A]:
def splatted(args: ???) -> A:
return f(*args)
return splatted
How do I annotate the type of parameter f
and the return value? Is it even expressible in Python's type system?
Sure this is possible, but I'd advise sticking to positional-only parameters and strictly avoid keyword parameters (which won't work).
The idea is to employ a type variable tuple (TVT) to capture the function's n
(positional) parameters, then unpack the parameters (represented by the TVT) into a single concrete tuple in the transformed function. The transformed function will then only have one parameter, which is the concrete tuple.
Demo (mypy Playground, Pyright playground):
import collections.abc as cx
class A: ...
def splat[*Ts](f: cx.Callable[[*Ts], A], /) -> cx.Callable[[tuple[*Ts]], A]:
def splatted(args: tuple[*Ts], /) -> A:
return f(*args)
return splatted
@splat
def f(a: int, b: str, /) -> A:
return A()
>>> f((1, "")) # OK
>>> f(1, "") # error, expected 1 tuple argument
>>> f((1, 1)) # error, second element of tuple should be `str`
The decorator usage is for demonstration purposes only - you don't need to use this as a decorator.