{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE RebindableSyntax     #-}
{-# LANGUAGE UndecidableInstances #-}

-- | This module is a duplication of the Control.Monad.Cont module, from the\
-- mtl.
module Control.Monad.Constrained.Cont
  (MonadCont(..)
  ,ContT(..)
  ,cont
  ,mapContT
  ,withContT
  ,runCont
  ,mapCont
  ,withCont)where

import           Control.Monad.Constrained

import           Control.Monad.Trans.Cont         hiding (callCC)
import qualified Control.Monad.Trans.Cont         as Cont

import qualified Control.Monad.Trans.Except       as Except
import qualified Control.Monad.Trans.Identity     as Identity
import qualified Control.Monad.Trans.Maybe        as Maybe
import qualified Control.Monad.Trans.Reader       as Reader
import qualified Control.Monad.Trans.State.Lazy   as State.Lazy
import qualified Control.Monad.Trans.State.Strict as State.Strict

import qualified Prelude

-- | A class for monads which can embed continuations.
class Monad m => MonadCont m where
    {- | @callCC@ (call-with-current-continuation)
    calls a function with the current continuation as its argument.
    Provides an escape continuation mechanism for use with Continuation monads.
    Escape continuations allow to abort the current computation and return
    a value immediately.
    They achieve a similar effect to 'Control.Monad.Error.throwError'
    and 'Control.Monad.Error.catchError'
    within an 'Control.Monad.Error.Error' monad.
    Advantage of this function over calling @return@ is that it makes
    the continuation explicit,
    allowing more flexibility and better control
    (see examples in "Control.Monad.Cont").

    The standard idiom used with @callCC@ is to provide a lambda-expression
    to name the continuation. Then calling the named continuation anywhere
    within its scope will escape from the computation,
    even if it is many layers deep within nested computations.
    -}
    callCC :: ((a -> m b) -> m a) -> m a

instance MonadCont (ContT r m) where
    callCC = Cont.callCC

instance (MonadCont m, Prelude.Monad (Unconstrained m)) =>
         MonadCont (Maybe.MaybeT m) where
    callCC = Maybe.liftCallCC callCC

instance MonadCont m => MonadCont (Reader.ReaderT r m) where
    callCC = Reader.liftCallCC callCC

instance (MonadCont m, Prelude.Monad (Unconstrained m)) =>
         MonadCont (State.Lazy.StateT s m) where
    callCC = State.Lazy.liftCallCC callCC

instance (MonadCont m, Prelude.Monad (Unconstrained m)) =>
         MonadCont (State.Strict.StateT s m) where
    callCC = State.Strict.liftCallCC callCC

instance MonadCont m => MonadCont (Identity.IdentityT m) where
    callCC = Identity.liftCallCC callCC

instance (MonadCont m, Prelude.Monad (Unconstrained m)) =>
         MonadCont (Except.ExceptT e m) where
    callCC = Except.liftCallCC callCC