contravariant-1.5.5: Contravariant functors

Copyright | (C) 2014-2015 Edward Kmett
License | BSD-style (see the file LICENSE)
Maintainer | Edward Kmett <ekmett@gmail.com>
Stability | provisional
Portability | portable
Safe Haskell | Safe
Language | Haskell2010

Data.Functor.Contravariant.Divisible

Contents

- Contravariant Applicative
- Contravariant Alternative
- Mathematical definitions
  - Divisible
    - A note on `conquer`
  - Decidable

Description

This module supplies contravariant analogues to the `Applicative` and `Alternative` classes.

Synopsis

- class Contravariant f => Divisible f where
  - divide :: (a -> (b, c)) -> f b -> f c -> f a
  - conquer :: f a
- divided :: Divisible f => f a -> f b -> f (a, b)
- conquered :: Divisible f => f ()
- liftD :: Divisible f => (a -> b) -> f b -> f a
- class Divisible f => Decidable f where
  - lose :: (a -> Void) -> f a
  - choose :: (a -> Either b c) -> f b -> f c -> f a
- chosen :: Decidable f => f b -> f c -> f (Either b c)
- lost :: Decidable f => f Void

# Contravariant Applicative

class Contravariant f => Divisible f where #

A `Divisible` contravariant functor is the contravariant analogue of `Applicative`.

Continuing the intuition that `Contravariant` functors consume input, a `Divisible` contravariant functor also has the ability to be composed "beside" another contravariant functor.

Serializers provide a good example of `Divisible` contravariant functors. To begin let's start with the type of serializers for specific types:

    newtype Serializer a = Serializer { runSerializer :: a -> ByteString }

This is a contravariant functor:

    instance Contravariant Serializer where
      contramap f s = Serializer (runSerializer s . f)

That is, given a serializer for `a` (`s :: Serializer a`), and a way to turn `b`s into `a`s (a mapping `f :: b -> a`), we have a serializer for `b`: `contramap f s :: Serializer b`.

Divisible gives us a way to combine two serializers that focus on different parts of a structure. If we postulate the existance of two primitive serializers - `string :: Serializer String` and `int :: Serializer Int`, we would like to be able to combine these into a serializer for pairs of `String`s and `Int`s. How can we do this? Simply run both serializers and combine their output!

    data StringAndInt = StringAndInt String Int

    stringAndInt :: Serializer StringAndInt
    stringAndInt = Serializer $ \(StringAndInt s i) ->
      let sBytes = runSerializer string s
          iBytes = runSerializer int i
      in sBytes <> iBytes

`divide` is a generalization by also taking a `contramap` like function to split any `a` into a pair. This conveniently allows you to target fields of a record, for instance, by extracting the values under two fields and combining them into a tuple.

To complete the example, here is how to write `stringAndInt` using a `Divisible` instance:

    instance Divisible Serializer where
      conquer = Serializer (const mempty)

      divide toBC bSerializer cSerializer = Serializer $ \a ->
        case toBC a of
          (b, c) ->
            let bBytes = runSerializer bSerializer b
                cBytes = runSerializer cSerializer c
            in bBytes <> cBytes

    stringAndInt :: Serializer StringAndInt
    stringAndInt =
      divide (\(StringAndInt s i) -> (s, i)) string int

Methods

divide :: (a -> (b, c)) -> f b -> f c -> f a #

conquer :: f a #

Conquer acts as an identity for combining `Divisible` functors.

#### Instances

Instances details

