aivika-transformers-2.1: Simulation/Aivika/Trans/Internal/Simulation.hs
{-# LANGUAGE RecursiveDo, TypeSynonymInstances #-}
-- |
-- Module : Simulation.Aivika.Trans.Internal.Simulation
-- Copyright : Copyright (c) 2009-2014, David Sorokin <david.sorokin@gmail.com>
-- License : BSD3
-- Maintainer : David Sorokin <david.sorokin@gmail.com>
-- 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