tokencrawler/.venv/lib/python3.9/site-packages/oslash/either.py
2022-03-17 22:16:30 +01:00

145 lines
4 KiB
Python

from abc import abstractmethod
from functools import partial
from typing import Callable, TypeVar, Generic
from oslash.typing import Applicative
from oslash.typing import Functor
from oslash.typing import Monad
TSource = TypeVar("TSource")
TResult = TypeVar("TResult")
TError = TypeVar("TError")
class Either(Generic[TSource, TError]):
"""The Either Monad.
Represents either a successful computation, or a computation that
has failed.
"""
@abstractmethod
def map(self, _: Callable[[TSource], TResult]) -> "Either[TResult, TError]":
raise NotImplementedError
@classmethod
@abstractmethod
def pure(cls, value: Callable[[TSource], TResult]) -> "Either[Callable[[TSource], TResult], TError]":
raise NotImplementedError
@abstractmethod
def apply(
self: "Either[Callable[[TSource], TResult], TError]", something: "Either[TSource, TError]"
) -> "Either[TResult, TError]":
raise NotImplementedError
@classmethod
@abstractmethod
def unit(cls, value: TSource) -> "Right[TSource, TError]":
raise NotImplementedError
@abstractmethod
def bind(self, func: Callable[[TSource], "Either[TResult, TError]"]) -> "Either[TResult, TError]":
raise NotImplementedError
@abstractmethod
def __eq__(self, other) -> bool:
raise NotImplementedError
class Right(Either[TSource, TError]):
"""Represents a successful computation."""
def __init__(self, value: TSource) -> None:
self._value = value
# Functor Section
# ===============
def map(self, mapper: Callable[[TSource], TResult]) -> Either[TResult, TError]:
result = mapper(self._value)
return Right(result)
# Applicative Section
# ===================
@classmethod
def pure(cls, value: Callable[[TSource], TResult]) -> "Right[Callable[[TSource], TResult], TError]":
return Right(value)
def apply(
self: "Right[Callable[[TSource], TResult], TError]", something: "Either[TSource, TError]"
) -> "Either[TResult, TError]":
def mapper(other_value):
try:
return self._value(other_value)
except TypeError:
return partial(self._value, other_value)
return something.map(mapper)
# Monad Section
# =============
@classmethod
def unit(cls, value: TSource) -> "Right[TSource, TError]":
return Right(value)
def bind(self, func: Callable[[TSource], Either[TResult, TError]]) -> Either[TResult, TError]:
return func(self._value)
# Operator Overloads
# ==================
def __eq__(self, other) -> bool:
return isinstance(other, Right) and self._value == other._value
def __str__(self) -> str:
return "Right %s" % self._value
class Left(Either[TSource, TError]):
"""Represents a computation that has failed."""
def __init__(self, error: TError) -> None:
self._error = error
@classmethod
def pure(cls, value: Callable[[TSource], TResult]) -> Either[Callable[[TSource], TResult], TError]:
return Right(value)
def apply(self, something: Either) -> Either:
return Left(self._error)
def map(self, mapper: Callable[[TSource], TResult]) -> Either[TResult, TError]:
return Left(self._error)
@classmethod
def unit(cls, value: TSource):
return Right(value)
def bind(self, func: Callable[[TSource], Either[TResult, TError]]) -> Either[TResult, TError]:
return Left(self._error)
def __eq__(self, other) -> bool:
return isinstance(other, Left) and self._error == other._error
def __str__(self) -> str:
return "Left: %s" % self._error
assert(isinstance(Either, Functor))
assert(isinstance(Either, Applicative))
assert(isinstance(Either, Monad))
assert(isinstance(Right, Functor))
assert(isinstance(Right, Applicative))
assert(isinstance(Right, Monad))
assert(isinstance(Left, Functor))
assert(isinstance(Left, Applicative))
assert(isinstance(Left, Monad))