{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverlappingInstances #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}

{-|

This module exports several very overlapping instances for the type classes
defined in the @mtl@ library, and should be used with caution, or not at all
(see the package description). The instances are defined:

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadBase' b m)      => 'MonadBase' b        (t m)@

  * @instance ('MonadTransControl' t, 'Monad' (t m), 'MonadCont' m)        => 'MonadCont'          (t m)@

  * @instance ('MonadTransControl' t, 'Monad' (t m), 'MonadError' e m)     => 'MonadError' e       (t m)@

  * @instance ('MonadTransControl' t, 'Monad' (t m), 'MonadFix' m)         => 'MonadFix'           (t m)@

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadIO' m)          => 'MonadIO'            (t m)@

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadReader' r m)    => 'MonadReader' r      (t m)@

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadRWS' r w s m)   => 'MonadRWS' r w s     (t m)@

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadState' s m)     => 'MonadState' s       (t m)@

  * @instance ('MonadTrans' t, 'Monad' (t m),        'MonadWriter' w m)    => 'MonadWriter' w      (t m)@

  * @instance ('MonadBaseControl' b m,             'MonadCont' b)        => 'MonadCont'          m@

  * @instance ('MonadBaseControl' b m,             'MonadError' e b)     => 'MonadError' e       m@

  * @instance ('MonadBaseControl' b m,             'MonadFix' b)         => 'MonadFix'           m@

  * @instance ('MonadBase' b m,                    'MonadIO' b)          => 'MonadIO'            m@

  * @instance ('MonadBase' b m,                    'MonadReader' r b)    => 'MonadReader' r      m@

  * @instance ('MonadBase' b m,                    'MonadRWS' r w s b)   => 'MonadRWS' r w s     m@

  * @instance ('MonadBase' b m,                    'MonadState' s b)     => 'MonadState' s       m@

  * @instance ('MonadBase' b m,                    'MonadWriter' w b)    => 'MonadWriter' w      m@

Note that the following instance is not included, as currently it cannot be due to GHC bug #4259:

  * @instance ('MonadTransControl' t, 'Monad' (t m), 'MonadBaseControl' b m) => 'MonadBaseControl' b (t m)@


-}

module Control.Monad.Instances.Evil
    ()
where

import           Control.Applicative (Applicative (..))
import           Control.Monad.Base (MonadBase (..))
import           Control.Monad.Cont.Class (MonadCont(..))
import           Control.Monad.Error.Class (MonadError (..))
import           Control.Monad.Fix (MonadFix (..), fix)
import           Control.Monad.IO.Class (MonadIO (..))
import           Control.Monad.Reader.Class (MonadReader (..))
import           Control.Monad.RWS.Class (MonadRWS (..))
import           Control.Monad.State.Class (MonadState (..))
import           Control.Monad.Trans.Class (MonadTrans (..))
import           Control.Monad.Trans.Control
                     ( ComposeSt
                     , MonadBaseControl (..)
                     , MonadTransControl (..)
                     , Run
                     , control
                     , defaultLiftBaseWith
                     , defaultRestoreM
                     )
import           Control.Monad.Writer.Class (MonadWriter (..))


------------------------------------------------------------------------------
instance (MonadTrans t, Applicative (t m), Monad (t m), MonadBase b m) => MonadBase b (t m) where
    liftBase = lift . liftBase


------------------------------------------------------------------------------
{- This doesn't work, see: http://hackage.haskell.org/trac/ghc/ticket/4259
instance (MonadTransControl t, Monad (t m), MonadBaseControl b m) => MonadBaseControl b (t m) where
    newtype StM (t m) a = StMT {unStMT :: ComposeSt t m a}
    liftBaseWith = defaultLiftBaseWith StMT
    restoreM = defaultRestoreM unStMT
-}


------------------------------------------------------------------------------
instance (MonadTransControl t, Monad (t m), MonadCont m) => MonadCont (t m) where
    callCC f = controlT $ \run -> callCC $ \c -> run . f $
        \a -> lift (run (return a) >>= c)


------------------------------------------------------------------------------
instance (MonadBaseControl b m, MonadCont b) => MonadCont m where
    callCC f = control $ \run -> callCC $ \c -> run . f $
        \a -> liftBase (run (return a) >>= c)


------------------------------------------------------------------------------
instance (MonadTransControl t, Monad (t m), MonadError e m) => MonadError e (t m) where
    throwError = lift . throwError
    catchError t h = controlT $ \run -> catchError (run t) (\e -> run (h e))


------------------------------------------------------------------------------
instance (MonadBaseControl b m, MonadError e b) => MonadError e m where
    throwError = liftBase . throwError
    catchError t h = control $ \run -> catchError (run t) (\e -> run (h e))


------------------------------------------------------------------------------
instance (MonadTransControl t, Monad (t m), MonadFix m) => MonadFix (t m) where
    mfix f = controlT $ \run -> mfix (\a -> run (restoreT (return a) >>= f))


------------------------------------------------------------------------------
instance (MonadBaseControl b m, MonadFix b) => MonadFix m where
    mfix f = control $ \run -> mfix (\a -> run (restoreM a >>= f))


------------------------------------------------------------------------------
instance (MonadTrans t, Monad (t m), MonadIO m) => MonadIO (t m) where
    liftIO = lift . liftIO


------------------------------------------------------------------------------
instance (MonadBase b m, MonadIO b) => MonadIO m where
    liftIO = liftBase . liftIO


------------------------------------------------------------------------------
instance (MonadTrans t, Monad (t m), MonadReader r m) => MonadReader r (t m) where
    ask = lift ask
    local f m = m >>= lift . local f . return


------------------------------------------------------------------------------
instance (MonadBase b m, MonadReader r b) => MonadReader r m where
    ask = liftBase ask
    local f m = m >>= liftBase . local f . return


------------------------------------------------------------------------------
instance (MonadTrans t, Monad (t m), MonadRWS r w s m) => MonadRWS r w s (t m)


------------------------------------------------------------------------------
instance (MonadBase b m, MonadRWS r w s b) => MonadRWS r w s m


------------------------------------------------------------------------------
instance (MonadTrans t, Monad (t m), MonadState s m) => MonadState s (t m) where
    get = lift get
    put s = lift $ put s


------------------------------------------------------------------------------
instance (MonadBase b m, MonadState s b) => MonadState s m where
    get = liftBase get
    put s = liftBase $ put s


------------------------------------------------------------------------------
instance (MonadTrans t, Monad (t m), MonadWriter w m) => MonadWriter w (t m) where
    tell w = lift $ tell w
    listen m = m >>= lift . listen . return
    pass m = m >>= lift . pass . return


------------------------------------------------------------------------------
instance (MonadBase b m, MonadWriter w b) => MonadWriter w m where
    tell w = liftBase $ tell w
    listen m = m >>= liftBase . listen . return
    pass m = m >>= liftBase . pass . return


------------------------------------------------------------------------------
controlT
    :: (MonadTransControl t, Monad (t m), Monad m)
    => (Run t -> m (StT t a)) -> t m a
controlT f = liftWith f >>= restoreT . return