from abc import abstractmethod
from typing import Callable, ClassVar, NoReturn, Sequence, Type, TypeVar
from typing_extensions import final
from returns.interfaces import container as _container
from returns.interfaces import lashable, swappable
from returns.primitives.asserts import assert_equal
from returns.primitives.hkt import KindN
from returns.primitives.laws import (
Law,
Law2,
Law3,
Lawful,
LawSpecDef,
law_definition,
)
_FirstType = TypeVar('_FirstType')
_SecondType = TypeVar('_SecondType')
_ThirdType = TypeVar('_ThirdType')
_UpdatedType = TypeVar('_UpdatedType')
_SingleFailableType = TypeVar('_SingleFailableType', bound='SingleFailableN')
_DiverseFailableType = TypeVar('_DiverseFailableType', bound='DiverseFailableN')
# Used in laws:
_NewFirstType = TypeVar('_NewFirstType')
_NewSecondType = TypeVar('_NewSecondType')
[docs]@final
class _FailableLawSpec(LawSpecDef):
"""
Failable laws.
We need to be sure that ``.lash`` won't lash success types.
"""
[docs] @law_definition
def lash_short_circuit_law(
raw_value: _FirstType,
container: 'FailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[
[_SecondType],
KindN['FailableN', _FirstType, _NewFirstType, _ThirdType],
],
) -> None:
"""Ensures that you cannot lash a success."""
assert_equal(
container.from_value(raw_value),
container.from_value(raw_value).lash(function),
)
[docs]class FailableN(
_container.ContainerN[_FirstType, _SecondType, _ThirdType],
lashable.LashableN[_FirstType, _SecondType, _ThirdType],
Lawful['FailableN[_FirstType, _SecondType, _ThirdType]'],
):
"""
Base type for types that can fail.
It is a raw type and should not be used directly.
Use ``SingleFailableN`` and ``DiverseFailableN`` instead.
"""
_laws: ClassVar[Sequence[Law]] = (
Law3(_FailableLawSpec.lash_short_circuit_law),
)
#: Type alias for kinds with two type arguments.
Failable2 = FailableN[_FirstType, _SecondType, NoReturn]
#: Type alias for kinds with three type arguments.
Failable3 = FailableN[_FirstType, _SecondType, _ThirdType]
[docs]@final
class _SingleFailableLawSpec(LawSpecDef):
"""
Single Failable laws.
We need to be sure that ``.map`` and ``.bind``
works correctly for ``empty`` property.
"""
[docs] @law_definition
def map_short_circuit_law(
container: 'SingleFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[[_FirstType], _NewFirstType],
) -> None:
"""Ensures that you cannot map from the `empty` property."""
assert_equal(
container.empty,
container.empty.map(function),
)
[docs] @law_definition
def bind_short_circuit_law(
container: 'SingleFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[
[_FirstType],
KindN['SingleFailableN', _NewFirstType, _SecondType, _ThirdType],
],
) -> None:
"""Ensures that you cannot bind from the `empty` property."""
assert_equal(
container.empty,
container.empty.bind(function),
)
[docs] @law_definition
def apply_short_circuit_law(
container: 'SingleFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[[_FirstType], _NewFirstType],
) -> None:
"""Ensures that you cannot apply from the `empty` property."""
wrapped_function = container.from_value(function)
assert_equal(
container.empty,
container.empty.apply(wrapped_function),
)
[docs]class SingleFailableN(
FailableN[_FirstType, _SecondType, _ThirdType],
):
"""
Base type for types that have just only one failed value.
Like ``Maybe`` types where the only failed value is ``Nothing``.
"""
_laws: ClassVar[Sequence[Law]] = (
Law2(_SingleFailableLawSpec.map_short_circuit_law),
Law2(_SingleFailableLawSpec.bind_short_circuit_law),
Law2(_SingleFailableLawSpec.apply_short_circuit_law),
)
@property
@abstractmethod
def empty(
self: _SingleFailableType,
) -> 'SingleFailableN[_FirstType, _SecondType, _ThirdType]':
"""This property represents the failed value."""
#: Type alias for kinds with two types arguments.
SingleFailable2 = SingleFailableN[_FirstType, _SecondType, NoReturn]
#: Type alias for kinds with three type arguments.
SingleFailable3 = SingleFailableN[_FirstType, _SecondType, _ThirdType]
[docs]@final
class _DiverseFailableLawSpec(LawSpecDef):
"""
Diverse Failable laws.
We need to be sure that ``.map``, ``.bind``, ``.apply`` and ``.alt``
works correctly for both success and failure types.
"""
[docs] @law_definition
def map_short_circuit_law(
raw_value: _SecondType,
container: 'DiverseFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[[_FirstType], _NewFirstType],
) -> None:
"""Ensures that you cannot map a failure."""
assert_equal(
container.from_failure(raw_value),
container.from_failure(raw_value).map(function),
)
[docs] @law_definition
def bind_short_circuit_law(
raw_value: _SecondType,
container: 'DiverseFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[
[_FirstType],
KindN['DiverseFailableN', _NewFirstType, _SecondType, _ThirdType],
],
) -> None:
"""
Ensures that you cannot bind a failure.
See: https://wiki.haskell.org/Typeclassopedia#MonadFail
"""
assert_equal(
container.from_failure(raw_value),
container.from_failure(raw_value).bind(function),
)
[docs] @law_definition
def apply_short_circuit_law(
raw_value: _SecondType,
container: 'DiverseFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[[_FirstType], _NewFirstType],
) -> None:
"""Ensures that you cannot apply a failure."""
wrapped_function = container.from_value(function)
assert_equal(
container.from_failure(raw_value),
container.from_failure(raw_value).apply(wrapped_function),
)
[docs] @law_definition
def alt_short_circuit_law(
raw_value: _SecondType,
container: 'DiverseFailableN[_FirstType, _SecondType, _ThirdType]',
function: Callable[[_SecondType], _NewFirstType],
) -> None:
"""Ensures that you cannot alt a success."""
assert_equal(
container.from_value(raw_value),
container.from_value(raw_value).alt(function),
)
[docs]class DiverseFailableN(
FailableN[_FirstType, _SecondType, _ThirdType],
swappable.SwappableN[_FirstType, _SecondType, _ThirdType],
Lawful['DiverseFailableN[_FirstType, _SecondType, _ThirdType]'],
):
"""
Base type for types that have any failed value.
Like ``Result`` types.
"""
_laws: ClassVar[Sequence[Law]] = (
Law3(_DiverseFailableLawSpec.map_short_circuit_law),
Law3(_DiverseFailableLawSpec.bind_short_circuit_law),
Law3(_DiverseFailableLawSpec.apply_short_circuit_law),
Law3(_DiverseFailableLawSpec.alt_short_circuit_law),
)
[docs] @classmethod
@abstractmethod
def from_failure(
cls: Type[_DiverseFailableType],
inner_value: _UpdatedType,
) -> KindN[_DiverseFailableType, _FirstType, _UpdatedType, _ThirdType]:
"""Unit method to create new containers from any raw value."""
#: Type alias for kinds with two type arguments.
DiverseFailable2 = DiverseFailableN[_FirstType, _SecondType, NoReturn]
#: Type alias for kinds with three type arguments.
DiverseFailable3 = DiverseFailableN[_FirstType, _SecondType, _ThirdType]