A contravariant functor is a functor from one category into its opposite category, i.e. from one category into another (albeit closely related) one. OTOH, a monad is foremostly an endofunctor i.e. from one category into itself. So it can't be contravariant.
This kind of stuff always tends to be a lot clearer when you consider the “fundamental mathematical” definition of monads:
class Functor m => Monad m where
pure :: a -> m a
join :: m (m a) -> m a
As you see there aren't really any arrows in there that you could turn around in the result, like you did with contrabind
. Of course there is
class Functor n => Comonad n where
extract :: n a -> a
duplicate :: n a -> n (n a)
but comonads are still covariant functors.
Unlike monads, applicatives (monoidal functors) needn't be endofunctors, so I believe these can be turned around. Let's start from the “fundamental” definition:
class Functor f => Monoidal f where
pureUnit :: () -> f ()
fzipWith :: ((a,b)->c) -> (f a, f b)->f c -- I avoid currying to make it clear what the arrows are.
(exercise: define a derived Applicative
instance in terms of this, and vice versa)
Turning it around
class Contravariant f => ContraApp f where
pureDisunit :: f () -> ()
fcontraunzip :: ((a,b)->c) -> f c->(f a, f b)
-- I'm not sure, maybe this should
-- be `f c -> Either (f a) (f b)` instead.
No idea how useful that would be. pureDisunit
is certainly not useful, because its only implementation is always const ()
.
Let's try writing the obvious instance:
newtype Opp a b = Opp { getOpp :: b -> a }
instance Contravariant (Opp a) where
contramap f (Opp g) = Opp $ g . f
instance ContraApp (Opp a) where
pureDisunit = const ()
fcontraunzip z (Opp g)
= (Opp $ \a -> ???, Opp $ \b -> ???) -- `z` needs both `a` and `b`, can't get it!
I don't think this is useful, though you might be able to define it with something like clever knot-tying recursion.
What might be more interesting is a contravariant co-monoidal functor, but this gets too weird for me right now.