{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies, FlexibleInstances, UndecidableInstances #-}
-- | Code in this module is not directly related to CellML, but it is rather convenience code used by the solver.
module Control.Monad.IO.Unwrappable (MonadIOUnwrappable, unwrapState, unwrapMonadIO, rewrapMonadIO)
where
import Control.Monad.Trans.Class
import Control.Monad
import Control.Monad.IO.Class
import Control.Monad.Error
import Control.Monad.Reader
import Control.Monad.Reader.Class
import Control.Monad.State
import Control.Monad.State.Class
import Data.IORef
data Single a = Single a
-- | Represents a MonadIO where any change further up the monad stack can be
-- | represented lower down in the stack.
class MonadIO m => MonadIOUnwrappable m c s | m -> c, m -> s where
-- | Sets up state (e.g. an IORef) to be used to simulate the monad from the
-- | IO monad.
unwrapState :: m s
-- | Maps the monad to only use IO level constructs and the state set up
-- | using unwrapState.
unwrapMonadIO :: s -> m a -> IO (c a)
-- | Reverses a previous unwrapMonadIO operation.
rewrapMonadIO :: s -> c a -> m a
instance MonadIOUnwrappable IO Single () where
unwrapState = return ()
unwrapMonadIO _ asIO = liftM Single asIO
rewrapMonadIO _ (Single x) = return x
newtype EitherChain a b c = EitherChain (a (Either b c))
instance (Error e, MonadIO m, MonadIOUnwrappable m c s) => MonadIOUnwrappable (ErrorT e m) (EitherChain c e) s where
unwrapState = lift (unwrapState)
unwrapMonadIO s m = liftM EitherChain $ unwrapMonadIO s (runErrorT m)
rewrapMonadIO s (EitherChain v) = ErrorT (rewrapMonadIO s v)
instance (MonadIO m, MonadIOUnwrappable m c s) => MonadIOUnwrappable (ReaderT r m) c (r, s) where
unwrapState = liftM2 (,) ask (lift unwrapState)
unwrapMonadIO (r, s) m = unwrapMonadIO s (runReaderT m r)
rewrapMonadIO (_, s) v = ReaderT (\_ -> rewrapMonadIO s v)
instance (MonadIO m, MonadIOUnwrappable m c s) => MonadIOUnwrappable (StateT r m) c (IORef r, s) where
unwrapState = liftM2 (,) (get >>= (liftIO . newIORef)) (lift unwrapState)
unwrapMonadIO (r, s) m = unwrapMonadIO s $ do
s0 <- liftIO (readIORef r)
(a, v') <- runStateT m s0
liftIO (writeIORef r v')
return a
rewrapMonadIO (r, s) a = StateT (\_ -> liftM2 (,) (rewrapMonadIO s a) (liftIO . readIORef $ r))