Source code for returns.interfaces.applicative

from abc import abstractmethod
from typing import Callable, ClassVar, NoReturn, Sequence, Type, TypeVar

from typing_extensions import final

from returns.functions import compose, identity
from returns.interfaces import mappable
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')
_UpdatedType = TypeVar('_UpdatedType')

_ApplicativeType = TypeVar('_ApplicativeType', bound='ApplicativeN')

# Only used in laws:
_NewType1 = TypeVar('_NewType1')
_NewType2 = TypeVar('_NewType2')


[docs]@final class _LawSpec(LawSpecDef): """ Applicative mappable laws. Definition: https://bit.ly/3hC8F8E Discussion: https://bit.ly/3jffz3L """
[docs] @law_definition def identity_law( container: 'ApplicativeN[_FirstType, _SecondType, _ThirdType]', ) -> None: """ Identity law. If we apply wrapped ``identity`` function to a container, nothing happens. """ assert_equal( container, container.apply(container.from_value(identity)), )
[docs] @law_definition 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)), ), )
[docs] @law_definition 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), ), )
[docs] @law_definition def composition_law( container: 'ApplicativeN[_FirstType, _SecondType, _ThirdType]', first: Callable[[_FirstType], _NewType1], second: Callable[[_NewType1], _NewType2], ) -> None: """ Composition law. Appling 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), ), )
[docs]class ApplicativeN( mappable.MappableN[_FirstType, _SecondType, _ThirdType], Lawful['ApplicativeN[_FirstType, _SecondType, _ThirdType]'], ): """ Allows to create unit containers from raw values and to apply wrapped funcs. See also: - https://en.wikipedia.org/wiki/Applicative_functor - http://learnyouahaskell.com/functors-applicative-functors-and-monoids """ _laws: ClassVar[Sequence[Law]] = ( Law1(_LawSpec.identity_law), Law3(_LawSpec.interchange_law), Law3(_LawSpec.homomorphism_law), Law3(_LawSpec.composition_law), )
[docs] @abstractmethod def apply( self: _ApplicativeType, container: KindN[ _ApplicativeType, Callable[[_FirstType], _UpdatedType], _SecondType, _ThirdType, ], ) -> KindN[_ApplicativeType, _UpdatedType, _SecondType, _ThirdType]: """Allows to apply a wrapped function over a container."""
[docs] @classmethod @abstractmethod def from_value( cls: Type[_ApplicativeType], # noqa: N805 inner_value: _UpdatedType, ) -> KindN[_ApplicativeType, _UpdatedType, _SecondType, _ThirdType]: """Unit method to create new containers from any raw value."""
#: Type alias for kinds with one type argument. Applicative1 = ApplicativeN[_FirstType, NoReturn, NoReturn] #: Type alias for kinds with two type arguments. Applicative2 = ApplicativeN[_FirstType, _SecondType, NoReturn] #: Type alias for kinds with three type arguments. Applicative3 = ApplicativeN[_FirstType, _SecondType, _ThirdType]