Source code for haskpy.typeclasses._apply

from hypothesis import given
import hypothesis.strategies as st

from haskpy.testing import assert_output
from haskpy import testing
from haskpy.internal import class_function

# Use the "hidden" module in order to avoid circular imports
from ._functor import Functor


class Apply(Functor):
    """Apply typeclass

    :py:class:`.Apply` is to :py:class:`.Applicative` as :py:class:`.Semigroup`
    is to :py:class:`.Monoid`.

    Minimal complete definition::

        map | (apply | apply_to)

    Why do we need :py:class:`.Apply` in addition to :py:class:`.Applicative`?
    For instance, :py:class:`.Dictionary` is an instance of :py:class:`.Apply`
    but not :py:class:`.Applicative`.

    References
    ----------

    - `Apply at PureScript Pursuit
      <https://pursuit.purescript.org/packages/purescript-prelude/3.0.0/docs/Control.Apply>`_

    """

[docs] def apply(self, f): """m a -> m (a -> b) -> m b Default implementation is based on ``apply_to``. """ return f.apply_to(self)
[docs] def apply_to(self, x): """f (a -> b) -> f a -> f b Default implementation is based on ``apply``. """ return x.apply(self)
[docs] def apply_first(self, x): """Combine two actions, keeping only the result of the first :: Apply f => f a -> f b -> f a """ from haskpy.utils import const return self.map(const).apply_to(x)
[docs] def apply_second(self, x): """Combine two actions, keeping only the result of the second :: Apply f => f a -> f b -> f b """ from haskpy.utils import identity return self.replace(identity).apply_to(x)
[docs] def __matmul__(self, x): """Application operand ``@`` applies similarly as ``<*>`` in Haskell ``f @ x`` translates to ``f.apply_to(x)``, ``x.apply(f)`` and ``apply(f, x)``. Why ``@`` operator? - It's not typically used as often as some other more common operators so less risk for confusion. - The operator is not a commutative as isn't ``apply`` either. - If we see matrix as some structure, then matrix multiplication takes both left and right operand inside this structure and gives a result also inside this structure, similarly as ``apply`` does. So it's an operator for two operands having a similar structure. - The operator evaluates the contained function(s) at the contained value(s). Thus, ``f`` "at" ``x`` makes perfect sense. """ return self.apply_to(x)
[docs] def __lshift__(self, x): """Sequence with``<<`` similarly as with ``<*`` and ``<<`` in Haskell""" return self.apply_first(x)
[docs] def __rshift__(self, x): """Sequence with ``>>`` similarly as with ``*>`` and ``>>`` in Haskell""" return self.apply_second(x)
# # Sampling methods for property tests # @class_function def sample_apply_type_constructor(cls): """Sample an apply type constructor By default, :py:meth:`.Functor.sample_functor_type_constructor` is used. If Apply type requires more constraints than Functor type, override this default implementation. """ return cls.sample_functor_type_constructor() # # Test typeclass laws # @class_function @assert_output def assert_apply_associative_composition(cls, u, v, w): """u @ (v @ w) == ((compose ** u) @ v) @ w""" from haskpy.types.function import compose return ( u.apply_to(v.apply_to(w)), u.map(compose).apply_to(v).apply_to(w), ) @class_function @given(st.data()) def test_apply_associative_composition(cls, data): # Draw types a = data.draw(testing.sample_eq_type()) b = data.draw(testing.sample_eq_type()) c = data.draw(testing.sample_type()) f = data.draw(cls.sample_apply_type_constructor()) fa = f(a) fab = f(testing.sample_function(b)) fbc = f(testing.sample_function(c)) # Draw values w = data.draw(fa) v = data.draw(fab) u = data.draw(fbc) cls.assert_apply_associative_composition(u, v, w, data=data) return # # Test laws based on default implementations # @class_function @assert_output def assert_apply_apply(cls, u, v): from .apply_ import apply return ( v.apply(u), apply(u, v), u.apply_to(v), u @ v, ) @class_function @given(st.data()) def test_apply_apply(cls, data): # Draw types a = data.draw(testing.sample_eq_type()) b = data.draw(testing.sample_type()) f = data.draw(cls.sample_apply_type_constructor()) fa = f(a) fab = f(testing.sample_function(b)) # Draw values v = data.draw(fa) u = data.draw(fab) cls.assert_apply_apply(u, v, data=data) return @class_function @assert_output def assert_apply_apply_first(cls, u, v): from .apply_ import apply_first return ( Apply.apply_first(u, v), u.apply_first(v), apply_first(u, v), u << v, ) @class_function @given(st.data()) def test_apply_apply_first(cls, data): # Draw types a = data.draw(testing.sample_type()) b = data.draw(testing.sample_type()) f = data.draw(cls.sample_apply_type_constructor()) fa = f(a) fb = f(b) # Draw values u = data.draw(fa) v = data.draw(fb) cls.assert_apply_apply_first(u, v, data=data) return @class_function @assert_output def assert_apply_apply_second(cls, u, v): from .apply_ import apply_second return ( Apply.apply_second(u, v), u.apply_second(v), apply_second(u, v), u >> v, ) @class_function @given(st.data()) def test_apply_apply_second(cls, data): # Draw types a = data.draw(testing.sample_type()) b = data.draw(testing.sample_type()) f = data.draw(cls.sample_apply_type_constructor()) fa = f(a) fb = f(b) # Draw values u = data.draw(fa) v = data.draw(fb) cls.assert_apply_apply_second(u, v, data=data) return