module ExceptMT (HasExcept(..), MT(..), WithExcept, removeExcept, mapExcept) 
  where

import MT
import Control_Monad_Fix 

import Monad(liftM,MonadPlus(..))

newtype WithExcept x m a   = E { removeExcept :: m (Either x a) } 
iso f = E . f . removeExcept


mapExcept :: Monad m => (x -> y) -> WithExcept x m a -> WithExcept y m a
mapExcept f = iso (liftM (either (Left . f) Right))


--------------------------------------------------------------------------------
instance Monad m => Functor (WithExcept x m) where
  fmap = liftM

instance Monad m => Monad (WithExcept x m) where
  return    = lift . return
  E m >>= f = E $ do x <- m
                     case x of 
                       Left x  -> return (Left x)
                       Right a -> removeExcept (f a)
            
instance MT (WithExcept x) where
  lift m    = E (m >>= return . Right)

instance MonadPlus m => MonadPlus (WithExcept x m) where
  mzero             = lift mzero   
  E m1 `mplus` E m2 = E (m1 `mplus` m2)
--------------------------------------------------------------------------------


--------------------------------------------------------------------------------
instance HasEnv m ix e => HasEnv (WithExcept x m) ix e where
  getEnv ix       = lift (getEnv ix)
  inModEnv ix f   = iso (inModEnv ix f)

instance HasState m ix s => HasState (WithExcept x m) ix s where
  updSt ix        = lift . updSt ix

instance HasOutput m ix o => HasOutput (WithExcept x m) ix o where
  outputTree ix   = lift . outputTree ix

instance Monad m => HasExcept (WithExcept x m) x where
  raise           = E . return . Left
  handle h (E m)  = E (m >>= either (removeExcept . h) (return . Right))

instance HasCont m => HasCont (WithExcept x m) where
  callcc f        = E $ callcc $ \k -> removeExcept $ f $ E . k . Right
  

instance HasBaseMonad m n => HasBaseMonad (WithExcept e m) n where
  inBase          = lift . inBase

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


instance MonadFix m => MonadFix (WithExcept x m) where
  mfix f          = E $ mfix (removeExcept . f . either undefined id)


