-- | Sum and product functors, with the usual instances. 
-- You can in principle use these to extend existing expressions, for example
--
-- > type ExtendedExpression = Mu (Expr :+: Custom)
--
-- This module uses the TypeOperators language extension for convenience.
--

{-# LANGUAGE TypeOperators #-}
module Data.Generics.Fixplate.Functor
  ( (:+:) (..)
  , (:*:) (..)
  )
  where

--------------------------------------------------------------------------------

import Prelude hiding ( foldl , foldr , mapM )

import Control.Applicative ()
import Control.Monad ( liftM )

import Data.Generics.Fixplate

--------------------------------------------------------------------------------

-- | Sum of two functors
data (f :+: g) a = InL (f a) | InR (g a) deriving (Eq,Ord,Show)

-- | Product of two functors
data (f :*: g) a = (f a) :*: (g a)       deriving (Eq,Ord,Show)

infixl 6 :+:
infixl 7 :*:

--------------------------------------------------------------------------------

instance (Functor f, Functor g) => Functor (f :+: g) where
  fmap h (InL x) = InL (fmap h x)
  fmap h (InR y) = InR (fmap h y)

instance (Foldable f, Foldable g) => Foldable (f :+: g) where
  foldl h a (InL x) = foldl h a x
  foldl h a (InR y) = foldl h a y

  foldr h a (InL x) = foldr h a x
  foldr h a (InR y) = foldr h a y

instance (Traversable f, Traversable g) => Traversable (f :+: g) where
  traverse h (InL x) = InL <$> traverse h x
  traverse h (InR y) = InR <$> traverse h y

  mapM h (InL x) = liftM InL $ mapM h x
  mapM h (InR y) = liftM InR $ mapM h y

--------------------------------------------------------------------------------

instance (Functor f, Functor g) => Functor (f :*: g) where
  fmap h (x :*: y) = fmap h x :*: fmap h y

instance (Foldable f, Foldable g) => Foldable (f :*: g) where
  foldl h a (x :*: y) = let a' = foldl h a x in foldl h a' y
  foldr h a (x :*: y) = let a' = foldr h a y in foldr h a' x

instance (Traversable f, Traversable g) => Traversable (f :*: g) where
  traverse h (x :*: y) = (:*:) <$> traverse h x <*> traverse h y
  mapM h (x :*: y) = do
    x1 <- mapM h x
    y1 <- mapM h y
    return (x1 :*: y1)

--------------------------------------------------------------------------------

app_prec , mul_prec :: Int
app_prec = 10
mul_prec = 7

--------------------------------------------------------------------------------

instance (EqF f, EqF g) => EqF (f :+: g) where
  equalF (InL x) (InL y) = equalF x y
  equalF (InR x) (InR y) = equalF x y
  equalF _       _       = False

instance (OrdF f, OrdF g) => OrdF (f :+: g) where
  compareF (InL x) (InL y) = compareF x y
  compareF (InR x) (InR y) = compareF x y
  compareF (InL _) (InR _) = LT
  compareF (InR _) (InL _) = GT

instance (ShowF f, ShowF g) => ShowF (f :+: g) where
  showsPrecF d (InL x) = showParen (d>app_prec)
    $ showString "InL "
    . showsPrecF (app_prec+1) x
  showsPrecF d (InR x) = showParen (d>app_prec)
    $ showString "InR "
    . showsPrecF (app_prec+1) x

--------------------------------------------------------------------------------

instance (EqF f, EqF g) => EqF (f :*: g) where
  equalF (x1 :*: x2) (y1 :*: y2) = equalF x1 y1 && equalF x2 y2

instance (OrdF f, OrdF g) => OrdF (f :*: g) where
  compareF (x1 :*: x2) (y1 :*: y2) = case compareF x1 y1 of
    LT -> LT
    GT -> GT
    EQ -> compareF  x2 y2

instance (ShowF f, ShowF g) => ShowF (f :*: g) where
  showsPrecF d (x :*: y) = showParen (d>mul_prec)
    $ showsPrecF (mul_prec+1) x
    . showString " :*: "
    . showsPrecF (mul_prec+1) y

--------------------------------------------------------------------------------