{-# LANGUAGE GeneralizedNewtypeDeriving #-}

module Control.Category.Dual where

import Prelude (Eq, Ord, Read, Show, Bounded, ($))

import Control.Category
import Data.Bifunctor
import Data.Bifoldable
import Data.Bitraversable
import Data.Functor
import Data.Functor.Classes
import Data.Semigroup (Semigroup)
import Data.Monoid (Monoid)

newtype Dual k a b = Dual { dual :: k b a }
  deriving (Eq, Ord, Read, Show, Bounded, Semigroup, Monoid)

instance Category k => Category (Dual k) where
    id = Dual id
    Dual f . Dual g = Dual (g . f)

instance Eq2 k => Eq2 (Dual k) where liftEq2 f g (Dual x) (Dual y) = liftEq2 g f x y
instance Ord2 k => Ord2 (Dual k) where liftCompare2 f g (Dual x) (Dual y) = liftCompare2 g f x y

instance Read2 k => Read2 (Dual k) where
    liftReadsPrec2 arp arl brp brl =
        readsData $ readsUnaryWith (liftReadsPrec2 brp brl arp arl) "Dual" Dual

instance Show2 k => Show2 (Dual k) where
    liftShowsPrec2 asp asl bsp bsl n =
        showsUnaryWith (liftShowsPrec2 bsp bsl asp asl) "Dual" n . dual

instance Bifunctor k => Bifunctor (Dual k) where
    bimap f g = Dual . bimap g f . dual

instance Bifoldable k => Bifoldable (Dual k) where
    bifold = bifold . dual
    bifoldMap f g = bifoldMap g f . dual

instance Bitraversable k => Bitraversable (Dual k) where
    bitraverse f g = fmap Dual . bitraverse g f . dual