{-# LANGUAGE StandaloneDeriving, GeneralizedNewtypeDeriving #-}

module Data.TrieMap.Applicative where

import Control.Applicative
import Control.Arrow
import Control.Monad

import Data.Monoid hiding (Dual)

newtype Id a = Id {unId :: a}
newtype WM w m a = WM {runWM :: m (w, a)}

deriving instance Functor First
deriving instance Functor Last
deriving instance Monad First
deriving instance Monad Last

instance Functor m => Functor (WM w m) where
	fmap f (WM x) = WM (fmap (second f) x)

instance (Applicative m, Monoid w) => Applicative (WM w m) where
	pure x = WM (pure (mempty, x))
	WM f <*> WM x = WM (fmap (\ (fW, ff) (xW, xx) -> (fW `mappend` xW, ff xx)) f <*> x)

instance (Alternative m, Monoid w) => Alternative (WM w m) where
	empty = WM empty
	WM a <|> WM b = WM (a <|> b)

write :: (Functor m, Monoid w) => w -> WM w m a -> WM w m a
write w (WM m) = WM (fmap (\ (v, xx) -> (v `mappend` w, xx)) m)

instance Applicative Id where
	pure = Id
	Id f <*> Id x = Id (f x)

instance Functor Id where
	fmap f (Id x) = Id (f x)

instance MonadPlus First where
	mzero = mempty
	mplus = mappend

instance MonadPlus Last where
	mzero = mempty
	mplus = mappend

-- instance Monad First where
-- 	return x = First (Just x)
-- 	First Nothing >>= _ = First Nothing
-- 	First (Just x) >>= k = k x
-- 
-- instance Monad Last

(.:) :: (c -> d) -> (a -> b -> c) -> a -> b -> d
(f .: g) x y = f (g x y)

(<.>) :: Functor f => (b -> c) -> (a -> f b) -> a -> f c
f <.> g = fmap f . g

(<.:>) :: Functor f => (c -> d) -> (a -> b -> f c) -> a -> b -> f d
(f <.:> g) x y = f <$> g x y
{-
(<|>) :: MonadPlus m => m a -> m a -> m a
(<|>) = mplus-}

instance Applicative First where
	pure = return
	(<*>) = ap

instance Alternative First where
	empty = mempty
	(<|>) = mplus

instance Applicative Last where
	pure = return
	(<*>) = ap

instance Alternative Last where
	empty = mempty
	(<|>) = mplus

newtype Dual f a = Dual {runDual :: f a}

instance Functor f => Functor (Dual f) where
	fmap f (Dual x) = Dual (fmap f x)

instance Applicative f => Applicative (Dual f) where
	pure = Dual . pure
	Dual f <*> Dual x = Dual (flip ($) <$> x <*> f)

instance Alternative f => Alternative (Dual f) where
	empty = Dual empty
	Dual a <|> Dual b = Dual (b <|> a)