dry-python/classes

Strict `__call__` invariance

sobolevn opened this issue · 1 comments

Now we have a problem, because function args in Python are covariant we can use it incorrectly with a typeclass.

Let's see an example:

from classes import typeclass

class A:
    ...

class B(A):
    ...

class C(B):
    ...

@typeclass
def some(instance) -> str:
    ...

@some.instance(B)
def _some_b(instance: B) -> str:
    ...

reveal_type(some(A()))  # Should be an error
# Argument 1 to "some" has incompatible type "A"; expected "B"

reveal_type(some(B()))  # ok

reveal_type(some(C()))  # Should be an error, but is not

Ok, we have another solution: we can change how __call__ is dispatched.
For example, we can use the same logic simgledispatch has with mro traversal.
This can help us to support more general types. In this case, we won't have to implement strict variance (which is really hard).

And, one more thing, we can have better defaults and better code reuse.

https://github.com/python/cpython/blob/00710e6346fd2394aa020b2dfae170093effac98/Lib/functools.py#L799