| Safe Haskell | None | 
|---|---|
| Language | Haskell2010 | 
Divisible
Synopsis
- class Contravariant f => Divisible (f :: * -> *) where
- divided :: Divisible f => f a -> f b -> f (a, b)
- conquered :: Divisible f => f ()
- contrazip3 :: Divisible f => f a1 -> f a2 -> f a3 -> f (a1, a2, a3)
- contrazip4 :: Divisible f => f a1 -> f a2 -> f a3 -> f a4 -> f (a1, a2, a3, a4)
- contrazip5 :: Divisible f => f a1 -> f a2 -> f a3 -> f a4 -> f a5 -> f (a1, a2, a3, a4, a5)
- data Supplied (divisible :: * -> *) where
Documentation
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
 bs into as (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
 Strings and Ints. How can we do this? Simply run both serializer 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 acts as an identity for combining Divisible functors.
Instances
contrazip3 :: Divisible f => f a1 -> f a2 -> f a3 -> f (a1, a2, a3) #
contrazip4 :: Divisible f => f a1 -> f a2 -> f a3 -> f a4 -> f (a1, a2, a3, a4) #
contrazip5 :: Divisible f => f a1 -> f a2 -> f a3 -> f a4 -> f a5 -> f (a1, a2, a3, a4, a5) #