module ContMT (HasCont(..), MT(..), at, Z, S, 
               removeCont, runCont, WithCont) where

import MT
import Monad(liftM,MonadPlus(..))
import Control_Monad_Fix
import ImpUtils


newtype WithCont o m i = C { ($$) :: (i -> m o) -> m o }

removeCont :: WithCont o m i -> (i -> m o) -> m o
removeCont = ($$)

runCont :: Monad m => WithCont i m i -> m i
runCont k = k $$ return


--------------------------------------------------------------------------------
instance Monad m => Functor (WithCont o m) where
  fmap      = liftM

instance Monad m => Monad (WithCont o m) where
  return x  = C ($ x) 
  C m >>= f = C $ \k -> m $ \a -> f a $$ k

instance MT (WithCont o) where
  lift m    = C (m >>=)

instance MonadPlus m => MonadPlus (WithCont o m) where
    mzero             = lift mzero
    C m1 `mplus` C m2 = C $ \k -> m1 k `mplus` m2 k
--------------------------------------------------------------------------------


--------------------------------------------------------------------------------
instance HasEnv m ix e => HasEnv (WithCont o m) ix e where
  getEnv ix           = lift (getEnv ix)
  inModEnv ix f (C m) = C (inModEnv ix f . m)

instance HasState m ix s => HasState (WithCont o m) ix s where
  updSt ix            = lift . updSt ix

instance HasOutput m ix o => HasOutput (WithCont ans m) ix o where
  outputTree ix       = lift . outputTree ix

instance HasExcept m x => HasExcept (WithCont o m) x where
  raise               = lift . raise
  handle f m          = C $ \k -> handle (\x -> f x $$ k) (m $$ k)

instance Monad m => HasCont (WithCont o m) where
  callcc f            = C $ \k -> f (\a -> C $ \d -> k a) $$ k
--------------------------------------------------------------------------------


instance HasBaseMonad m n => HasBaseMonad (WithCont o m) n where
  inBase              = lift . inBase

instance HasRefs m r => HasRefs (WithCont () m) r where
  newRef      = lift . newRef
  readRef     = lift . readRef
  writeRef r  = lift . writeRef r

-- Magnus' fixpoint implementation
instance (MonadFix m, HasRefs m r) => MonadFix (WithCont () m) where
 mfix m = C $ \k -> do x <- newRef Nothing
                       let xcases j n = readRef x >>= maybe n j
                           k' b'      = xcases (const (k b')) (writeRef x (Just b'))
                       mfix $ \b -> do m b $$ k'
                                       xcases return (return (error "mfix in ContMT is bottom"))
                       xcases k (return ())

  

shift     :: Monad m => ((a -> WithCont o m o) -> WithCont o m o) -> WithCont o m a
shift f   = C (\k -> runCont $ f (\a -> C (\k' -> k' =<< k a)))

reset     :: Monad m => WithCont o m o -> WithCont o m o 
reset m   = C (\k -> k =<< runCont m)


test1           :: WithCont String IO String
test1           = do x <- reset $ do y <- shift (\f -> do z <- f "100"
                                                          f z)
                                     return ("10 + " ++ y)
                     inBase (print "hello")
                     return $ "1 + " ++ x

{-
test2           = liftM ("1 + " ++) $ reset $ liftM ("10 + " ++) $ shift (\f -> return "100")
test3           = liftM ("1 + " ++) $ reset $ liftM ("10 + " ++)  $ shift $ \f -> liftM2 (\x y -> x ++ " + " ++ y) (f "100") (f "1000")
-}
