pythonvariadic-functionspython-typinghigher-order-functions

Type for heterogeneous *args


In typed Python, how do you type a *args list that you're expecting to pass to another argument that is a higher kinded function?

For example, this function takes a function and its first argument and internally just routes *args through, but its type signature depends on the type of that, possibly heterogeneous, argument list:

def curry_one[T, R](x: T, f: Callable[[T, WHAT_GOES_HERE], R]) -> Callable[[WHAT_GOES_HERE], R]:
  def curried(*args: WHAT_GOES_HERE) -> R:
    return f(x, *args)
  return curried

Can Expand be used with a generic type parameter over some kind of heterogeneous array?

(I'm not interested in a curry function in the standard library. I'm writing some specific function adapters and am just using curry as an example.)


Solution

  • You are looking for TypeVarTuple, which represents an unknown number of types:

    (playgrounds: Mypy, Pyright)

    def curry_one[T, *Us, R](x: T, f: Callable[[T, *Us], R]) -> Callable[[*Us], R]:
      def curried(*args: *Us) -> R:
        return f(x, *args)
      return curried
    
    def foo(a: int, b: str, c: float, d: bytes) -> None: ...
    
    reveal_type(foo1 := curry_one(0, foo))      # (str, float, bytes) -> None
    reveal_type(foo2 := curry_one('', foo1))    # (float, bytes) -> None
    reveal_type(foo3 := curry_one(3.14, foo2))  # (bytes) -> None
    reveal_type(foo4 := curry_one(b'', foo3))   # () -> None
    

    Do note that all parameter names will be lost and thus arguments can only be passed positionally.