Safe Haskell | None |
---|---|
Language | Haskell2010 |
Pure scheduled computations, as a monad transformer.
- type Tick = Sum Integer
- data Clock c = Clock {
- clockNow :: c Tick
- clockDelay :: Tick -> c ()
- type ScheduleT c m = ComposeT (ReaderT (Clock c)) (StateT (TaskState c m)) m
- type Task c m = ScheduleT c m ()
- type TaskCancel c m = ScheduleT c m (Maybe (Task c m))
- data TaskState c m
- getClock :: Monad m => ScheduleT c m (Clock c)
- tickNow :: Monad m => ScheduleT c m Tick
- tickPrev :: Monad m => ScheduleT c m Tick
- runTasksTo :: Monad m => Tick -> ScheduleT c m ()
- runScheduleT_ :: Monad m => ScheduleT c m a -> Clock c -> m (a, TaskState c m)
- after :: Monad m => Tick -> Task c m -> ScheduleT c m (TaskCancel c m)
- renew :: Monad m => Tick -> TaskCancel c m -> ScheduleT c m (Maybe (TaskCancel c m))
- type LiftClock c m = forall a. c a -> m a
- class MonadClock c m where
- defaultLiftClock :: (MonadClock c m, Monad m, MonadTrans t) => LiftClock c (t m)
- type LiftRT c n m = forall a. n a -> ScheduleT c m a
- class MonadRT c b m where
- defaultLiftRT :: (MonadRT c b m, Monad m, MonadTrans t) => LiftRT c b (t m)
- getClockNow' :: Monad m => LiftClock c m -> ScheduleT c m Tick
- runTasks' :: Monad m => LiftClock c m -> ScheduleT c m ()
- runScheduleT' :: Monad m => LiftClock c m -> ScheduleT c m a -> Clock c -> m (a, TaskState c m)
Documentation
A maybe-impure supplier of time, to a pure scheduled computation.
The type c
is the computational context where clock operations occur,
e.g. a Monad
such as IO
.
Clock implementations must be monotic. See System.Time.Monotonic for an example on how to wrap non-monotonic clocks. TODO: provide a generic monotonic wrapper.
Clock | |
|
Pure scheduled computation
type ScheduleT c m = ComposeT (ReaderT (Clock c)) (StateT (TaskState c m)) m Source
A computation that can schedule sub-computations for later.
We use ComposeT
so we need only one lift
from the inner monad m
.
TODO: There is an MFunctor
instance for the underlying type ComposeT
,
but that's not what we want here - a hoist must also morph the TaskState
.
So we should probably wrap this in a newtype and define a proper MFunctor
instance that isn't just hoist
from ComposeT
. However, this is probably
impossible with how ScheduleT
is currently defined; see
Control.Monad.Trans.Schedule.ExampleMFunctor
for details.
The solution would likely involve adding a s
type parameter for the state
that is independent of m
. This would increase complexity; however a
MFunctor
instance is quite important for this monad to be composeable
with other monads.
type Task c m = ScheduleT c m () Source
A task to run, in response to a tick event. This can be any computation.
In other programming contexts, this would be analogous to a callback, subscriber, observer, timeout, etc etc.
type TaskCancel c m = ScheduleT c m (Maybe (Task c m)) Source
Cancel a task.
Which task to cancel, is implicitly bound to each instance of this type.
See after
for more details.
tickPrev :: Monad m => ScheduleT c m Tick Source
Get the previous tick, whose tasks have all already run.
runTasksTo :: Monad m => Tick -> ScheduleT c m () Source
runScheduleT_ :: Monad m => ScheduleT c m a -> Clock c -> m (a, TaskState c m) Source
Run a scheduled computation starting from tick 0.
after :: Monad m => Tick -> Task c m -> ScheduleT c m (TaskCancel c m) Source
Schedule a task to run after a given number of ticks.
renew :: Monad m => Tick -> TaskCancel c m -> ScheduleT c m (Maybe (TaskCancel c m)) Source
Re-schedule a task to instead run after a given number of ticks. If the task was already cancelled, do nothing.
Generic impure execution
type LiftClock c m = forall a. c a -> m a Source
Lift a clock computation into the scheduled computation's context.
We use a type synonym instead of a typeclass, so that we can avoid overlapping instances such as these:
instance MonadBase c m => MonadClock c m (ScheduleT c m) instance MonadIO m => MonadClock IO m (ScheduleT IO m)
but still write generic code like runScheduleT'
to be useful externally.
class MonadClock c m where Source
A monad that can lift clock operations.
defaultLiftClock :: (MonadClock c m, Monad m, MonadTrans t) => LiftClock c (t m) Source
Helps to derive new instances of MonadClock
from base instances.
type LiftRT c n m = forall a. n a -> ScheduleT c m a Source
Lift some deeply-inner computation n
into a scheduled computation,
running tasks in parallel while the computation is still pending.
defaultLiftRT :: (MonadRT c b m, Monad m, MonadTrans t) => LiftRT c b (t m) Source
Helps to derive new instances of MonadRT
from base instances.
Don't use this yet, it's undefined right now.