-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Pure deterministic scheduled computations -- -- Schedule computations to run later, in a pure and deterministic way. -- -- This library is a pure alternative to System.Timeout suitable -- for IO-bound non-blocking computations. System.Timeout has a -- few issues that are at-odds with a Haskell or purely functional -- paradigm: (1) it is not deterministic, (2) the timeout state is not -- serialisable, and (3) the timeout functionality must be shared between -- unrelated components, making it harder to design components that are -- easily decomposable and reusable. -- -- This library solves these issues by implementing all schedule and -- timeout logic as a pure deterministic computation, with callbacks -- represented in defunctionalised serialisable form. The interface with -- the runtime execution environment is minimal: a simple source of clock -- inputs similar to other inputs such as network traffic or user -- commands, which can either be an IO-based impure "real" runtime, or a -- pure "mock" one e.g. that replays previous inputs to reproduce -- previous outputs. -- -- This library does no pre-emption e.g. by sending interrupts or -- asynchronous exceptions, so it is probably not suitable for -- blocking computations. To be clear, things will work, but clock -- inputs will be delivered only after the blocking is over. A workaround -- is to separate the blocking computations from your main computation, -- arrange to have these run externally (e.g. in worker threads) with the -- results being sent back to your main computation via some pure -- abstract input interface, similar to how we deliver clock inputs. -- -- If this is not suitable and you absolutely need pre-emption, then -- you'll need a richer runtime interface than the one expected by this -- library; luckily the Haskell runtime itself is such an example. In -- other words, simply use other existing IO-based utilities for setting -- timeouts, that typically rely on concurrency or asynchronous -- exceptions. But then, you'll have to figure out your own way of -- overcoming the issues mentioned in the first paragraph. -- -- The original motivation for this library comes from implementing -- secure communications protocols and decentralised distributed systems. -- In these contexts one must often set local timeouts for remote events -- that may or may not happen in the future, or periodically synchronise -- local views of shared data with remote peers. Most operations are -- IO-bound and can be written to be non-blocking; the main exception is -- heavy cryptography which can be delegated to worker threads as -- described above. Of course, this library is not tied to these -- use-cases and is a general replacement for System.Timeout. -- -- See Control.Monad.Schedule for the main monad-based API of this -- library. -- -- See Control.Arrow.Schedule for the main arrow-based API of this -- library. -- -- See Control.Clock.IO for various ways of combining clock inputs -- with other inputs and injecting them into your pure computations. -- -- See Control.Schedule.* for higher-level utilities that one -- often wants to use on top of a timeout primitive, such as futures and -- monitors. -- -- See unit tests for example usage. @package schedule @version 0.2.0.0 -- | Extra utilities and abstractions for Control.Monad.Primitive. -- -- The API structure is stable, but the naming is not great and may -- change. Ideally we would push this upstream into -- Control.Monad.Primitive itself. module Control.Monad.Primitive.Extra -- | Type abstracting a mutable reference. -- -- This can be thought of as a mutable version of a Lens' (PrimState -- m) s with the lens functor specialised to (,) a for each -- a. newtype PrimST m s PrimST :: (forall a. (s -> (a, s)) -> m a) -> PrimST m s [statePrimST] :: PrimST m s -> forall a. (s -> (a, s)) -> m a readPrimST :: PrimST m s -> m s writePrimST :: PrimST m s -> s -> m () modifyPrimST :: PrimST m s -> (s -> s) -> m () stMutVar :: PrimMonad m => MutVar (PrimState m) s -> PrimST m s -- | Class of monads which can perform primitive state-transformer actions class Monad m => PrimMonad (m :: Type -> Type) where { -- | State token type type family PrimState (m :: Type -> Type) :: Type; } -- | Common utilities for implementing reservation data structures. -- -- A reservation data structure is one that allows multiple -- inserts of the same item, by returning a unique handle for each insert -- operation that must be given to the delete operation. -- -- If you need to store the handle together with the item, e.g. so that -- the item knows how to delete itself, this can be achieved via the -- standard Haskell "tying the knot" technique. -- -- This API is experimental at the moment, and parts of it may -- change. module Data.Rsv.Common -- | Handle generator. Runtime invariants: -- --
    --
  1. A handle from one generator is not used in a context that expects -- a handle from a different generator. TODO: use a string or other data -- to distinguish the contexts.
  2. --
  3. Newly generated handles are distinguishable from -- previously-generated ones. checkHandle is used to check -- this.
  4. --
