module Air.Data.Monoid where

import Prelude hiding ((+), Monoid, mempty, mappend)
import qualified Prelude as Prelude


import qualified Data.ByteString.Lazy as LB
import qualified Data.ByteString as B

import qualified Data.Map as Map
import qualified Data.Set as Set
import qualified Data.Sequence as Sequence

import Data.Int (Int8, Int16, Int32, Int64)
import Data.Word (Word8, Word16, Word32, Word64)

import qualified Data.Text as ST
import qualified Data.Text.Lazy as LT

class Monoid a where
        mempty  :: a
        -- ^ Identity of 'mappend'
        mappend :: a -> a -> a
        -- ^ An associative operation
        mconcat :: [a] -> a

        -- ^ Fold a list using the monoid.
        -- For most types, the default definition for 'mconcat' will be
        -- used, but the function is included in the class definition so
        -- that an optimized version can be provided for specific types.

        mconcat = foldr mappend mempty


(+) :: (Monoid a) => a -> a -> a
(+) = mappend

infixl 6 +


-- Monoid instances.


instance Monoid Int where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Integer where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Double where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Float where
  mempty = 0
  mappend = (Prelude.+)


instance Monoid Int8 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Int16 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Int32 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Int64 where
  mempty = 0
  mappend = (Prelude.+)


instance Monoid Word8 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Word16 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Word32 where
  mempty = 0
  mappend = (Prelude.+)

instance Monoid Word64 where
  mempty = 0
  mappend = (Prelude.+)


instance Monoid [a] where
        mempty  = []
        mappend = (++)

instance Monoid b => Monoid (a -> b) where
        mempty _ = mempty
        mappend f g x = f x `mappend` g x

instance Monoid () where
        -- Should it be strict?
        mempty        = ()
        _ `mappend` _ = ()
        mconcat _     = ()

instance (Monoid a, Monoid b) => Monoid (a,b) where
        mempty = (mempty, mempty)
        (a1,b1) `mappend` (a2,b2) =
                (a1 `mappend` a2, b1 `mappend` b2)

instance (Monoid a, Monoid b, Monoid c) => Monoid (a,b,c) where
        mempty = (mempty, mempty, mempty)
        (a1,b1,c1) `mappend` (a2,b2,c2) =
                (a1 `mappend` a2, b1 `mappend` b2, c1 `mappend` c2)

instance (Monoid a, Monoid b, Monoid c, Monoid d) => Monoid (a,b,c,d) where
        mempty = (mempty, mempty, mempty, mempty)
        (a1,b1,c1,d1) `mappend` (a2,b2,c2,d2) =
                (a1 `mappend` a2, b1 `mappend` b2,
                 c1 `mappend` c2, d1 `mappend` d2)

instance (Monoid a, Monoid b, Monoid c, Monoid d, Monoid e) =>
                Monoid (a,b,c,d,e) where
        mempty = (mempty, mempty, mempty, mempty, mempty)
        (a1,b1,c1,d1,e1) `mappend` (a2,b2,c2,d2,e2) =
                (a1 `mappend` a2, b1 `mappend` b2, c1 `mappend` c2,
                 d1 `mappend` d2, e1 `mappend` e2)

instance Monoid (Maybe a) where
  mempty = Nothing
  Nothing `mappend` m = m
  Just m `mappend` _ = Just m

instance Monoid (B.ByteString) where
  mempty = B.empty
  mappend = B.append

instance Monoid (LB.ByteString) where
  mempty = LB.empty
  mappend = LB.append

instance Monoid (LT.Text) where
  mempty = LT.empty
  mappend = LT.append

instance Monoid (ST.Text) where
  mempty = ST.empty
  mappend = ST.append



instance (Ord a) => Monoid (Map.Map a b) where
  mempty = Map.empty
  mappend = Map.union

instance (Ord a) => Monoid (Set.Set a) where
  mempty = Set.empty
  mappend = Set.union

instance Monoid (Sequence.Seq a) where
  mempty = Sequence.empty
  mappend = (Sequence.><)
