Replies: 1 comment
-
Let's consider the implications of "distributing over every overload that matches the input function". If a call expression targets a function that accepts a single callback parameter and there are n overloads that match that callback parameter, the type checker will need to evaluate the call expression n times, once for each overload. What would the result of this call be? A union of all of the evaluated results? That's probably not what you want. You probably want the results to somehow be combined back into another overload, but that operation (as opposed to a union) is not well defined in the type system. Combining the results back into an overload might be possible in the simple case where the function returns a single callable type, but what if it doesn't? What if it returns a wrapper object? A union of types? Another consideration... What if the function has m parameters that are all callbacks, and each one matches n overloads? Now the type checker would need to analyze the function n ** m times, once for each combination of the overloads, and combine all of the results. Exponential explosions such as this are generally a bad idea in static analyzers, and it's best to avoid creating constructs that result in such explosions. I think what you're suggesting here could work in the most simple and contrived cases, but it breaks down as soon as you start to generalize it. That is likely why all current type checkers simply choose the first overload that matches. |
Beta Was this translation helpful? Give feedback.
-
Function overloads can be used to inform the type checker that there are multiple valid parameter typings for the function, possibly with distinct return types for each. This can be applied to higher-order functions, such as decorators. For example:
This example decorator transforms two-parameter functions into new functions that accept the same positional parameters, in the opposite order. For the purposes of this example, we use overloads rather than
TypeVar
s. This imposes unnecessary constraints on the input types, but it can still decorate a few example functions:But what happens if the input function is itself overloaded, and the possible overloads of the input function match different overloads of the decorator? At first, it seems to work:
However, existing typecheckers select the first overload of the decorator that is able to match any overload of the input function. As a result, they infer only one type for
takes_something_and_int_flipped
, with no overloading:(int, str) -> int
. I would have expected the resulting type to be equivalent to overloading(int, str) -> int
and(int, int) -> int
.Should a decorator's overloading "collapse" when it is applied, forcing the typechecker to commit to exactly one type for the resulting decorated function? Or should it distribute over every overload that matches any overload of the input function?
Beta Was this translation helpful? Give feedback.
All reactions