{-# OPTIONS_GHC -fglasgow-exts -fno-warn-unused-binds #-}

{-|
    Continuation with shift and reset operators.

>   The Road goes ever on and on
>   Down from the door where it began.
>   Now far ahead the Road has gone,
>   And I must follow, if I can...
-}

module Pugs.Cont (
    callCCT, shift, reset, shiftT, resetT,
    module Control.Monad.Cont,
) where

import qualified Control.Monad.Cont as C (lift)
import Control.Monad.Cont (mapContT, withContT, mapCont, withCont, Cont(..), ContT(..), MonadCont(..))

-- Cont' m a is the type of a continuation expecting an a within the 
-- continuation monad Cont m
type Cont' m a = forall r. a -> m r

callCCT :: forall a m. MonadCont m => (Cont' m a -> m a) -> m a
callCCT f = callCC f' where
  f' :: (a -> m (EmptyMonad m)) -> m a
  f' g = f g' where
    g' :: a -> m b
    g' = (=<<) runEmptyMonad . g

-- ghc doesn't allow something like m (forall c. m c)
newtype EmptyMonad m = EmptyMonad { runEmptyMonad :: forall c. m c }

-- shift/reset for the Cont monad
shift :: ((a -> Cont s r) -> Cont r r) -> Cont r a
shift e = Cont $ \k -> e (return . k) `runCont` id

reset :: Cont a a -> Cont r a 
reset e = return $ e `runCont` id

-- shiftT/resetT for the ContT monad transformer
shiftT :: Monad m => ((a -> ContT r m s) -> ContT s m s) -> ContT s m a
shiftT e = ContT $ \k -> e (C.lift . k) `runContT` return

resetT :: Monad m => ContT a m a -> ContT r m a
resetT e = C.lift $ e `runContT` return

