Type system improvements

Variadic generics


  • Builtin functions map(), zip(), etc.
  • Decorators, e.g. @contextmanager, and many user-defined
  • Numeric libraries (a separate discussion)

Potential syntax

  • Variadic type variable: Ts = TypeVar('Ts', variadic=True)
  • Variadic binding to star arguments: *args: List[Ts]
  • Equivalent to arg_0: List[T_0], arg_1: List[T_1], ...
  • Expanded binding in variadic type constructors: Tuple[int, Expand[Ts]]
  • Equivalent to Tuple[int, T_0, T_1, ...]

Syntax examples

Ts = TypeVar('Ts', variadic=True)
def timed_query(*qs: Query[Ts]) -> Tuple[float, Expand[Ts]]:
timed_query(Query(User), Query(Address))
    # inferred type is Tuple[float, User, Address]

R = TypeVar('R')
def make_async(fn: Callable[[Expand[Ts]], R]
               ) -> Callable[[Expand[Ts]], Future[R]]: ...

Open questions

  • Explicit Expand vs implicit Expand? In most cases Expand can be omitted.
  • Type-checking bodies? Probably no, as for overloads.
  • Support TypedDict for **kwargs, should Expand be required?

More open questions

Support defining variadic classes and aliases? This useful for callback protocols and numeric types:

class Timed(Protocol[Ts, R]):
    def __call__(self, *args: Ts, timeout: int = ...) -> R: ...

def drop_long(fn: Timed[Ts, R]) -> Timed[Ts, Optional[R]]:

FloatArray = NDArray[float, Shape[Ts]]
arr: FloatArray[N, M]  # same as NDArray[float, Shape[N, M]]

Numeric stack

Numeric data types

  • Dense data, a.k.a. ND array, a.k.a. “tensor”:
  • Element type: int8, float32, etc.
  • Array shape: N_0 by N_1 by N_2 by …
  • Sparse data, a.k.a. dataframe, a.k.a, model:
  • Set of columns: 'col_0', 'col_1',
  • Each can have different type

Typing dense data

  • Extended numeric tower
  • New typing primitive Shape[...]
  • Integer generics: N = IntVar('N')