We try to bring as much type safety as possible.
However, there are some limitations. This section will guide you through both features and troubles of type-safe typeclasses in Python.
First of all, we always check that the signature is the same for all cases.
>>> from classes import typeclass >>> @typeclass ... def example(instance, arg: int, *, keyword: str) -> bool: ... ... ... >>> @example.instance(str) ... def _example_str(instance: str, arg: int, *, keyword: str) -> bool: ... return instance * arg + keyword ...
Let’s look at this example closely:
We create a typeclass with the signature that all cases must share:
except for the
instance parameter, which is polymorphic.
We check that return type of all cases match the original one:
bool in our particular case
We check that
instance has the correct type in
When calling typeclasses, we also do the type check:
>>> example('a', 3, keyword='b') 'aaab'
Here are features that we support:
Typechecker knows that we allow only defined instances to be the first (or instance) parameter in a call.
We also check that other parameters do exist in the original signature
We check the return type: it matches that defined one in the signature
We are limited in generics support. We support them, but without type parameters.
We don’t support
Why? Because we cannot tell the difference
List[str] in runtime.
Python just does not have this information. It requires types to be infered. And that’s currently not possible.
So, this would not work:
>>> from typing import List >>> from classes import typeclass >>> @typeclass ... def generic_typeclass(instance) -> str: ... """We use this example to demonstrate the typing limitation.""" ... >>> @generic_typeclass.instance(List[int]) ... def _generic_typeclass_list_int(instance: List[int]): ... ... ... Traceback (most recent call last): ... TypeError: ...
But, this will (note that we use
>>> from typing import List >>> from classes import typeclass >>> @typeclass ... def generic_typeclass(instance) -> str: ... """We use this example to demonstrate the typing limitation.""" ... >>> @generic_typeclass.instance(list) ... def _generic_typeclass_list_int(instance: List): ... return ''.join(str(list_item) for list_item in instance) ... >>> generic_typeclass([1, 2, 3]) '123' >>> generic_typeclass(['a', 1, True]) 'a1True'
Use primitive generics as they always have
Annotations should also be bound to any parameters.
Do not supply any other values there, we cannot even check for it.