ProductProfunctor is a generalization of
Applicative. It
has the usual
Applicative "output" (covariant) parameter on the
right. Additionally it has an "input" (contravariant) type parameter
on the left.
The methods for
ProductProfunctor correspond closely to those
for
Applicative as laid out in the following table. The only
difference between them is that the
ProductProfunctor has a
contravariant type parameter on the left. We can use the contravariant
to compose them in nice ways as described at
Data.Profunctor.Product.
| Correspondence between Applicative and ProductProfunctor
|
| Applicative f ProductProfunctor p
|
| pure purePP
| :: b -> f b :: b -> p a b
|
| (<$>) (***$)
| :: (b -> b') :: (b -> b')
| -> f b -> p a b
| -> f b' -> p a b'
|
| (<*>) (****)
| :: f (b -> b') :: p a (b -> b')
| -> f b -> p a b
| -> f b' -> p a b'
If
p is an instance of
ProductProfunctor then
p a
a' represents a sort of process for turning
as into
a's that can be "laid out side-by-side" with other values of
p to form "wider" processes. For example, if I have
p :: p a x -- a process for turning as into xs
q :: p b y -- a process for turning bs into ys
r :: p c z -- a process for turning cs into zs
then I can combine them using
p3 to get
p3 p q r :: p (a, b, c) (x, y, z)
-- a process for turning (a, b, c)s into (x, y, z)s
You would typically compose
ProductProfunctors using
Profunctors's
lmap and
Applicative's
pure,
<$> /
fmap and
<*>.
It's easy to make instances of
ProductProfunctor. Just make
instances
instance Profunctor MyProductProfunctor where
...
instance Applicative (MyProductProfunctor a) where
...
and then write
instance ProductProfunctor MyProductProfunctor where
purePP = pure
(****) = (<*>)