event-monad-0.0.0.2: src/Control/Monad/EventT.hs
{-
- ``Control/Monad/Event/EventT''
- (c) 2008 Cook, J. MR SSD, Inc.
-}
{-# LANGUAGE
GeneralizedNewtypeDeriving,
MultiParamTypeClasses,
FlexibleContexts,
FlexibleInstances,
UndecidableInstances,
TypeSynonymInstances,
KindSignatures
#-}
module Control.Monad.EventT
(
EventT
, EventM
, runEventT
, runEventGraph
, runEventGraphWithState
, newEventT_RState
, newEventT_RWState
, onClockChanged
, onEventDispatch
, onEventComplete
, onEventSchedule
, onEventCancel
, addHandler
, removeHandler
, SetDebugHandlers(..)
, DebugHandlerOptions(..)
, defaultHandlerOptions
) where
import Control.Monad.Event.Classes
import Control.Monad.Event.Internal.Types
import Data.Handler
import Data.PriorityQueue
import Data.StateRef
import Control.Monad.RWS
import Control.Monad.Loops
import Text.PrettyPrint.Leijen
import IO
import Control.Monad.Trans
import Control.Concurrent (threadDelay)
-- imports just for classes to implement
import Control.Monad.Error (MonadError(..))
import Control.Monad (MonadPlus(..))
import Control.Monad.Cont (MonadCont(..))
import qualified Data.Map as M
{- Time management -}
instance Monad m => MonadTime (EventT t m) t where
getCurrentTime = EventT (gets currentTime)
{- Time Management (internal) -}
setCurrentTime :: Monad m => t -> EventT t m ()
setCurrentTime t1 = do
rwState <- EventT get
let t0 = currentTime rwState
EventT (put (rwState {currentTime = t1}))
invokeHandler clockChangedHandler (t0,t1)
{- simulation control -}
instance Monad m => MonadSimControl (EventT t m) where
resumeSimulation = do
rwState <- EventT get
EventT (put (rwState {simRunning = True}))
pauseSimulation = do
rwState <- EventT get
EventT (put (rwState {simRunning = False}))
isSimulationRunning = EventT (gets simRunning)
{- MonadEvent instance for unadorned (EventT t m) actions -}
instance Monad m => MonadEvent (EventT t m) (EventT t m a) where
describeEvent e = return (text "Undocumented Event")
runEvent e = e >> return ()
{- MonadEvent instance for Event Descriptors - this does most of
the real grunt work of running an event -}
instance (Monad m, Pretty t) => MonadEvent (EventT t m) (EventDescriptor (EventT t m) t) where
describeEvent (EventDescriptor {eventId = eid, eventTime = t, event = e}) = do
eventDescription <- describeEvent e
return (
brackets (
fill 5 (pretty eid)
<> text "|"
<> fill 10 (pretty t)
) <> colon
<+> align eventDescription
)
runEvent event@(EventDescriptor {event = e}) = do
setCurrentTime (eventTime event)
invokeHandler eventDispatchHandler event
EventT (local (\env -> env {currentEvent = Just event}) (unWrapEventT (runEvent e)))
invokeHandler eventCompleteHandler event
{- event scheduling, canceling, etc. -}
instance (Monad m, Ord t, Num t, MonadEvent (EventT t m) e) => ScheduleEvent (EventT t m) t e where
scheduleEventIn dt e = do
now <- getCurrentTime
let t = (now + dt)
eid <- getNextEventId
let event = EventDescriptor
{eventId = eid, eventTime = t, event = e}
enqueue eventQueueRef event
invokeHandler eventScheduleHandler event
return eid
doNext e = scheduleEventIn 0 e >> return ()
instance (Monad m, Ord t) => CancelEvent (EventT t m) t where
cancelEvent eid = do
maybeEvent <- dequeueWhere eventQueueRef (\e -> eventId e == eid)
invokeHandler eventCancelHandler (maybe (Left eid) Right maybeEvent)
return maybeEvent
{- services for use in implementing events -}
instance Monad m => GetCurrentEvent (EventT t m) t where
getCurrentEvent = EventT (asks currentEvent)
instance Monad m => RetryEvent (EventT t m) t where
retryEventIn dt = do
maybeCe <- getCurrentEvent
case maybeCe of
Nothing -> fail "retry called outside an event"
Just (EventDescriptor {event = e}) ->
scheduleEventIn dt e
instance (Monad m, Ord t) => MonadEventQueueInfo (EventT t m) t where
eventQueueSize = withEventQueue queueSize
eventQueueContents = withEventQueue peekQueue
{- The EventT type and related administrative bits (state management stuff) -}
-- |A monad transformer which adds an event queue and related operations to
-- an underlying monad. The \"t\" parameter specifies the type of the
-- simulation time.
--
-- Several hooks are provided to allow special handling of various events,
-- such as the progression of time, the scheduling or canceling or dispatch
-- of an event, etc.
newtype EventT t m a = EventT { unWrapEventT :: RWST (EventT_RState t m) () (EventT_RWState t m) m a }
deriving (Functor, Monad, MonadIO)
instance MonadTrans (EventT t) where
lift = EventT . lift
{- Monad type-class pass-through implementations -}
instance MonadReader r m => MonadReader r (EventT t m) where
ask = EventT (lift ask)
local f (EventT x) = EventT $ do
rState <- ask
rwState <- get
(a, rwState, w) <- lift (local f (runRWST x rState rwState))
put rwState
tell w
return a
instance MonadWriter w m => MonadWriter w (EventT t m) where
tell w = EventT (lift (tell w))
listen (EventT x) = EventT $ do
rState <- ask
rwState <- get
((a, rwState, w), w2) <- lift (listen (runRWST x rState rwState))
put rwState
tell w
return (a, w2)
pass (EventT x) = EventT $ do
rState <- ask
rwState <- get
let knot = do
((a, f), rwState, w) <- runRWST x rState rwState
return ((a, rwState, w), f)
(a, rwState, w) <- lift (pass knot)
put rwState
tell w
return a
instance MonadState s m => MonadState s (EventT t m) where
get = EventT (lift get)
put s = EventT (lift (put s))
instance MonadError e m => MonadError e (EventT t m) where
throwError e = EventT (lift (throwError e))
catchError (EventT x) onErr = EventT $ do
rState <- ask
rwState <- get
let onErr' err = runRWST (unWrapEventT (onErr err)) rState rwState
(a, rwState, w) <- lift (catchError (runRWST x rState rwState) onErr')
put rwState
tell w
return a
-- is this valid?
instance MonadPlus m => MonadPlus (EventT t m) where
mzero = EventT (lift mzero)
mplus (EventT a) (EventT b) = EventT $ do
rState <- ask
rwState <- get
(c, rwState, w) <- lift (runRWST a rState rwState `mplus` runRWST b rState rwState)
put rwState
tell w
return c
-- what semantics does this inherit? is it sensible? is it unique?
instance MonadCont m => MonadCont (EventT t m) where
callCC f = EventT $
callCC $ \cont ->
unWrapEventT (f (EventT . cont))
-- |'EventM' is a shorthand for an event graph monad in IO with clock of type Double
type EventM = EventT Double IO
{- Running (EventT t m) actions and whole event graphs -}
-- |Run an 'EventT' wrapped action. This is a \"raw\" action - there is no
-- interaction with the state (including event graph) except whatever the
-- action itself does.
runEventT :: Monad m => EventT t m a -> EventT_RState t m -> EventT_RWState t m -> m (a, EventT_RWState t m)
runEventT (EventT x) rState rwState = do
(a, rwState, _) <- runRWST x rState rwState
return (a, rwState)
-- |Repeatedly pull and run the next event in the queue until it's
-- empty or until the simulation is paused using 'pauseSimulation'
-- or something equivalent.
runEventGraphWithState ::
( Monad m, Ord t, Pretty t
) => EventT_RState t m -> EventT_RWState t m -> m (EventT_RWState t m)
runEventGraphWithState rState rwState = do
(_, rwState) <- runEventT (whileJust_ dequeueNextEvent runEvent) rState rwState
return rwState
-- |Initialize the event queue and other stuff, enqueue the provided \"start
-- event\", and run the queue until it's empty or until the simulation is
-- paused.
runEventGraph ::
( Monad m, MonadEvent (EventT t m) e
, Ord t, Num t, Pretty t
) => e -> m (EventT_RState t m, EventT_RWState t m)
runEventGraph e = do
let rState = newEventT_RState
rwState = newEventT_RWState 0
(_, rwState) <- runEventT (scheduleEventIn 0 e) rState rwState
rwState <- runEventGraphWithState rState rwState
return (rState, rwState)
{- the main state vectors -}
data EventT_RState t m = EventT_RState
{ currentEvent :: Maybe (EventDescriptor (EventT t m) t)
}
data EventT_RWState t m = EventT_RWState
{ currentTime :: t
, simRunning :: Bool
, nextEventId :: EventID
, eventQueue :: EventQueue t m
, handlers :: EventTHandlers t m
}
-- |A new instance of the read/write portion of the 'EventT' internal
-- state. The parameter is the initial time value.
newEventT_RWState :: (Monad m, Ord t) => t -> EventT_RWState t m
newEventT_RWState t = EventT_RWState
{ currentTime = t
, simRunning = True
, nextEventId = EventID 1
, eventQueue = newEventQueue
, handlers = newEventTHandlers
}
-- |A new instance of the read-only portion of the 'EventT' internal
-- state.
newEventT_RState :: EventT_RState t m
newEventT_RState = EventT_RState
{ currentEvent = Nothing
}
{- Miscellaneous small state management functions -}
getNextEventId :: Monad m => EventT t m EventID
getNextEventId = do
rwState <- EventT get
let eID = nextEventId rwState
EventT (put (rwState {nextEventId = succ eID}))
return eID
{- Support for debugging event handlers -}
data EventTHandlers t m = EventTHandlers
{ eventScheduleHandler :: HandlerSet (EventT t m) (EventDescriptor (EventT t m) t) ()
, eventCancelHandler :: HandlerSet (EventT t m) (Either EventID (EventDescriptor (EventT t m) t)) ()
, eventDispatchHandler :: HandlerSet (EventT t m) (EventDescriptor (EventT t m) t) ()
, eventCompleteHandler :: HandlerSet (EventT t m) (EventDescriptor (EventT t m) t) ()
, clockChangedHandler :: HandlerSet (EventT t m) (t, t) () -- (old time, new time)
}
type HandlerAccessor t m a b =
( EventTHandlers t m -> HandlerSet (EventT t m) a b
, HandlerSet (EventT t m) a b -> EventTHandlers t m -> EventTHandlers t m
)
-- |Fires after an event is scheduled. Is passed an 'EventDescriptor' for
-- the event.
onEventSchedule :: HandlerAccessor t m (EventDescriptor (EventT t m) t) ()
onEventSchedule = (eventScheduleHandler, \h hs -> hs {eventScheduleHandler = h})
-- |Fires after an event is canceled. Is passed either an 'EventID' (if the
-- cancellation failed) or an 'EventDescriptor' for the event that was canceled.
onEventCancel :: HandlerAccessor t m (Either EventID (EventDescriptor (EventT t m) t)) ()
onEventCancel = (eventCancelHandler, \h hs -> hs {eventCancelHandler = h})
-- |Fires just before an event is dispatched. Is passed an 'EventDescriptor'
-- describing the event about to be run.
onEventDispatch :: HandlerAccessor t m (EventDescriptor (EventT t m) t) ()
onEventDispatch = (eventDispatchHandler, \h hs -> hs {eventDispatchHandler = h})
-- |Fires after an event returns. Is passed an 'EventDescriptor' for the
-- event that just finished.
onEventComplete :: HandlerAccessor t m (EventDescriptor (EventT t m) t) ()
onEventComplete = (eventCompleteHandler, \h hs -> hs {eventCompleteHandler = h})
-- |Fires whenever the clock changes, and is passed a tuple containing
-- (old time, new time)
onClockChanged :: HandlerAccessor t m (t, t) ()
onClockChanged = (clockChangedHandler, \h hs -> hs {clockChangedHandler = h})
newEventTHandlers :: Monad m => EventTHandlers t m
newEventTHandlers = EventTHandlers
{ eventScheduleHandler = emptyHandlerSet
, eventCancelHandler = emptyHandlerSet
, eventDispatchHandler = emptyHandlerSet
, eventCompleteHandler = emptyHandlerSet
, clockChangedHandler = emptyHandlerSet
}
-- |Add an event handler to be called when the specified event happens.
addHandler :: Monad m => HandlerAccessor t m a b -> (a -> EventT t m b) -> EventT t m HandlerID
addHandler (getter, setter) h = do
hSet <- EventT (gets (getter.handlers))
let (newHSet, hId) = addHandlerToSet h hSet
EventT (modify (\s -> s {handlers = setter newHSet (handlers s)}))
return hId
-- |Remove an event handler given its ID, and return it if it was in the set.
removeHandler :: Monad m => HandlerAccessor t m a b -> HandlerID -> EventT t m (Maybe (a -> EventT t m b))
removeHandler (getter, setter) hId = do
hSet <- EventT (gets (getter.handlers))
let (newHSet, h) = removeHandlerFromSet hId hSet
EventT (modify (\s -> s {handlers = setter newHSet (handlers s)}))
return h
invokeHandler :: Monad m => (EventTHandlers t m -> HandlerSet (EventT t m) a b) -> a -> EventT t m b
invokeHandler h args = do
hSet <- EventT (gets (h.handlers))
invokeHandlers hSet args
{- internal support relating to the event queue -}
type EventQueue t m = PQ (EventDescriptor (EventT t m) t)
type EventQueueRef t m = PriorityQueue (EventT t m) (EventDescriptor (EventT t m) t)
newEventQueue :: Ord t => EventQueue t m
newEventQueue = emptyPQ eventTime
withEventQueue :: (Monad m, Ord t) => (EventQueueRef t m -> EventT t m a) -> EventT t m a
withEventQueue f = f eventQueueRef
eventQueueRef :: (Monad m, Ord t) => EventQueueRef t m
eventQueueRef = mkPriorityQueue (UnsafeModifyRef (Accessor (Getter getEventQueue, Setter setEventQueue)))
where
getEventQueue = EventT (gets eventQueue)
setEventQueue eq = EventT $ do
modify (\rwState -> rwState {eventQueue = eq})
dequeueNextEvent :: (Monad m, Ord t) => EventT t m (Maybe (EventDescriptor (EventT t m) t))
dequeueNextEvent = do
running <- isSimulationRunning
if running
then withEventQueue dequeue
else return Nothing
{- Some handy event handlers for logging and speed regulation -}
{- at some point these may be extracted to a type class -}
-- |(warning: very likely to change or disappear)
--
-- A collection of 'Bool's telling 'SetDebugHandlers' which handlers to install.
data DebugHandlerOptions = DebugHandlerOptions
{ -- |Run roughly in sync with the wall clock (using seconds as the simulation time unit)
sync :: Bool
, -- |Print event dispatches to 'stderr' as they occur
showDispatches :: Bool
, -- |Print event cancellations to 'stderr' as they occur
showCancels :: Bool
, -- |Print event scheduling actions to 'stderr' as they occur
showSchedules :: Bool
, -- |Dump the entire contents of the event queue to
-- 'stderr' every time an event is dispatched
dumpQueue :: Bool
} deriving Show
defaultHandlerOptions = DebugHandlerOptions
{ sync = False
, showDispatches = False
, showCancels = False
, showSchedules = False
, dumpQueue = False
}
-- |(warning: very likely to change or disappear)
--
-- An event which installs some very primitive logging event handlers
-- useful for watching what's going on inside the event queue.
data SetDebugHandlers t (m :: * -> *) = SetDebugHandlers DebugHandlerOptions
instance Pretty (SetDebugHandlers t m) where
pretty (SetDebugHandlers opts) = text "Set debug handlers, using options" <+> colon <> text (show opts)
instance (MonadIO m, RealFrac t, Pretty t) => MonadEvent (EventT t m) (SetDebugHandlers t m) where
describeEvent e = return (pretty e)
runEvent (SetDebugHandlers opts) = do
let addHandlerIf p hSel h = if p opts then addHandler hSel h >> return () else return ()
-- very rough wallclock synchronization
addHandlerIf sync onClockChanged $ \(t1, t2) -> do
let dt = t2 - t1
let delayTime = max 0 (dt * 1000000)
liftIO (threadDelay (floor delayTime))
addHandlerIf showDispatches onEventDispatch $ \e -> do
description <- describeEvent e
liftIO (hPutDoc stderr (text "Dispatch: " <> description <> linebreak))
addHandlerIf showCancels onEventCancel $ \e -> case e of
Right e -> do
description <- describeEvent e
liftIO (hPutDoc stderr (text "Canceled" <> colon <+> description <> linebreak))
Left eid -> liftIO (hPutDoc stderr (text "Cancel" <> colon <+> text "event" <+> pretty eid <+> text "failed to be canceled - it probably already ran" <> linebreak))
addHandlerIf showSchedules onEventSchedule $ \e -> do
sz <- eventQueueSize
qc <- eventQueueContents
description <- describeEvent e
liftIO (hPutDoc stderr (text "Schedule" <> colon <+> align description <> linebreak))
addHandlerIf dumpQueue onEventDispatch $ \e -> do
qc <- eventQueueContents
descriptions <- mapM describeEvent qc
liftIO (hPutDoc stderr (text "Event Queue" <> colon <+> align (vcat descriptions) <> linebreak))
return ()