Here are the technical docs about
typeclass and how to use it.
Typeclasses for Python.
The first and the simplest example of a typeclass is just its definition:
>>> from classes import typeclass >>> @typeclass ... def example(instance) -> str: ... '''Example typeclass.''' >>> example(1) Traceback (most recent call last): ... NotImplementedError: Missing matched typeclass instance for type: int
In this example we work with the default implementation of a typeclass.
It raises a
NotImplementedError when no instances match.
And we don’t yet have a special case for
that why we fallback to the default implementation.
It works almost like a regular function right now.
Let’s do the next step and introduce
int instance for our typeclass:
>>> @example.instance(int) ... def _example_int(instance: int) -> str: ... return 'int case' >>> assert example(1) == 'int case'
Now we have a specific instance for
which does something different from the default implementation.
What will happen if we pass something new, like
>>> example('a') Traceback (most recent call last): ... NotImplementedError: Missing matched typeclass instance for type: str
Because again, we don’t yet have
an instance of this typeclass for
Let’s fix that.
>>> @example.instance(str) ... def _example_str(instance: str) -> str: ... return instance >>> assert example('a') == 'a'
Now it works with
str as well. But differently.
This allows developer to base the implementation on type information.
So, the rule is clear: if we have a typeclass instance for a specific type, then it will be called, otherwise the default implementation will be called instead.
We also support protocols. It has the same limitation as
It is also dispatched after all regular instances are checked.
To work with protocols, one needs
protocol named argument to instance:
>>> from typing import Sequence >>> @example.instance(protocol=Sequence) ... def _sequence_example(instance: Sequence) -> str: ... return ','.join(str(item) for item in instance) >>> assert example([1, 2, 3]) == '1,2,3'
str will still have higher priority over
>>> assert example('abc') == 'abc'
We also support user-defined protocols:
>>> from typing_extensions import Protocol, runtime_checkable >>> @runtime_checkable ... class CustomProtocol(Protocol): ... field: str >>> @example.instance(protocol=CustomProtocol) ... def _custom_protocol_example(instance: CustomProtocol) -> str: ... return instance.field
Now, let’s build a class that match this protocol and test it:
>>> class WithField(object): ... field: str = 'with field' >>> assert example(WithField()) == 'with field'
See our official docs to learn more!
General case function to create typeclasses.
Base class for all associated types.
How to use? Just import and subclass it:
>>> from classes import AssociatedType, typeclass >>> class Example(AssociatedType): ... ... >>> @typeclass(Example) ... def example(instance) -> str: ... ...
It is special, since it can be used as variadic generic type
(generic with any amount of type variables),
thanks to our
>>> from typing import TypeVar >>> A = TypeVar('A') >>> B = TypeVar('B') >>> C = TypeVar('C') >>> class WithOne(AssociatedType[A]): ... ... >>> class WithTwo(AssociatedType[A, B]): ... ... >>> class WithThree(AssociatedType[A, B, C]): ... ...
At the moment of writing,
is not accepted and is not supported by
Right now it does nothing in runtime, but this can change in the future.
Used to specify that some value is a part of a typeclass.
>>> from classes import typeclass, Supports >>> class ToJson(object): ... ... >>> @typeclass(ToJson) ... def to_json(instance) -> str: ... ... >>> @to_json.instance(int) ... def _to_json_int(instance: int) -> str: ... return str(instance) >>> def convert_to_json(instance: Supports[ToJson]) -> str: ... return to_json(instance) >>> assert convert_to_json(1) == '1' >>> convert_to_json(None) Traceback (most recent call last): ... NotImplementedError: Missing matched typeclass instance for type: NoneType
You can also annotate values as
Supports if you need to:
>>> my_int: Supports[ToJson] = 1
But, this will fail in
my_str: Supports[ToJson] = 'abc' # Incompatible types in assignment # (expression has type "str", variable has type "Supports[ToJson]")
Supports only works with typeclasses defined with associated types.