Skip to content

Instantly share code, notes, and snippets.

@sobolevn
Last active May 30, 2022 20:04
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sobolevn/7f8ffd885aec70e55dd47928a1fb3e61 to your computer and use it in GitHub Desktop.
Save sobolevn/7f8ffd885aec70e55dd47928a1fb3e61 to your computer and use it in GitHub Desktop.
Higher Kinded Types in Python
import abc
import dataclasses
from typing import Callable, Generic, TypeVar
# TODO: pip install returns
# TODO: add `returns.contrib.mypy.returns_plugin` to mypy plugins
# TODO: read the docs at https://github.com/dry-python/returns
from returns.primitives.hkt import Kind1, SupportsKind1, kinded
_ValueType = TypeVar('_ValueType')
_NewValueType = TypeVar('_NewValueType')
_InstanceKind = TypeVar('_InstanceKind', bound='HasValue')
class HasValue(Generic[_ValueType]):
@abc.abstractmethod
@property
def value(self) -> _ValueType:
"""Returns a value property."""
@abc.abstractmethod
def with_value(
self: _InstanceKind,
new_value: _NewValueType,
) -> Kind1[_InstanceKind, _NewValueType]:
"""Creates a new instance with a changed value."""
@dataclasses.dataclass
class Box(SupportsKind1['Box', _ValueType], HasValue[_ValueType]):
value: _ValueType
length: int
width: int
height: int
def with_value(self, new_value: _NewValueType) -> 'Box[_NewValueType]':
return Box(new_value, self.length, self.width, self.height)
@dataclasses.dataclass
class Bag(SupportsKind1['Bag', _ValueType], HasValue[_ValueType]):
value: _ValueType
brand: str
model: str
def with_value(self, new_value: _NewValueType) -> 'Bag[_NewValueType]':
return Bag(new_value, self.brand, self.model)
@kinded
def apply_function(
instance: Kind1[_InstanceKind, _ValueType],
callback: Callable[[_ValueType], _NewValueType],
) -> Kind1[_InstanceKind, _NewValueType]:
new_value = callback(instance.value)
return instance.with_value(new_value)
box = Box(value=10, length=1, width=2, height=3)
bag = Bag(value=5, brand='Fancy', model='Baggy')
reveal_type(apply_function(box, str))
reveal_type(apply_function(bag, str))
# ex.py:58: note: Revealed type is 'ex.Box[builtins.str]'
# ex.py:59: note: Revealed type is 'ex.Bag[builtins.str]'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment