Safe Haskell | None |
---|---|
Language | Haskell2010 |
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
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 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
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) #