| Safe Haskell | Safe |
|---|---|
| Language | Haskell2010 |
LazyAsync
Description
What is this — A LazyAsync is an action that doesn't start right away.
When it does run, it runs in a separate thread.
How to get one — The lazyAsync function makes a LazyAsync available
within a ContT context because it ensures the asynchronous action is cancelled
when the continuation ends, to avoid accidentally leaving any unneeded threads
running in the background.
How to use it — You can incite a LazyAsync to begin by using 🚀 start,
and then you can use ⏸️ wait to block until it completes. There is also
🚀⏸️ startWait, which does both.
If the only thing you ever do with your LazyAsyncs is startWait on them,
then you may consider using memoize instead, which does not require
interacting with the LazyAsync type at all.
Synopsis
- data LazyAsync a
- lazyAsync :: MonadBaseControl IO m => m a -> ContT r m (LazyAsync (StM m a))
- startWait :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m a
- apply :: LazyAsync (a -> b) -> LazyAsync a -> LazyAsync b
- choose :: LazyAsync a -> LazyAsync a -> LazyAsync a
- merge :: (Status a -> Status b -> Status c) -> LazyAsync a -> LazyAsync b -> LazyAsync c
- startWaitCatch :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Outcome a)
- data Outcome a
- = Failure SomeException
- | Success a
- applyOutcome :: Outcome (a -> b) -> Outcome a -> Outcome b
- chooseOutcome :: Outcome a -> Outcome a -> Outcome a
- poll :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Status a)
- data Status a
- = Incomplete
- | Done (Outcome a)
- applyStatus :: Status (a -> b) -> Status a -> Status b
- chooseStatus :: Status a -> Status a -> Status a
- start :: (MonadBase base m, MonadIO base) => LazyAsync a -> m ()
- wait :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m a
- waitCatch :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Outcome a)
- acquire :: MonadBaseControl IO m => m a -> m (Resource m (LazyAsync (StM m a)))
- data Resource m a = Resource {}
- pollSTM :: LazyAsync a -> STM (Status a)
- startSTM :: LazyAsync a -> STM ()
- waitCatchSTM :: LazyAsync a -> STM (Outcome a)
- memoize :: MonadBaseControl IO m => m a -> ContT r m (m a)
- manyLazyAsyncs :: (MonadBaseControl IO m, Traversable t) => t (m a) -> ContT r m (t (LazyAsync (StM m a)))
- memoizeMany :: (MonadBaseControl IO m, Traversable t) => t (m a) -> ContT r m (t (m a))
- withLazyAsyncIO :: IO a -> (LazyAsync a -> IO b) -> IO b
- startWaitIO :: LazyAsync a -> IO a
- startWaitCatchIO :: LazyAsync a -> IO (Outcome a)
- pollIO :: LazyAsync a -> IO (Status a)
- startIO :: LazyAsync a -> IO ()
- waitIO :: LazyAsync a -> IO a
- waitCatchIO :: LazyAsync a -> IO (Outcome a)
- acquireIO :: IO a -> IO (Resource IO (LazyAsync a))
- withLazyAsyncListIO :: [IO a] -> ([LazyAsync a] -> IO b) -> IO b
- withMemoizedIO :: IO a -> (IO a -> IO b) -> IO b
- withMemoizedListIO :: [IO a] -> ([IO a] -> IO b) -> IO b
- newtype ContT (r :: k) (m :: k -> Type) a = ContT {
- runContT :: (a -> m r) -> m r
- evalContT :: Monad m => ContT r m r -> m r
- class MonadBase b m => MonadBaseControl (b :: Type -> Type) (m :: Type -> Type) | m -> b where
- class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase (b :: Type -> Type) (m :: Type -> Type) | m -> b where
- liftBase :: b α -> m α
- class Monad m => MonadIO (m :: Type -> Type) where
LazyAsync
An asynchronous action that does not start right away
Spawning
Arguments
| :: MonadBaseControl IO m | |
| => m a | Action |
| -> ContT r m (LazyAsync (StM m a)) |
Creates a situation wherein:
Getting results
Combining actions
Conjunctively combines the results of two LazyAsyncs
🚀 start starts both parts immediately
⏸️ wait returns a Success result after both
parts complete successfully. As soon as one part fails, the whole conjunction
fails immediately (but any Incomplete part keeps running in the
background)
🕵️ poll returns Failure if either part has failed;
otherwise Incomplete if either part has not finished; otherwise
Success
💣 The wait and poll operations disclose the
leftmost exception of the parts that have failed so far, which may not
be consistent over time
🌈 apply is equivalent to (merge applyStatus)
Disjunctively combines the results of two LazyAsyncs
🚀 start starts both parts immediately
⏸️ wait returns a Success result after either part
completes successfully. As soon as one part succeeds, the whole disjunction
succeeds immediately (but any Incomplete part keeps running in the
background)
🕵️ poll returns Success if either part has
succeeded; otherwise Incomplete if either part has not finished;
otherwise Failure
✅ The wait and poll operations disclose the leftmost
result of the parts that have succeeded so far, which may not be consistent
over time
🌈 choose is equivalent to (merge chooseStatus)
Catching (Outcome)
startWaitCatch :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Outcome a) Source #
🚀 Starts an asynchronous action, ⏸️ waits for it to complete, and ✅ returns its value
💣 If the action throws an exception, then the exception is returned
🌈 ( is equivalent to startWaitCatch x)(start x *> waitCatch x)
Constructors
| Failure SomeException | 💣 The |
| Success a | ✅ The |
Instances
| Foldable Outcome Source # | |
Defined in LazyAsync.Types.Outcome Methods fold :: Monoid m => Outcome m -> m # foldMap :: Monoid m => (a -> m) -> Outcome a -> m # foldMap' :: Monoid m => (a -> m) -> Outcome a -> m # foldr :: (a -> b -> b) -> b -> Outcome a -> b # foldr' :: (a -> b -> b) -> b -> Outcome a -> b # foldl :: (b -> a -> b) -> b -> Outcome a -> b # foldl' :: (b -> a -> b) -> b -> Outcome a -> b # foldr1 :: (a -> a -> a) -> Outcome a -> a # foldl1 :: (a -> a -> a) -> Outcome a -> a # elem :: Eq a => a -> Outcome a -> Bool # maximum :: Ord a => Outcome a -> a # minimum :: Ord a => Outcome a -> a # | |
| Traversable Outcome Source # | |
| Alternative Outcome Source # | 🌈 |
| Applicative Outcome Source # | 🌈 |
| Functor Outcome Source # | |
| Show a => Show (Outcome a) Source # | |
Polling (Status)
poll :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Status a) Source #
🕵️ Checks whether an asynchronous action has completed yet
🛑 Does not start the action
Constructors
| Incomplete | ⏳
The |
| Done (Outcome a) | ⌛
The |
Instances
| Foldable Status Source # | |
Defined in LazyAsync.Types.Status Methods fold :: Monoid m => Status m -> m # foldMap :: Monoid m => (a -> m) -> Status a -> m # foldMap' :: Monoid m => (a -> m) -> Status a -> m # foldr :: (a -> b -> b) -> b -> Status a -> b # foldr' :: (a -> b -> b) -> b -> Status a -> b # foldl :: (b -> a -> b) -> b -> Status a -> b # foldl' :: (b -> a -> b) -> b -> Status a -> b # foldr1 :: (a -> a -> a) -> Status a -> a # foldl1 :: (a -> a -> a) -> Status a -> a # elem :: Eq a => a -> Status a -> Bool # maximum :: Ord a => Status a -> a # minimum :: Ord a => Status a -> a # | |
| Traversable Status Source # | |
| Alternative Status Source # | 🌈 |
| Applicative Status Source # | 🌈 |
| Functor Status Source # | |
| Show a => Show (Status a) Source # | |
applyStatus :: Status (a -> b) -> Status a -> Status b Source #
Combines two LazyAsync statuses to produce the status of their
conjunction
💣 Returns the leftmost Failure, if there is one
⏳ Otherwise, if any part of a conjunction is Incomplete, then the whole thing
evaluates to Incomplete
✅ Only when all parts have completed as Success does the whole succeed
For example, = applyStatus Incomplete (Failure e)Failure e
chooseStatus :: Status a -> Status a -> Status a Source #
Combines two LazyAsync statuses to produce the status of their
disjunction
✅ Returns the leftmost Success, if there is one
⏳ Otherwise, if any part of a disjunction is Incomplete, then the whole thing
evaluates to Incomplete
💣 Only when all parts have completed as Failure does the whole fail
Starting manually
start :: (MonadBase base m, MonadIO base) => LazyAsync a -> m () Source #
🚀 Starts an asynchronous action, if it has not already been started
wait :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m a Source #
⏸️ Waits for the action to complete and ✅ returns its value
💣 If the action throws an exception, then the exception is re-thrown
🛑 Does not start the action
waitCatch :: (MonadBaseControl base m, MonadIO base) => LazyAsync (StM m a) -> m (Outcome a) Source #
⏸️ Waits for the action to complete and ✅ returns its value
💣 If the action throws an exception, then the exception is returned
🛑 Does not start the action
Manual cancellation
Arguments
| :: MonadBaseControl IO m | |
| => m a | Action |
| -> m (Resource m (LazyAsync (StM m a))) |
Like lazyAsync, but does not automatically stop the action
The returned Resource includes the desired LazyAsync (the resource), as
well as a release action that brings it to a halt. If the action is not yet
started, release prevents it from ever starting. If the action is in progress,
release throws an async exception to stop it. If the action is completed,
release has no effect.
A LazyAsync represents a background thread which may be utilizing
time and space. A running thread is not automatically reaped by the garbage
collector, so one should take care to eventually release every LazyAsync
resource to avoid accidentally leaving unwanted LazyAsyncs running.
A resource and an action that releases it
A resource is something that can be acquired and then released, where releasing an object once it is no longer needed is important because the supply is exhaustible.
Transactions
Memoization
Arguments
| :: MonadBaseControl IO m | |
| => m a | Action |
| -> ContT r m (m a) | Memoized action, in a continuation |
Creates a situation wherein:
- The action shall begin running only once the memoized action runs
- The action shall run at most once
- The action shall run only within the continuation (when the continuation ends, the action is stopped)
Bulk operations
If you have a list (or other Traversable) of actions, the "many" functions
(manyLazyAsyncs and memoizeMany) can create a thread for each action in the
list.
If you have a big recordful of actions and feel like getting real fancy, try
making your datatype "higher-kinded" and using the rank2classes package.
manyLazyAsyncs :: (MonadBaseControl IO m, Traversable t) => t (m a) -> ContT r m (t (LazyAsync (StM m a))) Source #
🌈 manyLazyAsyncs is equivalent to (traverse lazyAsync)
memoizeMany :: (MonadBaseControl IO m, Traversable t) => t (m a) -> ContT r m (t (m a)) Source #
🌈 memoizeMany is equivalent to (traverse memoize)
Notes on monads
Working with ContT — Compose actions within the ContT monadic context, and
apply evalContT at the top to run the continuation. You can also apply
runContT to a ContT action to convert it to a "continuation-passing style"
higher-order function.
Working with MonadBaseControl and StM — Most of the functions in this module
are generalized using MonadBaseControl, which allows you to work in monads
other than IO (to see an example of this, see the test suite for
this package, which creates LazyAsyncs in Hedgehog's PropertyT context).
StM is a type family which often "disappears" (that is, for
many StM m a ~ am).
Unlifted variants
If you are uninterested in monad transformers, you may prefer the functions in this section.
- All of the
mtype variables are specialized toIO, thus eliminatingMonadBase,MonadBaseControl,MonadIO, andStMfrom the types - Async spawning is done with explicit continuation passing instead of
ContTactions Traversable-constrained type constructors are specialized to[]
startWaitCatchIO :: LazyAsync a -> IO (Outcome a) Source #
Akin to startWaitCatch
withLazyAsyncListIO :: [IO a] -> ([LazyAsync a] -> IO b) -> IO b Source #
Akin to manyLazyAsyncs
withMemoizedListIO :: [IO a] -> ([IO a] -> IO b) -> IO b Source #
Akin to memoizeMany
Re-exports
Some key monad lifting concepts from other packages are re-exported from this module.
base (Control.Monad.IO.Class)
transformers (Control.Monad.Trans.Cont)
monad-base (Control.Monad.Base)
monad-control (Control.Monad.Trans.Control)
newtype ContT (r :: k) (m :: k -> Type) a #
The continuation monad transformer.
Can be used to add continuation handling to any type constructor:
the Monad instance and most of the operations do not require m
to be a monad.
ContT is not a functor on the category of monads, and many operations
cannot be lifted through it.
Instances
| MonadBase b m => MonadBase b (ContT r m) | |
Defined in Control.Monad.Base | |
| MonadTrans (ContT r) | |
Defined in Control.Monad.Trans.Cont | |
| MonadFail m => MonadFail (ContT r m) | |
Defined in Control.Monad.Trans.Cont | |
| MonadIO m => MonadIO (ContT r m) | |
Defined in Control.Monad.Trans.Cont | |
| Applicative (ContT r m) | |
Defined in Control.Monad.Trans.Cont | |
| Functor (ContT r m) | |
| Monad (ContT r m) | |
| MonadThrow m => MonadThrow (ContT r m) | |
Defined in Control.Monad.Catch | |
class MonadBase b m => MonadBaseControl (b :: Type -> Type) (m :: Type -> Type) | m -> b where #
Writing instances
The usual way to write a instance for a transformer
stack over a base monad MonadBaseControlB is to write an instance MonadBaseControl B B
for the base monad, and MonadTransControl T instances for every transformer
T. Instances for are then simply implemented using
MonadBaseControl, ComposeSt, defaultLiftBaseWith.defaultRestoreM
Associated Types
type StM (m :: Type -> Type) a #
Monadic state that m adds to the base monad b.
For all base (non-transformed) monads, StM m a ~ a:
StMIOa ~ a StMMaybea ~ a StM (Eithere) a ~ a StM [] a ~ a StM ((->) r) a ~ a StMIdentitya ~ a StMSTMa ~ a StM (STs) a ~ a
If m is a transformed monad, m ~ t b, is the monadic state of
the transformer StMt (given by its StT from MonadTransControl). For a
transformer stack, is defined recursively:StM
StM (IdentityTm) a ~ComposeStIdentityTm a ~ StM m a StM (MaybeTm) a ~ComposeStMaybeTm a ~ StM m (Maybea) StM (ErrorTe m) a ~ComposeStErrorTm a ~Errore => StM m (Eithere a) StM (ExceptTe m) a ~ComposeStExceptTm a ~ StM m (Eithere a) StM (ListTm) a ~ComposeStListTm a ~ StM m [a] StM (ReaderTr m) a ~ComposeStReaderTm a ~ StM m a StM (StateTs m) a ~ComposeStStateTm a ~ StM m (a, s) StM (WriterTw m) a ~ComposeStWriterTm a ~Monoidw => StM m (a, w) StM (RWSTr w s m) a ~ComposeStRWSTm a ~Monoidw => StM m (a, s, w)
Methods
liftBaseWith :: (RunInBase m b -> b a) -> m a #
liftBaseWith is similar to liftIO and liftBase in that it
lifts a base computation to the constructed monad.
Instances should satisfy similar laws as the MonadIO and MonadBase laws:
liftBaseWith (\_ -> return a) = return a
liftBaseWith (\_ -> m >>= f) = liftBaseWith (\_ -> m) >>= (\a -> liftBaseWith (\_ -> f a))
As Li-yao Xia explains, parametricity guarantees that
f $ liftBaseWith q = liftBaseWith $ runInBase -> f $ q runInBase
The difference with liftBase is that before lifting the base computation
liftBaseWith captures the state of m. It then provides the base
computation with a RunInBase function that allows running m
computations in the base monad on the captured state:
withFileLifted :: MonadBaseControl IO m => FilePath -> IOMode -> (Handle -> m a) -> m a
withFileLifted file mode action = liftBaseWith (\runInBase -> withFile file mode (runInBase . action)) >>= restoreM
-- = control $ \runInBase -> withFile file mode (runInBase . action)
-- = liftBaseOp (withFile file mode) action
is usually not implemented directly, but using
liftBaseWith.defaultLiftBaseWith
Construct a m computation from the monadic state of m that is
returned from a RunInBase function.
Instances should satisfy:
liftBaseWith (\runInBase -> runInBase m) >>= restoreM = m
is usually not implemented directly, but using
restoreM.defaultRestoreM
Instances
class (Applicative b, Applicative m, Monad b, Monad m) => MonadBase (b :: Type -> Type) (m :: Type -> Type) | m -> b where #
Instances
class Monad m => MonadIO (m :: Type -> Type) where #
Monads in which IO computations may be embedded.
Any monad built by applying a sequence of monad transformers to the
IO monad will be an instance of this class.
Instances should satisfy the following laws, which state that liftIO
is a transformer of monads:
Methods
Lift a computation from the IO monad.
This allows us to run IO computations in any monadic stack, so long as it supports these kinds of operations
(i.e. IO is the base monad for the stack).
Example
import Control.Monad.Trans.State -- from the "transformers" library printState :: Show s => StateT s IO () printState = do state <- get liftIO $ print state
Had we omitted , we would have ended up with this error:liftIO
• Couldn't match type ‘IO’ with ‘StateT s IO’ Expected type: StateT s IO () Actual type: IO ()
The important part here is the mismatch between StateT s IO () and .IO ()
Luckily, we know of a function that takes an and returns an IO a(m a): ,
enabling us to run the program and see the expected results:liftIO
> evalStateT printState "hello" "hello" > evalStateT printState 3 3