{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FunctionalDependencies #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE UndecidableInstances #-}
module Data.OpenApi.Declare where
import Prelude ()
import Prelude.Compat
import Control.Monad
import Control.Monad.Cont (ContT)
import Control.Monad.List (ListT)
import Control.Monad.Reader (ReaderT)
import Control.Monad.Trans
import Control.Monad.Trans.Except (ExceptT)
import Control.Monad.Trans.Identity (IdentityT)
import Control.Monad.Trans.Maybe (MaybeT)
import Control.Monad.Trans.State.Lazy as Lazy
import Control.Monad.Trans.State.Strict as Strict
import Control.Monad.Trans.RWS.Lazy as Lazy
import Control.Monad.Trans.RWS.Strict as Strict
import Control.Monad.Trans.Writer.Lazy as Lazy
import Control.Monad.Trans.Writer.Strict as Strict
import Data.Functor.Identity
newtype DeclareT d m a = DeclareT { runDeclareT :: d -> m (d, a) }
  deriving (Functor)
instance (Applicative m, Monad m, Monoid d) => Applicative (DeclareT d m) where
  pure x = DeclareT (\_ -> pure (mempty, x))
  DeclareT df <*> DeclareT dx = DeclareT $ \d -> do
    ~(d',  f) <- df d
    ~(d'', x) <- dx (mappend d d')
    return (mappend d' d'', f x)
instance (Applicative m, Monad m, Monoid d) => Monad (DeclareT d m) where
  return x = DeclareT (\_ -> pure (mempty, x))
  DeclareT dx >>= f = DeclareT $ \d -> do
    ~(d',  x) <- dx d
    ~(d'', y) <- runDeclareT (f x) (mappend d d')
    return (mappend d' d'', y)
instance Monoid d => MonadTrans (DeclareT d) where
  lift m = DeclareT (\_ -> (,) mempty <$> m)
class (Applicative m, Monad m) => MonadDeclare d m | m -> d where
  
  declare :: d -> m ()
  
  look :: m d
instance (Applicative m, Monad m, Monoid d) => MonadDeclare d (DeclareT d m) where
  declare d = DeclareT (\_ -> return (d, ()))
  look = DeclareT (\d -> return (mempty, d))
liftDeclare :: MonadDeclare d m => Declare d a -> m a
liftDeclare da = do
  (d', a) <- looks (runDeclare da)
  declare d'
  pure a
looks :: MonadDeclare d m => (d -> a) -> m a
looks f = f <$> look
evalDeclareT :: Monad m => DeclareT d m a -> d -> m a
evalDeclareT (DeclareT f) d = snd <$> f d
execDeclareT :: Monad m => DeclareT d m a -> d -> m d
execDeclareT (DeclareT f) d = fst <$> f d
undeclareT :: (Monad m, Monoid d) => DeclareT d m a -> m a
undeclareT = flip evalDeclareT mempty
type Declare d = DeclareT d Identity
runDeclare :: Declare d a -> d -> (d, a)
runDeclare m = runIdentity . runDeclareT m
evalDeclare :: Declare d a -> d -> a
evalDeclare m = runIdentity . evalDeclareT m
execDeclare :: Declare d a -> d -> d
execDeclare m = runIdentity . execDeclareT m
undeclare :: Monoid d => Declare d a -> a
undeclare = runIdentity . undeclareT
instance MonadDeclare d m => MonadDeclare d (ContT r m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (ExceptT e m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (IdentityT m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (MaybeT m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (ReaderT r m) where
  declare = lift . declare
  look = lift look
instance (Monoid w, MonadDeclare d m) => MonadDeclare d (Lazy.RWST r w s m) where
  declare = lift . declare
  look = lift look
instance (Monoid w, MonadDeclare d m) => MonadDeclare d (Strict.RWST r w s m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (Lazy.StateT s m) where
  declare = lift . declare
  look = lift look
instance MonadDeclare d m => MonadDeclare d (Strict.StateT s m) where
  declare = lift . declare
  look = lift look
instance (Monoid w, MonadDeclare d m) => MonadDeclare d (Lazy.WriterT w m) where
  declare = lift . declare
  look = lift look
instance (Monoid w, MonadDeclare d m) => MonadDeclare d (Strict.WriterT w m) where
  declare = lift . declare
  look = lift look