Safe Haskell | Safe |
---|---|
Language | Haskell2010 |
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 LazyAsync
s 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))
- memoizeRank2 :: (MonadBaseControl IO m, Traversable t) => t m -> ContT r m (t m)
- 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
:: 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 LazyAsync
s
🚀 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 LazyAsync
s
🚀 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)
Failure SomeException | 💣 The |
Success a | ✅ The |
Instances
Functor Outcome Source # | |
Applicative Outcome Source # | 🌈 |
Foldable Outcome Source # | |
Defined in LazyAsync.Types.Outcome 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 # | 🌈 |
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
Incomplete | ⏳
The |
Done (Outcome a) | ⌛
The |
Instances
Functor Status Source # | |
Applicative Status Source # | 🌈 |
Foldable Status Source # | |
Defined in LazyAsync.Types.Status 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 # | 🌈 |
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
:: 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 LazyAsync
s 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
:: 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 memoizeRank2
to automatically
create a bunch of threads at once. You'll need the rank2classes
package; see
Rank2 and Rank2.TH.
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
)
memoizeRank2 :: (MonadBaseControl IO m, Traversable t) => t m -> ContT r m (t m) Source #
🌈 memoizeRank2
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 LazyAsync
s 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
m
type variables are specialized toIO
, thus eliminatingMonadBase
,MonadBaseControl
,MonadIO
, andStM
from the types - Async spawning is done with explicit continuation passing instead of
ContT
actions 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 | |
Monad (ContT r m) | |
Functor (ContT r m) | |
MonadFail m => MonadFail (ContT r m) | |
Defined in Control.Monad.Trans.Cont | |
Applicative (ContT r m) | |
MonadIO m => MonadIO (ContT r m) | |
Defined in Control.Monad.Trans.Cont | |
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 MonadBaseControl
B
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
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
:
StMIO
a ~ a StMMaybe
a ~ a StM (Either
e) a ~ a StM [] a ~ a StM ((->) r) a ~ a StMIdentity
a ~ a StMSTM
a ~ a StM (ST
s) a ~ a
If m
is a transformed monad, m ~ t b
,
is the monadic state of
the transformer StM
t
(given by its StT
from MonadTransControl
). For a
transformer stack,
is defined recursively:StM
StM (IdentityT
m) a ~ComposeSt
IdentityT
m a ~ StM m a StM (MaybeT
m) a ~ComposeSt
MaybeT
m a ~ StM m (Maybe
a) StM (ErrorT
e m) a ~ComposeSt
ErrorT
m a ~Error
e => StM m (Either
e a) StM (ExceptT
e m) a ~ComposeSt
ExceptT
m a ~ StM m (Either
e a) StM (ListT
m) a ~ComposeSt
ListT
m a ~ StM m [a] StM (ReaderT
r m) a ~ComposeSt
ReaderT
m a ~ StM m a StM (StateT
s m) a ~ComposeSt
StateT
m a ~ StM m (a, s) StM (WriterT
w m) a ~ComposeSt
WriterT
m a ~Monoid
w => StM m (a, w) StM (RWST
r w s m) a ~ComposeSt
RWST
m a ~Monoid
w => StM m (a, s, w)
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: