145 lines
4 KiB
Python
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))
|