Pyright hangs on code involving polymorphic functions
Pyright hangs on the following code:
from collections.abc import Callable
from typing import Protocol
class MyProto(Protocol):
def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...
p: MyProto = lambda a: lambda b: a(b)
The code contains a type error on purpose.
If I fix the type error in the implementation...
p: MyProto = lambda a, b: a(b(a))
...pyright correctly shows no errors.
But if I then curry both the protocol and the implementation using my favorite ✨ curry 💅 function...
def curry[First, *Rest, Result](function: Callable[[First, *Rest], Result]) -> Callable[[*Rest], Callable[[First], Result]]:
return lambda *rest: lambda first: function(first, *rest)
class MyProto(Protocol):
@curry
def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...
p: MyProto = curry(lambda a, b: a(b(a)))
...pyright, again, hangs.
def call[A, B](self, a: Callable[[A], B], b: Callable**[[Callable[[A], B]], A])** -> B: ...
try this ([Callable[[A], B]], A]) or this [[Callable[[A], B]], A]]
The fix for bug #10000 (which is included in the 1.1.397 release) addresses the hang for the first code sample above, but the hang persists for the second example.
I've addressed the hang in the second code sample above. However, this code will not type check without error. The problem is that the curry decorator function is being applied to the pre-bound version of __call__, so the First type parameter maps to the self parameter, but the return type of curry eliminates this parameter from the resulting signature. That means the decorated function can no longer be successfully bound to the containing protocol class, making the decorated __call__ method unusable.
You can fix this by defining a curry_method decorator that correctly handles the self parameter in an instance method (or the cls parameter in a class method).
def curry_method[S, First, *Rest, Result](
function: Callable[[S, First, *Rest], Result],
) -> Callable[[S, *Rest], Callable[[First], Result]]: ...
class MyProto(Protocol):
@curry_method
def __call__[A, B](self, a: Callable[[A], B], b: Callable[[Callable[[A], B]], A]) -> B: ...
Thank you so much for the fix and for your help! ❤️
This is addressed in pyright 1.1.398.