def right_identity( 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(lambda inner: container.from_value(inner), ), )
def composition_law( container: 'ApplicativeN[_FirstType, _SecondType, _ThirdType]', first: Callable[[_FirstType], _NewType1], second: Callable[[_NewType1], _NewType2], ) -> None: """ Composition law. Apply two functions twice is the same as applying their composition once. """ assert_equal( container.apply(container.from_value(compose(first, second))), container.apply(container.from_value(first), ).apply( container.from_value(second), ), )
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), )
def left_identity( 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), )
def homomorphism_law( raw_value: _FirstType, container: 'ApplicativeN[_FirstType, _SecondType, _ThirdType]', function: Callable[[_FirstType], _NewType1], ) -> None: """ Homomorphism law. The homomorphism law says that applying a wrapped function to a wrapped value is the same as applying the function to the value in the normal way and then using ``.from_value`` on the result. """ assert_equal( container.from_value(function(raw_value)), container.from_value(raw_value).apply( container.from_value(function), ), )
def associativity( 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)), )
def interchange_law( raw_value: _FirstType, container: 'ApplicativeN[_FirstType, _SecondType, _ThirdType]', function: Callable[[_FirstType], _NewType1], ) -> None: """ Interchange law. Basically we check that we can start our composition with both ``raw_value`` and ``function``. Great explanation: https://stackoverflow.com/q/27285918/4842742 """ assert_equal( container.from_value(raw_value).apply( container.from_value(function), ), container.from_value(function).apply( container.from_value(lambda inner: inner(raw_value)), ), )
def identity_law( mappable: 'MappableN[_FirstType, _SecondType, _ThirdType]', ) -> None: """Mapping identity over a value must return the value unchanged.""" assert_equal(mappable.map(identity), mappable)
def test_assert_equal(container, anyio_backend_name: str): """Ensure that containers can be equal.""" assert_equal(container, container, backend=anyio_backend_name)