-- {-# OPTIONS_GHC -fglasgow-exts -fallow-undecidable-instances #-}
-------------------------------------------------------------------------------------------
-- |
-- Module	: Control.Category.Associative
-- Copyright 	: 2008 Edward Kmett
-- License	: BSD
--
-- Maintainer	: Edward Kmett <ekmett@gmail.com>
-- Stability	: experimental
-- Portability	: portable
--
-- NB: this contradicts another common meaning for an 'Associative' 'Category', which is one 
-- where the pentagonal condition does not hold, but for which there is an identity.
--
-------------------------------------------------------------------------------------------
module Control.Category.Associative 
	( Associative(..)
	, Coassociative(..)
	) where

import Control.Functor
import Control.Category.Hask

{- | A category with an associative bifunctor satisfying Mac Lane\'s pentagonal coherence identity law:

> bimap id associate . associate . bimap associate id = associate . associate
-}
class Bifunctor p k k k => Associative k p where
	associate :: k (p (p a b) c) (p a (p b c))

{- | A category with a coassociative bifunctor satisyfing the dual of Mac Lane's pentagonal coherence identity law:

> bimap coassociate id . coassociate . bimap id coassociate = coassociate . coassociate
-}
class Bifunctor s k k k => Coassociative k s where
	coassociate :: k (s a (s b c)) (s (s a b) c)

{-# RULES
"copentagonal coherence" bimap coassociate id . coassociate . bimap id coassociate = coassociate . coassociate
"pentagonal coherence" bimap id associate . associate . bimap associate id = associate . associate
 #-}

instance Associative Hask (,) where
        associate ((a,b),c) = (a,(b,c))

instance Coassociative Hask (,) where
        coassociate (a,(b,c)) = ((a,b),c)

instance Associative Hask Either where
        associate (Left (Left a)) = Left a
        associate (Left (Right b)) = Right (Left b)
        associate (Right c) = Right (Right c)

instance Coassociative Hask Either where
        coassociate (Left a) = Left (Left a)
        coassociate (Right (Left b)) = Left (Right b)
        coassociate (Right (Right c)) = Right c