Recall the Applicative class:
class Functor f => Applicative f where
pure :: a -> f a
liftA2 :: (a -> b -> c) -> f a -> f b -> f c
(<*>) :: f (a -> b) -> f a -> f b
liftA2 h x y = fmap h x <*> y
(<*>) = liftA2 id
Though it's not immediately clear how to express this class in terms of (mathematical) category theory, it becomes clear when the following function is defined:
liftZ2 :: Applicative f => (f a, f b) -> f (a, b)
liftZ2 (x, y) = liftA2 (,) x y
Here, (,) should be identifiable as the categorical product. Replacing products by coproducts and reversing all the arrows gives the following class:
class Functor f => Coapplicative f where
copure :: f a -> a
coliftZ2 :: f (Either a b) -> Either (f a) (f b)
Some instances:
import Data.Functor.Identity
import qualified Data.List.NonEmpty as NonEmpty
import Data.Either
instance Coapplicative Identity where
copure (Identity x) = x
coliftZ2 (Identity (Left x)) = Left (Identity x)
coliftZ2 (Identity (Right y)) = Right (Identity y)
instance Coapplicative NonEmpty.NonEmpty where
copure (x NonEmpty.:| _) = x
coliftZ2 (Left x NonEmpty.:| xs) = Left (x NonEmpty.:| lefts xs)
coliftZ2 (Right y NonEmpty.:| ys) = Right (y NonEmpty.:| rights ys)
instance Coapplicative ((,) e) where
copure (_, x) = x
coliftZ2 (e, Left x) = Left (e, x)
coliftZ2 (e, Right y) = Right (e, y)
instance Monoid m => Coapplicative ((->) m) where
copure f = f mempty
coliftZ2 f = case copure f of
Left x -> Left (fromLeft x . f)
Right y -> Right (fromRight y . f)
I have a strong intuition that Coapplicative is a superclass for Comonad, but I don't know how to prove it. Also, is there a Coapplicative instance that is not a Comonad?
This is not a complete answer, but I can at least make the case that your instance of
Coapplicative NonEmptycannot be derived fromComonadmethods alone; that is, if there is some parametric implementation ofthen it does not generate your instance for
NonEmpty. This is because the methods ofComonad NonEmptyalone do not afford us any way of changing the length of a list, but yourNonEmptyinstance ofcoliftZ2changes the length. So ifNonEmptyis to be aCoapplicative, it must do so in some other way, or elseCoapplicativecan't be a superclass ofComonad.As you investigate further, I would say it is worth exploring the comonad
and considering what its
coliftZ2implementation must be. You haven't given any laws forCoapplicative, but if you did, I would put my money on this breaking them because there is no implementation ofcoliftZ2which is satisfyingly symmetrical. Both of the following equations are rather forced, but they seem to indicate very different intentions for the operation:In my opinion the question that will shed the most light on this is: What should we expect of the relationship of
coliftZ2to the comonad methods?