dry-python/classes

Make sure the generic definition of AssociatedType matches a typeclass instance

sobolevn opened this issue · 1 comments

Let's say you have this code:

from typing import TypeVar

from classes import typeclass, AssociatedType

X = TypeVar('X')

class Compare(AssociatedType):
    ...

@typeclass(Compare)
def compare(instance: X, other: X) -> X:
    ...

As you can see, your typeclass is generic. But, associated type is not.
It might lead to invalid type inference in some cases:

from typing import TypeVar, Iterable, List

from classes import typeclass, AssociatedType

X = TypeVar('X')

class GetItem(AssociatedType):
    ...

@typeclass(GetItem)
def get_item(instance: Iterable[X], index: int) -> X:
    ...

@get_item.instance(list)
def _get_item_list(instance: List[X], index: int) -> X:
    ...

reveal_type(get_item([1, 2, 3], 1))
# Revealed type is "<nothing>"

It can be fixed by modifing GetItem type:

class GetItem(AssociatedType[X]):
    ...

reveal_type(get_item([1, 2, 3], 1))
# Revealed type is "builtins.int*"

We need to add an error message for this case.

Update, here's the correct "wrong" example:

from typing import TypeVar, List

from classes import typeclass, AssociatedType, Supports

X = TypeVar('X')

class Compare(AssociatedType):
    ...

@typeclass(Compare)
def compare(instance: List[X]) -> X:
    ...

x: Supports[Compare]
reveal_type(compare(x))