newtype RHandles RHandles :: RHandle -> RHandles [getNextHandle] :: RHandles -> RHandle data RHandle newHandles :: RHandles nextHandle :: RHandles -> (RHandle, RHandles) -- | Check that an existing handle is consistent with the current state of -- a handle generator, i.e. it must not be part of the generator's -- future. checkHandle :: RHandles -> RHandle -> Bool withHandle :: ((RHandle, i) -> c -> c) -> i -> (RHandles, c) -> (RHandle, (RHandles, c)) sEnqueue :: a -> Seq a -> Seq a sUnqueue :: (HasCallStack, Eq k) => k -> Seq (k, a) -> (Maybe a, Seq (k, a)) sDequeue :: Seq a -> (Maybe a, Seq a) instance GHC.Classes.Eq Data.Rsv.Common.RHandles instance GHC.Generics.Generic Data.Rsv.Common.RHandles instance GHC.Read.Read Data.Rsv.Common.RHandles instance GHC.Show.Show Data.Rsv.Common.RHandles instance GHC.Enum.Bounded Data.Rsv.Common.RHandle instance GHC.Enum.Enum Data.Rsv.Common.RHandle instance GHC.Classes.Ord Data.Rsv.Common.RHandle instance GHC.Classes.Eq Data.Rsv.Common.RHandle instance GHC.Generics.Generic Data.Rsv.Common.RHandle instance GHC.Read.Read Data.Rsv.Common.RHandle instance GHC.Show.Show Data.Rsv.Common.RHandle -- | This module implements a reservation multi-map. -- -- Each insert is indexed by a key; many inserts (of the same or -- different items) may be performed on the same key. -- -- A reservation data structure is one that allows multiple -- inserts of the same item, by returning a unique handle for each insert -- operation that must be given to the delete operation. -- -- This API is experimental at the moment, and parts of it may -- change. module Data.Rsv.RMMap data RMMap k a RMMap :: !RHandles -> !Map k (Entries a) -> RMMap k a [handles] :: RMMap k a -> !RHandles [content] :: RMMap k a -> !Map k (Entries a) _handles :: forall k_alMa a_alMb. Lens' (RMMap k_alMa a_alMb) RHandles _content :: forall k_alMa a_alMb k_an9C a_an9D. Lens (RMMap k_alMa a_alMb) (RMMap k_an9C a_an9D) (Map k_alMa (Entries a_alMb)) (Map k_an9C (Entries a_an9D)) data Delete k a -- | Check the map that its internal invariants all hold. -- -- You must run this on every instance obtained not via the API functions -- here. For example, you must run this on instances obtained via -- deserialisation, which in general cannot check the complex invariants -- maintained by the API functions. Also, for all handles you obtain via -- a similarly non-standard method, including by deserialisation of a -- parent data structure, you must run checkHandle map -- handle. -- -- Nothing means the check passed; Just errmsg -- gives a failure reason. -- -- Note: this does not guard against all malicious behaviour, but it does -- guard against violation (either malicious or accidental) of the -- runtime invariants assumed by this data structure. checkValidity :: RMMap k a -> Maybe Text -- | Check that an existing handle is consistent with the current state of -- the structure, i.e. it is not a handle that could be generated in the -- future. checkHandle :: RMMap k a -> Delete k a -> Bool empty :: RMMap k a isEmpty :: RMMap k a -> Bool (!) :: Ord k => RMMap k a -> k -> Seq a toList :: RMMap k a -> [Delete k a] -- | Append an item on a key, returning a handle to remove it with. The -- same item may be added twice, in which case it will occupy multiple -- positions in the map, and the handles distinguish these occurences. enqueue :: Ord k => (k, a) -> RMMap k a -> (Delete k a, RMMap k a) -- | Delete an item corresponding to a given handle. If the item was -- already removed, Nothing is returned instead. unqueue :: Ord k => Delete k a -> RMMap k a -> (Maybe (k, a), RMMap k a) -- | Remove an item from a key, from the front. Return Nothing if key is -- empty. dequeue :: Ord k => k -> RMMap k a -> (Maybe (Delete k a, a), RMMap k a) instance GHC.Classes.Ord k => GHC.Classes.Ord (Data.Rsv.RMMap.Delete k a) instance GHC.Classes.Eq k => GHC.Classes.Eq (Data.Rsv.RMMap.Delete k a) instance GHC.Generics.Generic (Data.Rsv.RMMap.Delete k a) instance GHC.Read.Read k => GHC.Read.Read (Data.Rsv.RMMap.Delete k a) instance GHC.Show.Show k => GHC.Show.Show (Data.Rsv.RMMap.Delete k a) instance (GHC.Classes.Eq k, GHC.Classes.Eq a) => GHC.Classes.Eq (Data.Rsv.RMMap.RMMap k a) instance GHC.Generics.Generic (Data.Rsv.RMMap.RMMap k a) instance (GHC.Classes.Ord k, GHC.Read.Read k, GHC.Read.Read a) => GHC.Read.Read (Data.Rsv.RMMap.RMMap k a) instance (GHC.Show.Show k, GHC.Show.Show a) => GHC.Show.Show (Data.Rsv.RMMap.RMMap k a) module Data.Schedule.Internal type Tick = Integer type TickDelta = Word64 -- | A task that is currently or was part of a schedule. -- -- t is the type of input parameter for each task, i.e. the task -- contents. newtype Task t Task :: Delete Tick t -> Task t -- | The current status of a task as returned by taskStatus. data TaskStatus t -- | The task is not pending - either it was already run, or cancelled. TaskNotPending :: TaskStatus t -- | The task is due to run at some future tick. TaskPending :: !Tick -> !t -> TaskStatus t -- | The task is running right now. TaskRunning :: !t -> TaskStatus t -- | The state of all scheduled pending tasks. -- -- t is the type of task-params. data Schedule t Schedule :: !Tick -> !RMMap Tick t -> !Set (Task t) -> !Maybe (Task t, t) -> Schedule t [now] :: Schedule t -> !Tick [tasks] :: Schedule t -> !RMMap Tick t [pending] :: Schedule t -> !Set (Task t) [running] :: Schedule t -> !Maybe (Task t, t) newSchedule :: Schedule t -- | Check the schedule that its internal invariants all hold. -- -- You must run this on every instance obtained not via the API functions -- here. For example, you must run this on instances obtained via -- deserialisation, which in general cannot check the complex invariants -- maintained by the API functions. Also, for all Tasks you obtain -- via a similarly non-standard method, including by deserialisation of a -- parent data structure, you must run checkTask schedule -- task. -- -- Nothing means the check passed; Just errmsg -- gives a failure reason. -- -- Note: this does not guard against all malicious behaviour, but it does -- guard against violation (either malicious or accidental) of the -- runtime invariants assumed by this data structure. checkValidity :: Schedule t -> Maybe Text -- | Check that an existing task is consistent with the current state of -- the structure, i.e. it is not a task that could be generated in the -- future. checkTask :: Schedule t -> Task t -> Bool -- | Get the current tick, whose tasks have not all run yet. -- -- From the perspective of the pure computation that is running this -- schedule, you should treat this as the current "logical time", even if -- an impure clock is telling you that the "environment time" is in the -- future. tickNow :: Schedule t -> Tick -- | Get the previous tick, whose tasks have all already run. tickPrev :: Schedule t -> Tick -- | Get the number of ticks until the next scheduled task. -- -- This may be used by an impure runtime environment to set an actual -- timeout; see Control.Clock for a starting point. ticksToIdle :: Schedule t -> Maybe TickDelta taskStatus :: HasCallStack => Task t -> Schedule t -> TaskStatus t -- | Schedule a task to run after a given number of ticks. -- -- This is relative to tickNow; a 0 delta schedules the -- task to be run at the end of the current tick, i.e. as soon as -- possible but not immediately. -- -- If your task params needs to refer to the task itself, you may achieve -- this by using the standard Haskell "tying the knot" technique, e.g.: -- --
--   >>> data TPar = TPar !(Task TPar) deriving (Show, Eq)
--   
--   >>> s = newSchedule
--   
--   >>> let (t, s') = after 1 (TPar t) s -- @t@ on LHS & RHS, tying the knot
--   
--   >>> t
--   Task (Delete 1 (RHandle {getHandle = 0}))
--   
--   >>> taskStatus t s
--   TaskNotPending
--   
--   >>> taskStatus t s'
--   TaskPending 1 (TPar (Task (Delete 1 (RHandle {getHandle = 0}))))
--   
--   >>> taskStatus t s' == TaskPending 1 (TPar t)
--   True
--   
after :: TickDelta -> t -> Schedule t -> (Task t, Schedule t) -- | Cancel a task. Result is Nothing if task was not already pending. cancel :: Task t -> Schedule t -> (Maybe t, Schedule t) -- | Cancel a task, discarding the result. cancel_ :: Task t -> Schedule t -> ((), Schedule t) -- | Reschedule a pending task to instead run after a given number of -- ticks. -- -- If the task was not already pending, do nothing. If you need to -- reschedule a task unconditionally even if it was already cancelled or -- run, use both cancel_ and after in combination. renew :: TickDelta -> Task t -> Schedule t -> (Maybe (Task t), Schedule t) -- | Pop the next task to be run in this tick. If there are no more tasks -- remaining, then advance to the next tick. popOrTick :: HasCallStack => Schedule t -> (Maybe (Task t, t), Schedule t) -- | Lock the schedule before running a particular task. -- -- This prevents popOrTick from being called, or other tasks from -- running. It is not re-entrant; only one task is supposed to run at -- once. acquireTask :: HasCallStack => (Task t, t) -> Schedule t -> Schedule t -- | Unlock the schedule after running a particular task. -- -- This allows popOrTick to be called again and other tasks to run. It is -- not re-entrant; only one task is supposed to run at once. releaseTask :: HasCallStack => Task t -> Schedule t -> Schedule t instance GHC.Classes.Eq t => GHC.Classes.Eq (Data.Schedule.Internal.Schedule t) instance GHC.Generics.Generic (Data.Schedule.Internal.Schedule t) instance GHC.Read.Read t => GHC.Read.Read (Data.Schedule.Internal.Schedule t) instance GHC.Show.Show t => GHC.Show.Show (Data.Schedule.Internal.Schedule t) instance GHC.Classes.Ord t => GHC.Classes.Ord (Data.Schedule.Internal.TaskStatus t) instance GHC.Classes.Eq t => GHC.Classes.Eq (Data.Schedule.Internal.TaskStatus t) instance GHC.Generics.Generic (Data.Schedule.Internal.TaskStatus t) instance GHC.Read.Read t => GHC.Read.Read (Data.Schedule.Internal.TaskStatus t) instance GHC.Show.Show t => GHC.Show.Show (Data.Schedule.Internal.TaskStatus t) instance GHC.Classes.Ord (Data.Schedule.Internal.Task t) instance GHC.Classes.Eq (Data.Schedule.Internal.Task t) instance GHC.Generics.Generic (Data.Schedule.Internal.Task t) instance GHC.Read.Read (Data.Schedule.Internal.Task t) instance GHC.Show.Show (Data.Schedule.Internal.Task t) -- | Data structure representing scheduled tasks. -- -- Most of the time you will want the more fully-featured -- Control.Monad.Schedule or Control.Arrow.Schedule modules -- instead, which re-export this module. module Data.Schedule type Tick = Integer type TickDelta = Word64 -- | A task that is currently or was part of a schedule. -- -- t is the type of input parameter for each task, i.e. the task -- contents. data Task t -- | The current status of a task as returned by taskStatus. data TaskStatus t -- | The task is not pending - either it was already run, or cancelled. TaskNotPending :: TaskStatus t -- | The task is due to run at some future tick. TaskPending :: !Tick -> !t -> TaskStatus t -- | The task is running right now. TaskRunning :: !t -> TaskStatus t -- | The state of all scheduled pending tasks. -- -- t is the type of task-params. data Schedule t newSchedule :: Schedule t -- | Check the schedule that its internal invariants all hold. -- -- You must run this on every instance obtained not via the API functions -- here. For example, you must run this on instances obtained via -- deserialisation, which in general cannot check the complex invariants -- maintained by the API functions. Also, for all Tasks you obtain -- via a similarly non-standard method, including by deserialisation of a -- parent data structure, you must run checkTask schedule -- task. -- -- Nothing means the check passed; Just errmsg -- gives a failure reason. -- -- Note: this does not guard against all malicious behaviour, but it does -- guard against violation (either malicious or accidental) of the -- runtime invariants assumed by this data structure. checkValidity :: Schedule t -> Maybe Text -- | Check that an existing task is consistent with the current state of -- the structure, i.e. it is not a task that could be generated in the -- future. checkTask :: Schedule t -> Task t -> Bool -- | Get the current tick, whose tasks have not all run yet. -- -- From the perspective of the pure computation that is running this -- schedule, you should treat this as the current "logical time", even if -- an impure clock is telling you that the "environment time" is in the -- future. tickNow :: Schedule t -> Tick -- | Get the previous tick, whose tasks have all already run. tickPrev :: Schedule t -> Tick -- | Get the number of ticks until the next scheduled task. -- -- This may be used by an impure runtime environment to set an actual -- timeout; see Control.Clock for a starting point. ticksToIdle :: Schedule t -> Maybe TickDelta taskStatus :: HasCallStack => Task t -> Schedule t -> TaskStatus t -- | Schedule a task to run after a given number of ticks. -- -- This is relative to tickNow; a 0 delta schedules the -- task to be run at the end of the current tick, i.e. as soon as -- possible but not immediately. -- -- If your task params needs to refer to the task itself, you may achieve -- this by using the standard Haskell "tying the knot" technique, e.g.: -- --
--   >>> data TPar = TPar !(Task TPar) deriving (Show, Eq)
--   
--   >>> s = newSchedule
--   
--   >>> let (t, s') = after 1 (TPar t) s -- @t@ on LHS & RHS, tying the knot
--   
--   >>> t
--   Task (Delete 1 (RHandle {getHandle = 0}))
--   
--   >>> taskStatus t s
--   TaskNotPending
--   
--   >>> taskStatus t s'
--   TaskPending 1 (TPar (Task (Delete 1 (RHandle {getHandle = 0}))))
--   
--   >>> taskStatus t s' == TaskPending 1 (TPar t)
--   True
--   
after :: TickDelta -> t -> Schedule t -> (Task t, Schedule t) -- | Cancel a task. Result is Nothing if task was not already pending. cancel :: Task t -> Schedule t -> (Maybe t, Schedule t) -- | Cancel a task, discarding the result. cancel_ :: Task t -> Schedule t -> ((), Schedule t) -- | Reschedule a pending task to instead run after a given number of -- ticks. -- -- If the task was not already pending, do nothing. If you need to -- reschedule a task unconditionally even if it was already cancelled or -- run, use both cancel_ and after in combination. renew :: TickDelta -> Task t -> Schedule t -> (Maybe (Task t), Schedule t) -- | Run an action, accumulating its monoid result until it returns -- Nothing. -- -- TODO: export to upstream extra whileJustM :: (Monad m, Monoid a) => m (Maybe a) -> m a -- | Convert a modification function into a state transition function. modST :: (s -> s) -> s -> ((), s) -- | Convert a getter function into a state transition function. getST :: (s -> o) -> s -> (o, s) -- | Convert a state transition function into a state transition arrow. stA :: (s -> os) -> (i, s) -> os -- | Convert a modification arrow into a state transition arrow. imodA :: (i -> s -> s) -> (i, s) -> ((), s) -- | Convert a getter function into a state transition arrow. getA :: (s -> a) -> (i, s) -> (a, s) -- | Pure serialisable futures. -- -- This API is experimental at the moment, and parts of it may -- change. module Control.Schedule.Future type OSet a = Set a type OMap k v = Map k v data TimedResult tk r TimedOut :: !tk -> TimedResult tk r GotResult :: !r -> TimedResult tk r data SFuture wo ro -- | SExpects waiting on us SFWaiting :: !OSet wo -> SFuture wo ro -- | Result of the Future SFResult :: !ro -> SFuture wo ro _SFResult :: forall wo_avlh ro_avli ro_avEg. Prism (SFuture wo_avlh ro_avEg) (SFuture wo_avlh ro_avli) ro_avEg ro_avli _SFWaiting :: forall wo_avlh ro_avli wo_avEb. Prism (SFuture wo_avEb ro_avli) (SFuture wo_avlh ro_avli) (OSet wo_avEb) (OSet wo_avlh) data SExpect wi ri tk SExpect :: !OMap wi (Task tk) -> !OMap wi (TimedResult tk ri) -> SExpect wi ri tk -- | SFutures we're waiting for, with our own timeout. -- -- Note that the SFuture might have its own separate timeout which is -- different; this t timeout is when *we* stop waiting on it. -- -- For example if (i ~ TimedResult a) and our timeout is longer -- than their timeout then seResults will get a GotResult -- (TimedOut t). [seExpects] :: SExpect wi ri tk -> !OMap wi (Task tk) -- | SFutures that have completed, with the result. This is meant to be a -- holding place and the caller of this should move items from here into -- some other place to indicate that the results have been processed, so -- that if it is called twice it does not process these results twice. [seResults] :: SExpect wi ri tk -> !OMap wi (TimedResult tk ri) _seResults :: forall wi_avEr ri_avEs tk_avEt ri_aw0V. Lens (SExpect wi_avEr ri_avEs tk_avEt) (SExpect wi_avEr ri_aw0V tk_avEt) (OMap wi_avEr (TimedResult tk_avEt ri_avEs)) (OMap wi_avEr (TimedResult tk_avEt ri_aw0V)) _seExpects :: forall wi_avEr ri_avEs tk_avEt. Lens' (SExpect wi_avEr ri_avEs tk_avEt) (OMap wi_avEr (Task tk_avEt)) data SFStatus e Expecting :: e -> SFStatus e NotExpecting :: SFStatus e type SFStatusFull wo tk = SFStatus (OSet wo, Task tk) data SFError SFEAlreadyFinished :: SFError SFEInvalidPrecondition :: !SFStatus () -> !SFStatus () -> SFError [sfePreExpect] :: SFError -> !SFStatus () [sfePreActual] :: SFError -> !SFStatus () sCheckStatus :: (HasCallStack, Ord wi, Ord wo) => wi -> wo -> Lens' s (SFuture wo r) -> Lens' s (SExpect wi r tk) -> s -> SFStatusFull wo tk sExpectFuture :: (Ord wi, Ord wo) => TickDelta -> tk -> wi -> wo -> Lens' s (SFuture wo r) -> Lens' s (SExpect wi r tk) -> Lens' s (Schedule tk) -> s -> Either SFError s sExpectCancel :: (Ord wi, Ord wo) => wi -> wo -> Lens' s (SFuture wo r) -> Lens' s (SExpect wi r tk) -> Lens' s (Schedule tk) -> s -> Either SFError s sExpectTimeout :: (HasCallStack, Ord wi, Ord wo) => tk -> wi -> wo -> Lens' s (SFuture wo r) -> Lens' s (SExpect wi r tk) -> Lens' s (Schedule tk) -> s -> Either SFError s sFutureResult :: (Ord wi, Ord wo) => r -> wi -> Lens' s (SFuture wo r) -> IndexedTraversal' wo s (SExpect wi r tk) -> Lens' s (Schedule tk) -> s -> Either SFError s instance GHC.Classes.Ord Control.Schedule.Future.SFError instance GHC.Classes.Eq Control.Schedule.Future.SFError instance GHC.Generics.Generic Control.Schedule.Future.SFError instance GHC.Read.Read Control.Schedule.Future.SFError instance GHC.Show.Show Control.Schedule.Future.SFError instance GHC.Classes.Ord e => GHC.Classes.Ord (Control.Schedule.Future.SFStatus e) instance GHC.Classes.Eq e => GHC.Classes.Eq (Control.Schedule.Future.SFStatus e) instance GHC.Generics.Generic (Control.Schedule.Future.SFStatus e) instance GHC.Read.Read e => GHC.Read.Read (Control.Schedule.Future.SFStatus e) instance GHC.Show.Show e => GHC.Show.Show (Control.Schedule.Future.SFStatus e) instance GHC.Classes.Ord wi => GHC.Base.Semigroup (Control.Schedule.Future.SExpect wi ri tk) instance GHC.Classes.Ord wi => GHC.Base.Monoid (Control.Schedule.Future.SExpect wi ri tk) instance (GHC.Classes.Ord wi, GHC.Classes.Ord tk, GHC.Classes.Ord ri) => GHC.Classes.Ord (Control.Schedule.Future.SExpect wi ri tk) instance (GHC.Classes.Eq wi, GHC.Classes.Eq tk, GHC.Classes.Eq ri) => GHC.Classes.Eq (Control.Schedule.Future.SExpect wi ri tk) instance GHC.Generics.Generic (Control.Schedule.Future.SExpect wi ri tk) instance (GHC.Classes.Ord wi, GHC.Read.Read wi, GHC.Read.Read tk, GHC.Read.Read ri) => GHC.Read.Read (Control.Schedule.Future.SExpect wi ri tk) instance (GHC.Show.Show wi, GHC.Show.Show tk, GHC.Show.Show ri) => GHC.Show.Show (Control.Schedule.Future.SExpect wi ri tk) instance (GHC.Classes.Ord wo, GHC.Classes.Ord ro) => GHC.Classes.Ord (Control.Schedule.Future.SFuture wo ro) instance (GHC.Classes.Eq wo, GHC.Classes.Eq ro) => GHC.Classes.Eq (Control.Schedule.Future.SFuture wo ro) instance GHC.Generics.Generic (Control.Schedule.Future.SFuture wo ro) instance (GHC.Classes.Ord wo, GHC.Read.Read wo, GHC.Read.Read ro) => GHC.Read.Read (Control.Schedule.Future.SFuture wo ro) instance (GHC.Show.Show wo, GHC.Show.Show ro) => GHC.Show.Show (Control.Schedule.Future.SFuture wo ro) instance (GHC.Classes.Ord tk, GHC.Classes.Ord r) => GHC.Classes.Ord (Control.Schedule.Future.TimedResult tk r) instance (GHC.Classes.Eq tk, GHC.Classes.Eq r) => GHC.Classes.Eq (Control.Schedule.Future.TimedResult tk r) instance GHC.Generics.Generic (Control.Schedule.Future.TimedResult tk r) instance (GHC.Read.Read tk, GHC.Read.Read r) => GHC.Read.Read (Control.Schedule.Future.TimedResult tk r) instance (GHC.Show.Show tk, GHC.Show.Show r) => GHC.Show.Show (Control.Schedule.Future.TimedResult tk r) -- | Pure abstractions for time and clocks. module Control.Clock -- | 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 monotonic. See -- System.Time.Monotonic for an example on how to wrap -- non-monotonic clocks to be monotonic. data Clock c Clock :: !c Tick -> !TickDelta -> c () -> !forall a. c a -> c (Clocked c a) -> !forall a. TickDelta -> c a -> c (Either Tick a) -> Clock c -- | Get the current time. [clockNow] :: Clock c -> !c Tick -- | Suspend the current computation for a given number of ticks. -- -- Nothing else in the computation runs until the suspension is over. -- Afterwards, clockNow will give the expected value, i.e. for all -- n: -- --
--   do
--       old <- clockNow
--       clockDelay n
--       new <- clockNow
--       let new' = assert (old + n <= new) new
--   
-- -- The relation is <= not ==, because the computer might -- have slept during the mean time or something. On the other hand, if -- the underlying physical clock might delay for a shorter period than -- requested, then implementations of this function must -- loop-delay until the <= condition is satisfied. -- -- The above is the only condition that scheduled computations should -- rely on, and any actual physical real delay is up to the -- implementation. [clockDelay] :: Clock c -> !TickDelta -> c () -- | Interleave actions with ticks. -- -- This is typically recommended for the use-case where your action -- represents a stream of inputs, e.g. from the network or the user. It -- is meant to satisfy the same functionality as the select -- system call found in common operating systems, used with a timeout -- parameter. -- -- If action when executed repeatedly gives a sequence of -- results, then in the expression clkAct <- clockWith -- clock action, a subsequent call to runClocked -- clkAct when executed repeatedly gives the same sequence of -- results but with ticks interleaved in between them. Executing -- finClocked clkAct closes any resources and invalidates -- any future calls to clkAct. -- -- It is not necessary to call finClocked if any part of -- runClocked (e.g. child threads) throws an exception - -- implementations should detect these situations and clean these up -- automatically. This frees the user of this function from having to add -- extra constraints which would be the case if it had been necessary to -- run finally ... (finClocked clkAct) as cleanup. [clockWith] :: Clock c -> !forall a. c a -> c (Clocked c a) -- | Given an action, run it with a timeout. -- -- This is typically recommended for the use-case where your action -- represents the response to a single previously-sent request. -- -- The action may complete despite the timeout firing, in which case its -- result will be lost. This is in general unavoidable and is a common -- property that one simply has to live with in distributed systems. If -- you run the input action repeatedly, then this property applies *for -- every execution*, i.e. it is possible that you get 10 timeouts even -- though the action succeeded 10 times, and you'll lose 10 results. -- -- If you want all results of all actions, use clockWith -- instead. The downside with that, is that it's slightly less efficient -- than this, as it will interleave every single Tick event and it -- is up to you to deal with skipping/ignoring any of them. [clockTimer] :: Clock c -> !forall a. TickDelta -> c a -> c (Either Tick a) -- | Run clockDelay then clockNow. clockTick :: Monad c => Clock c -> TickDelta -> c Tick -- | See clockWith for details on what this is for. data Clocked c a Clocked :: !c (Either Tick a) -> !c () -> Clocked c a [runClocked] :: Clocked c a -> !c (Either Tick a) [finClocked] :: Clocked c a -> !c () type TickDelta = Word64 type Tick = Integer -- | Implementations of Clock in the IO monad. module Control.Clock.IO -- | Create a new clock ticking at a given interval. newClock :: DiffTime -> IO (Clock IO) -- | Create a new clock ticking at a given interval in picoseconds. newClockPico :: Integer -> IO (Clock IO) -- | Create a new clock ticking at a given interval in milliseconds. newClockMilli :: Integer -> IO (Clock IO) -- | Create a new clock ticking at 1 millisecond. newClock1ms :: IO (Clock IO) -- | Create a new clock ticking at 1 second. newClock1s :: IO (Clock IO) -- | Convert a System.Time.Monotonic.Clock into an abstract -- Clock for scheduled computations, ticking at the given -- interval. convClock :: DiffTime -> Clock -> Clock IO clockWithIO :: Clock IO -> IO a -> IO (Clocked IO a) clockTimerIO :: Clock IO -> TickDelta -> IO a -> IO (Either Tick a) voidInput :: IO Void -- | Run scheduled computations in any (stateful) monad, using an adapter. -- -- This module mostly contains utilities for dealing with clock inputs. -- To get or set the existing timeouts, use your RunSched adapter -- on one of the functions from Data.Schedule, which this module -- also re-exports. module Control.Monad.Schedule -- | Something that can run Schedule state transition functions. -- -- This could be pure (e.g. StateT) or impure (e.g. reference to a -- PrimST). -- -- Examples: -- --
--   primState :: PrimMonad m => RunSched t (ReaderT (PrimST m (Schedule t)) m)
--   primState sched = asks statePrimST >>= run -> lift (run sched)
--   
--   state :: Monad m => RunSched t (StateT (Schedule t) m)
--   zoom _lens . state :: Monad m => RunSched t (StateT s m)
--   
-- -- See the unit tests for more examples. type RunSched t m = forall a. (Schedule t -> (a, Schedule t)) -> m a runTick :: (Monad m, Monoid a) => RunSched t m -> (t -> m a) -> m a runTicksTo :: (Monad m, Monoid a) => RunSched t m -> (Tick -> t -> m a) -> Tick -> m a getInput :: Monad m => RunSched t m -> (TickDelta -> m (Either Tick i)) -> m (Either Tick i) mkOutput :: (Monad m, Monoid a) => RunSched t m -> (Tick -> t -> m a) -> (i -> m a) -> Either Tick i -> m a -- | A more general version of mkOutput that uses a -- Prism-like optic. -- -- Given an inner computation it -> m a where one branch of -- the it type has a (Tick, t) tuple -- representing individual input tasks, return an outer computation of -- type i -> m a where the i type only has a -- Tick. When the outer computation receives these Tick -- inputs, it automatically resolves the relevant tasks of type -- t that are active for that Tick, and passes each tuple -- in sequence to the wrapped inner computation. tickTask :: (Monad m, Monoid a) => RunSched t m -> (forall f. Applicative f => (Tick -> f (Tick, t)) -> i -> f it) -> (it -> m a) -> i -> m a -- | Run scheduled computations in any (stateful) arrow, using an adapter. -- -- This module mostly contains utilities for dealing with clock inputs. -- To get or set the existing timeouts, use your RunSched adapter -- on one of the functions from Data.Schedule, which this module -- also re-exports. module Control.Arrow.Schedule -- | Something that can run Schedule state transition arrows. -- -- This could be pure (e.g. StateArrow) or impure (e.g. reference -- to a PrimST). type RunSched t a = forall i o. ((i, Schedule t) -> (o, Schedule t)) -> a i o runTick :: (ArrowChoice a, Monoid o) => RunSched t a -> a (Tick, t) o -> a Tick o runTicksTo :: (ArrowChoice a, Monoid o) => RunSched t a -> a (Tick, t) o -> a Tick o getInput :: Arrow a => RunSched t a -> a TickDelta (Either Tick i) -> a i' (Either Tick i) mkOutput :: (ArrowChoice a, Monoid o) => RunSched t a -> a (Tick, t) o -> a i o -> a (Either Tick i) o -- | A more general version of mkOutput that uses a -- Prism-like optic. -- -- Given an inner computation a it o where one branch of the -- it type has a (Tick, t) tuple representing -- individual input tasks, return an outer computation of type a i -- o where the i type only has a Tick. When the -- outer computation receives these Tick inputs, it automatically -- resolves the relevant tasks of type t that are active for -- that Tick, and passes each tuple in sequence to the wrapped -- inner computation. tickTask :: (ArrowChoice a, ArrowApply a, Monoid o) => RunSched t a -> (forall f. Applicative f => (Tick -> f (Tick, t)) -> i -> f it) -> a it o -> a i o