Divisible SettableStateVar # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> SettableStateVar b -> SettableStateVar c -> SettableStateVar a # conquer :: SettableStateVar a #
Divisible Predicate # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Predicate b -> Predicate c -> Predicate a # conquer :: Predicate a #
Divisible Comparison # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Comparison b -> Comparison c -> Comparison a # conquer :: Comparison a #
Divisible Equivalence # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Equivalence b -> Equivalence c -> Equivalence a # conquer :: Equivalence a #
Divisible (U1 :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> U1 b -> U1 c -> U1 a # conquer :: U1 a #
Monoid r => Divisible (Op r) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Op r b -> Op r c -> Op r a # conquer :: Op r a #
Divisible (Proxy :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Proxy b -> Proxy c -> Proxy a # conquer :: Proxy a #
Divisible m => Divisible (MaybeT m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> MaybeT m b -> MaybeT m c -> MaybeT m a # conquer :: MaybeT m a #
Divisible m => Divisible (ListT m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> ListT m b -> ListT m c -> ListT m a # conquer :: ListT m a #
Divisible f => Divisible (Rec1 f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Rec1 f b -> Rec1 f c -> Rec1 f a # conquer :: Rec1 f a #
Monoid m => Divisible (Const m :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Const m b -> Const m c -> Const m a # conquer :: Const m a #
Divisible f => Divisible (Alt f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Alt f b -> Alt f c -> Alt f a # conquer :: Alt f a #
Divisible f => Divisible (Reverse f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Reverse f b -> Reverse f c -> Reverse f a # conquer :: Reverse f a #
Monoid m => Divisible (Constant m :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Constant m b -> Constant m c -> Constant m a # conquer :: Constant m a #
Divisible m => Divisible (WriterT w m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> WriterT w m b -> WriterT w m c -> WriterT w m a # conquer :: WriterT w m a #
Divisible m => Divisible (WriterT w m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> WriterT w m b -> WriterT w m c -> WriterT w m a # conquer :: WriterT w m a #
Divisible m => Divisible (StateT s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> StateT s m b -> StateT s m c -> StateT s m a # conquer :: StateT s m a #
Divisible m => Divisible (StateT s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> StateT s m b -> StateT s m c -> StateT s m a # conquer :: StateT s m a #
Divisible m => Divisible (ReaderT r m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> ReaderT r m b -> ReaderT r m c -> ReaderT r m a # conquer :: ReaderT r m a #
Divisible f => Divisible (IdentityT f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> IdentityT f b -> IdentityT f c -> IdentityT f a # conquer :: IdentityT f a #
Divisible m => Divisible (ExceptT e m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> ExceptT e m b -> ExceptT e m c -> ExceptT e m a # conquer :: ExceptT e m a #
Divisible m => Divisible (ErrorT e m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> ErrorT e m b -> ErrorT e m c -> ErrorT e m a # conquer :: ErrorT e m a #
Divisible f => Divisible (Backwards f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Backwards f b -> Backwards f c -> Backwards f a # conquer :: Backwards f a #
(Divisible f, Applicative g) => Divisible (ComposeCF f g) # |
Instance details Defined in Data.Functor.Contravariant.Compose Methods divide :: (a -> (b, c)) -> ComposeCF f g b -> ComposeCF f g c -> ComposeCF f g a # conquer :: ComposeCF f g a #
(Applicative f, Divisible g) => Divisible (ComposeFC f g) # |
Instance details Defined in Data.Functor.Contravariant.Compose Methods divide :: (a -> (b, c)) -> ComposeFC f g b -> ComposeFC f g c -> ComposeFC f g a # conquer :: ComposeFC f g a #
(Divisible f, Divisible g) => Divisible (f :*: g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> (f :*: g) b -> (f :*: g) c -> (f :*: g) a # conquer :: (f :*: g) a #
(Divisible f, Divisible g) => Divisible (Product f g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Product f g b -> Product f g c -> Product f g a # conquer :: Product f g a #
Divisible f => Divisible (M1 i c f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c0)) -> M1 i c f b -> M1 i c f c0 -> M1 i c f a # conquer :: M1 i c f a #
(Applicative f, Divisible g) => Divisible (f :.: g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> (f :.: g) b -> (f :.: g) c -> (f :.: g) a # conquer :: (f :.: g) a #
(Applicative f, Divisible g) => Divisible (Compose f g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> Compose f g b -> Compose f g c -> Compose f g a # conquer :: Compose f g a #
Divisible m => Divisible (RWST r w s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> RWST r w s m b -> RWST r w s m c -> RWST r w s m a # conquer :: RWST r w s m a #
Divisible m => Divisible (RWST r w s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods divide :: (a -> (b, c)) -> RWST r w s m b -> RWST r w s m c -> RWST r w s m a # conquer :: RWST r w s m a #

divided :: Divisible f => f a -> f b -> f (a, b) #

    divided = divide id

conquered :: Divisible f => f () #

Redundant, but provided for symmetry.

    conquered = conquer

liftD :: Divisible f => (a -> b) -> f b -> f a #

This is the divisible analogue of `liftA`. It gives a viable default definition for `contramap` in terms of the members of `Divisible`.

    liftD f = divide ((,) () . f) conquer

# Contravariant Alternative

class Divisible f => Decidable f where #

A `Decidable` contravariant functor is the contravariant analogue of `Alternative`.

Noting the superclass constraint that `f` must also be `Divisible`, a `Decidable` functor has the ability to "fan out" input, under the intuition that contravariant functors consume input.

In the discussion for `Divisible`, an example was demonstrated with `Serializer`s, that turn `a`s into `ByteString`s. `Divisible` allowed us to serialize the product of multiple values by concatenation. By making our `Serializer` also `Decidable`- we now have the ability to serialize the sum of multiple values - for example different constructors in an ADT.

Consider serializing arbitrary identifiers that can be either `String`s or `Int`s:

    data Identifier = StringId String | IntId Int

We know we have serializers for `String`s and `Int`s, but how do we combine them into a `Serializer` for `Identifier`? Essentially, our `Serializer` needs to scrutinise the incoming value and choose how to serialize it:

    identifier :: Serializer Identifier
    identifier = Serializer $ \identifier ->
      case identifier of
        StringId s -> runSerializer string s
        IntId i -> runSerializer int i

It is exactly this notion of choice that `Decidable` encodes. Hence if we add an instance of `Decidable` for `Serializer`...

    instance Decidable Serializer where
      lose f = Serializer $ \a -> absurd (f a)
      choose split l r = Serializer $ \a ->
        either (runSerializer l) (runSerializer r) (split a)

Then our `identifier` `Serializer` is

    identifier :: Serializer Identifier
    identifier = choose toEither string int where
      toEither (StringId s) = Left s
      toEither (IntId i) = Right i

Methods

lose :: (a -> Void) -> f a #

Acts as identity to `choose`.

choose :: (a -> Either b c) -> f b -> f c -> f a #

#### Instances

Instances details

Decidable SettableStateVar # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> SettableStateVar a # choose :: (a -> Either b c) -> SettableStateVar b -> SettableStateVar c -> SettableStateVar a #
Decidable Predicate # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Predicate a # choose :: (a -> Either b c) -> Predicate b -> Predicate c -> Predicate a #
Decidable Comparison # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Comparison a # choose :: (a -> Either b c) -> Comparison b -> Comparison c -> Comparison a #
Decidable Equivalence # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Equivalence a # choose :: (a -> Either b c) -> Equivalence b -> Equivalence c -> Equivalence a #
Decidable (U1 :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> U1 a # choose :: (a -> Either b c) -> U1 b -> U1 c -> U1 a #
Monoid r => Decidable (Op r) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Op r a # choose :: (a -> Either b c) -> Op r b -> Op r c -> Op r a #
Decidable (Proxy :: Type -> Type) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Proxy a # choose :: (a -> Either b c) -> Proxy b -> Proxy c -> Proxy a #
Divisible m => Decidable (MaybeT m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> MaybeT m a # choose :: (a -> Either b c) -> MaybeT m b -> MaybeT m c -> MaybeT m a #
Divisible m => Decidable (ListT m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> ListT m a # choose :: (a -> Either b c) -> ListT m b -> ListT m c -> ListT m a #
Decidable f => Decidable (Rec1 f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Rec1 f a # choose :: (a -> Either b c) -> Rec1 f b -> Rec1 f c -> Rec1 f a #
Decidable f => Decidable (Alt f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Alt f a # choose :: (a -> Either b c) -> Alt f b -> Alt f c -> Alt f a #
Decidable f => Decidable (Reverse f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Reverse f a # choose :: (a -> Either b c) -> Reverse f b -> Reverse f c -> Reverse f a #
Decidable m => Decidable (WriterT w m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> WriterT w m a # choose :: (a -> Either b c) -> WriterT w m b -> WriterT w m c -> WriterT w m a #
Decidable m => Decidable (WriterT w m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> WriterT w m a # choose :: (a -> Either b c) -> WriterT w m b -> WriterT w m c -> WriterT w m a #
Decidable m => Decidable (StateT s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> StateT s m a # choose :: (a -> Either b c) -> StateT s m b -> StateT s m c -> StateT s m a #
Decidable m => Decidable (StateT s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> StateT s m a # choose :: (a -> Either b c) -> StateT s m b -> StateT s m c -> StateT s m a #
Decidable m => Decidable (ReaderT r m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> ReaderT r m a # choose :: (a -> Either b c) -> ReaderT r m b -> ReaderT r m c -> ReaderT r m a #
Decidable f => Decidable (IdentityT f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> IdentityT f a # choose :: (a -> Either b c) -> IdentityT f b -> IdentityT f c -> IdentityT f a #
Decidable f => Decidable (Backwards f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Backwards f a # choose :: (a -> Either b c) -> Backwards f b -> Backwards f c -> Backwards f a #
(Applicative f, Decidable g) => Decidable (ComposeFC f g) # |
Instance details Defined in Data.Functor.Contravariant.Compose Methods lose :: (a -> Void) -> ComposeFC f g a # choose :: (a -> Either b c) -> ComposeFC f g b -> ComposeFC f g c -> ComposeFC f g a #
(Decidable f, Decidable g) => Decidable (f :*: g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> (f :*: g) a # choose :: (a -> Either b c) -> (f :*: g) b -> (f :*: g) c -> (f :*: g) a #
(Decidable f, Decidable g) => Decidable (Product f g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Product f g a # choose :: (a -> Either b c) -> Product f g b -> Product f g c -> Product f g a #
Decidable f => Decidable (M1 i c f) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> M1 i c f a # choose :: (a -> Either b c0) -> M1 i c f b -> M1 i c f c0 -> M1 i c f a #
(Applicative f, Decidable g) => Decidable (f :.: g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> (f :.: g) a # choose :: (a -> Either b c) -> (f :.: g) b -> (f :.: g) c -> (f :.: g) a #
(Applicative f, Decidable g) => Decidable (Compose f g) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> Compose f g a # choose :: (a -> Either b c) -> Compose f g b -> Compose f g c -> Compose f g a #
Decidable m => Decidable (RWST r w s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> RWST r w s m a # choose :: (a -> Either b c) -> RWST r w s m b -> RWST r w s m c -> RWST r w s m a #
Decidable m => Decidable (RWST r w s m) # |
Instance details Defined in Data.Functor.Contravariant.Divisible Methods lose :: (a -> Void) -> RWST r w s m a # choose :: (a -> Either b c) -> RWST r w s m b -> RWST r w s m c -> RWST r w s m a #

chosen :: Decidable f => f b -> f c -> f (Either b c) #

    chosen = choose id

lost :: Decidable f => f Void #

    lost = lose id

# Mathematical definitions

## Divisible

In denser jargon, a `Divisible` contravariant functor is a monoid object in the category of presheaves from Hask to Hask, equipped with Day convolution mapping the Cartesian product of the source to the Cartesian product of the target.

By way of contrast, an `Applicative` functor can be viewed as a monoid object in the category of copresheaves from Hask to Hask, equipped with Day convolution mapping the Cartesian product of the source to the Cartesian product of the target.

Given the canonical diagonal morphism:

    delta a = (a,a)

``divide` `delta`` should be associative with `conquer` as a unit

    divide delta m conquer = m
    divide delta conquer m = m
    divide delta (divide delta m n) o = divide delta m (divide delta n o)

With more general arguments you'll need to reassociate and project using the monoidal structure of the source category. (Here fst and snd are used in lieu of the more restricted lambda and rho, but this construction works with just a monoidal category.)

    divide f m conquer = contramap (fst . f) m
    divide f conquer m = contramap (snd . f) m
    divide f (divide g m n) o = divide f' m (divide id n o) where
      f' a = let (bc, d) = f a; (b, c) = g bc in (b, (c, d))

### A note on `conquer`

The underlying theory would suggest that this should be:

    conquer :: (a -> ()) -> f a

However, as we are working over a Cartesian category (Hask) and the Cartesian product, such an input morphism is uniquely determined to be ``const` `mempty``, so we elide it.

## Decidable

A `Divisible` contravariant functor is a monoid object in the category of presheaves from Hask to Hask, equipped with Day convolution mapping the cartesian product of the source to the Cartesian product of the target.

    choose Left m (lose f)  = m
    choose Right (lose f) m = m
    choose f (choose g m n) o = choose f' m (choose id n o) where
      f' = either (either id Left . g) (Right . Right) . f

In addition, we expect the same kind of distributive law as is satisfied by the usual covariant `Alternative`, w.r.t `Applicative`, which should be fully formulated and added here at some point!