{-# LANGUAGE MultiParamTypeClasses, RankNTypes, DefaultSignatures #-}
-- |A module for functors
module Clean.Functor(
  Functor(..),Cofunctor(..),Bifunctor(..),
  
  Id(..),Const(..),Flip(..),Compose(..),

  (<$>),(<$),(<&>),void,
  promap
  ) where

import qualified Prelude as P

import Clean.Classes
import Clean.Core
import Data.Tree

class Cofunctor f where
  comap :: (a -> b) -> f b -> f a
instance Cofunctor (Flip (->) r) where
  comap f (Flip g) = Flip (g . f)
instance (Functor f,Cofunctor g) => Cofunctor (Compose f g) where
  comap f (Compose c) = Compose (map (comap f) c)
promap f c = unFlip (comap f (Flip c))

class Bifunctor p where
  dimap :: (c -> a) -> (b -> d) -> p a b -> p c d
  default dimap :: (Functor (p a),Cofunctor (Flip p d)) => (c -> a) -> (b -> d) -> p a b -> p c d
  dimap f g = promap f . map g
instance Bifunctor (->)

instance Functor [] where map f = f' where f' [] = [] ; f' (x:t) = f x:f' t
instance Functor Tree where
  map f (Node a subs) = Node (f a) (map (map f) subs)

-- |The Identity Functor
newtype Id a = Id { getId :: a }
deriving instance Semigroup a => Semigroup (Id a)
deriving instance Monoid w => Monoid (Id w)
deriving instance Ring w => Ring (Id w)
instance Unit Id where pure = Id
instance Functor Id
instance Applicative Id
instance Monad Id where Id a >>= k = k a

-- |The Constant Functor
newtype Const a b = Const { getConst :: a }
deriving instance Semigroup w => Semigroup (Const w a)
deriving instance Monoid a => Monoid (Const a b)
instance Unit (Const a) where pure _ = Const undefined
instance Functor (Const a)
instance Applicative (Const a)
instance Monad (Const a) where Const a >>= _ = Const a

-- |A motherflippin' functor
newtype Flip f a b = Flip { unFlip :: f b a }

-- |The Composition functor
newtype Compose f g a = Compose { getCompose :: f (g a) }
instance (Unit f,Unit g) => Unit (Compose f g) where pure = Compose . pure . pure
instance (Functor f,Functor g) => Functor (Compose f g) where
  map f (Compose c) = Compose (map (map f) c)

newtype Product f g a = Product { getProduct :: f a:*:g a }
instance (Functor f,Functor g) => Functor (Product f g) where
  map f = Product . (map f <#> map f) . getProduct
newtype Sum f g a = Sum { getSum :: f a:+:g a }
instance (Functor f,Functor g) => Functor (Sum f g) where
  map f = Sum . (Left<$>map f <|> Right<$>map f) . getSum

instance Functor (Either b) where map f = Left <|> Right . f
instance Functor ((,) b) where map f (b,a) = (b,f a)
instance Functor ((->) a) where map = (.)
deriving instance Functor Interleave

instance Functor IO
instance Applicative IO
instance Monad IO where (>>=) = (P.>>=)

(<$>) = map
x<&>f = map f x
a <$ x = const a <$> x

void :: Functor f => f a -> f ()
void = map (const ())