Source code for categories.applicative

from infix import make_infix

from categories import instances
from categories.utils import compose, id_
from categories.instances import Instances, Getter  # noqa: F401
from categories.instances import Adder, Undefiner  # noqa: F401


__instances = {}  # type: Instances
get_instance = instances.make_getter(__instances,
                                     'Applicative')  # type: Getter
_add_instance = instances.make_adder(__instances)  # type: Adder
undefine_instance = instances.make_undefiner(__instances)  # type: Undefiner


[docs]class Applicative: def __init__(self, pure, apply): self.pure = pure self.apply = apply
[docs]def instance(type, pure, apply): instance = Applicative(pure, apply) _add_instance(type, instance) return instance
[docs]def pure(x, _type, type_form=None): """ The optional type_form arg is an attempt to provide some more flexibility with the type that pure is required to cast to. For example, pure for the tuple Applicative instance needs to know the types of its elements so it can return the correct object: pure(7, tuple, type_form=(str, int)) == ("", 7) """ kwargs = {'type_form': type_form} if type_form else {} instance = get_instance(_type) return instance.pure(x, **kwargs)
@make_infix('or') def apply(f, x): """ Same as Haskell's <*> operator :param f: A function contained in an Applicative :param x: A value contained in the Applicative :return: f(x) contained in the Applicative """ instance = get_instance(type(f)) return instance.apply(f, x) ap = apply
[docs]def identity_law(v, type_form=None): """ pure id <*> v == v where (<*>) = apply """ pure_args = {'type_form': type_form} if type_form else {} pure_id = pure(id_, type(v), **pure_args) return apply(pure_id, v) == v
[docs]def homomorphism_law(f, x, _type, type_form=None): """ pure f <*> pure x == pure (f x) where (<*>) = apply """ pure_args = {'type_form': type_form} if type_form else {} pure_f = pure(f, _type, **pure_args) pure_x = pure(x, _type, **pure_args) return apply(pure_f, pure_x) == pure(f(x), _type, **pure_args)
[docs]def interchange_law(u, y, type_form=None): """ u <*> pure y == pure ($ y) <*> u where (<*>) = apply """ pure_args = {'type_form': type_form} if type_form else {} pure_y = pure(y, type(u), **pure_args) pure_dollar_y = pure(lambda f: f(y), type(u), **pure_args) return apply(u, pure_y) == apply(pure_dollar_y, u)
[docs]def composition_law(u, v, w, type_form=None): # pragma: no cover """ pure (.) <*> u <*> v <*> w == u <*> (v <*> w) where (<*>) = apply """ raise NotImplementedError("Applicative composition law not implemented") # TODO: having some problems here because of not being able to partially # apply functions wrapped in Applicative. This may not actually # be possible without some sort of hack or workaround pure_compose = pure(compose, type(u)) return apply(apply(apply(pure_compose, u), v), w) == \ apply(u, apply(v, w))