{-# LANGUAGE RecursiveDo, TypeSynonymInstances #-} -- | -- Module : Simulation.Aivika.Trans.Internal.Simulation -- Copyright : Copyright (c) 2009-2014, David Sorokin -- License : BSD3 -- Maintainer : David Sorokin -- Stability : experimental -- Tested with: GHC 7.8.3 -- -- The module defines the 'Simulation' monad transformer that represents a computation -- within the simulation run. -- module Simulation.Aivika.Trans.Internal.Simulation (-- * Simulation SimulationLift(..), runSimulation, runSimulations, -- * Error Handling catchSimulation, finallySimulation, throwSimulation, -- * Memoization memoSimulation) where import Control.Exception import Control.Monad import Control.Monad.Trans import Control.Monad.Fix import Control.Applicative import Simulation.Aivika.Trans.Exception import Simulation.Aivika.Trans.Session import Simulation.Aivika.Trans.ProtoRef import Simulation.Aivika.Trans.Generator import Simulation.Aivika.Trans.Comp import Simulation.Aivika.Trans.Internal.Specs import Simulation.Aivika.Trans.Internal.Parameter instance Monad m => Monad (Simulation m) where {-# INLINE return #-} return a = Simulation $ \r -> return a {-# INLINE (>>=) #-} (Simulation m) >>= k = Simulation $ \r -> do a <- m r let Simulation m' = k a m' r -- | Run the simulation using the specified specs. runSimulation :: MonadComp m => Simulation m a -> Specs m -> m a runSimulation (Simulation m) sc = do s <- newSession q <- newEventQueue s sc g <- newGenerator s $ spcGeneratorType sc m Run { runSpecs = sc, runSession = s, runIndex = 1, runCount = 1, runEventQueue = q, runGenerator = g } -- | Run the given number of simulations using the specified specs, -- where each simulation is distinguished by its index 'simulationIndex'. runSimulations :: MonadComp m => Simulation m a -> Specs m -> Int -> [m a] runSimulations (Simulation m) sc runs = map f [1 .. runs] where f i = do s <- newSession q <- newEventQueue s sc g <- newGenerator s $ spcGeneratorType sc m Run { runSpecs = sc, runSession = s, runIndex = i, runCount = runs, runEventQueue = q, runGenerator = g } instance Functor m => Functor (Simulation m) where {-# INLINE fmap #-} fmap f (Simulation x) = Simulation $ \r -> fmap f $ x r instance Applicative m => Applicative (Simulation m) where {-# INLINE pure #-} pure = Simulation . const . pure {-# INLINE (<*>) #-} (Simulation x) <*> (Simulation y) = Simulation $ \r -> x r <*> y r liftMS :: Monad m => (a -> b) -> Simulation m a -> Simulation m b {-# INLINE liftMS #-} liftMS f (Simulation x) = Simulation $ \r -> do { a <- x r; return $ f a } instance MonadTrans Simulation where {-# INLINE lift #-} lift = Simulation . const instance MonadCompTrans Simulation where {-# INLINE liftComp #-} liftComp = Simulation . const instance MonadIO m => MonadIO (Simulation m) where {-# INLINE liftIO #-} liftIO = Simulation . const . liftIO -- | A type class to lift the simulation computations into other computations. class SimulationLift t where -- | Lift the specified 'Simulation' computation into another computation. liftSimulation :: MonadComp m => Simulation m a -> t m a instance SimulationLift Simulation where {-# INLINE liftSimulation #-} liftSimulation = id instance ParameterLift Simulation where {-# INLINE liftParameter #-} liftParameter (Parameter x) = Simulation x -- | Exception handling within 'Simulation' computations. catchSimulation :: (MonadComp m, Exception e) => Simulation m a -> (e -> Simulation m a) -> Simulation m a catchSimulation (Simulation m) h = Simulation $ \r -> catchComp (m r) $ \e -> let Simulation m' = h e in m' r -- | A computation with finalization part like the 'finally' function. finallySimulation :: MonadComp m => Simulation m a -> Simulation m b -> Simulation m a finallySimulation (Simulation m) (Simulation m') = Simulation $ \r -> finallyComp (m r) (m' r) -- | Like the standard 'throw' function. throwSimulation :: (MonadComp m, Exception e) => e -> Simulation m a throwSimulation = throw instance MonadFix m => MonadFix (Simulation m) where {-# INLINE mfix #-} mfix f = Simulation $ \r -> do { rec { a <- invokeSimulation r (f a) }; return a } -- | Memoize the 'Simulation' computation, always returning the same value -- within a simulation run. memoSimulation :: MonadComp m => Simulation m a -> Simulation m (Simulation m a) memoSimulation m = Simulation $ \r -> do let s = runSession r ref <- newProtoRef s Nothing return $ Simulation $ \r -> do x <- readProtoRef ref case x of Just v -> return v Nothing -> do v <- invokeSimulation r m writeProtoRef ref (Just v) return v