1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
|
from collections.abc import Callable, Sequence
from typing import ClassVar, TypeVar, final
from typing_extensions import Never
from returns.interfaces import applicative, bindable
from returns.primitives.asserts import assert_equal
from returns.primitives.hkt import KindN
from returns.primitives.laws import (
Law,
Law1,
Law3,
Lawful,
LawSpecDef,
law_definition,
)
_FirstType = TypeVar('_FirstType')
_SecondType = TypeVar('_SecondType')
_ThirdType = TypeVar('_ThirdType')
# Only used in laws:
_NewType1 = TypeVar('_NewType1')
_NewType2 = TypeVar('_NewType2')
@final
class _LawSpec(LawSpecDef):
"""
Container laws.
Definition: https://wiki.haskell.org/Monad_laws
Good explanation: https://bit.ly/2Qsi5re
"""
__slots__ = ()
@law_definition
def left_identity_law(
raw_value: _FirstType,
container: 'ContainerN[_FirstType, _SecondType, _ThirdType]',
function: Callable[
[_FirstType],
KindN['ContainerN', _NewType1, _SecondType, _ThirdType],
],
) -> None:
"""
Left identity.
The first law states that if we take a value, put it in a default
context with return and then feed it to a function by using ``bind``,
it's the same as just taking the value and applying the function to it.
"""
assert_equal(
container.from_value(raw_value).bind(function),
function(raw_value),
)
@law_definition
def right_identity_law(
container: 'ContainerN[_FirstType, _SecondType, _ThirdType]',
) -> None:
"""
Right identity.
The second law states that if we have a container value
and we use ``bind`` to feed it to ``.from_value``,
the result is our original container value.
"""
assert_equal(
container,
container.bind(
container.from_value,
),
)
@law_definition
def associative_law(
container: 'ContainerN[_FirstType, _SecondType, _ThirdType]',
first: Callable[
[_FirstType],
KindN['ContainerN', _NewType1, _SecondType, _ThirdType],
],
second: Callable[
[_NewType1],
KindN['ContainerN', _NewType2, _SecondType, _ThirdType],
],
) -> None:
"""
Associativity law.
The final monad law says that when
we have a chain of container functions applications with ``bind``,
it shouldn't matter how they're nested.
"""
assert_equal(
container.bind(first).bind(second),
container.bind(lambda inner: first(inner).bind(second)),
)
class ContainerN(
applicative.ApplicativeN[_FirstType, _SecondType, _ThirdType],
bindable.BindableN[_FirstType, _SecondType, _ThirdType],
Lawful['ContainerN[_FirstType, _SecondType, _ThirdType]'],
):
"""
Handy alias for types with ``.bind``, ``.map``, and ``.apply`` methods.
Should be a base class for almost any containers you write.
See also:
- https://bit.ly/2CTEVov
"""
__slots__ = ()
_laws: ClassVar[Sequence[Law]] = (
Law3(_LawSpec.left_identity_law),
Law1(_LawSpec.right_identity_law),
Law3(_LawSpec.associative_law),
)
#: Type alias for kinds with one type argument.
Container1 = ContainerN[_FirstType, Never, Never]
#: Type alias for kinds with two type arguments.
Container2 = ContainerN[_FirstType, _SecondType, Never]
#: Type alias for kinds with three type arguments.
Container3 = ContainerN[_FirstType, _SecondType, _ThirdType]
|