-- | 'Cofunctor' is a structure from category theory dual to 'Functor'
--
-- A 'Functor' is defined by the operation 'fmap':
--
-- > fmap :: (a -> b) -> (f a -> f b)
--
-- This means that its dual must be defined by the following operation:
--
-- > cofmap :: (b -> a) -> (f b -> f a)
--
-- Since beginning his investigations, the author of this package has discovered
-- that this pattern is /at least/ as commonly used as 'Functor'. In fact, many
-- ubiquitous Haskell types (e.g. @[]@, 'Maybe', @((->) a)@ turn out to have a
-- 'Cofunctor' instance.
module Data.Cofunctor
    ( Cofunctor (..)
    ) where

import Control.Monad (liftM)

-- | 'Cofunctor' is a structure from category theory dual to 'Functor'
class Cofunctor f where
    cofmap :: (b -> a) -> f b -> f a

instance Cofunctor [] where
    cofmap _ []       = []
    cofmap f (x : xs) = f x : cofmap f xs

instance Cofunctor Maybe where
    cofmap _ Nothing  = Nothing
    cofmap f (Just x) = Just (f x)

instance Cofunctor (Either e) where
    cofmap _ (Left e)  = Left e
    cofmap f (Right x) = Right (f x)

instance Cofunctor ((->) a) where
    cofmap = (.)

instance Cofunctor ((,) a) where
    cofmap f (e, x) = (e, f x)

instance Cofunctor IO where
    cofmap = liftM