2-- Module : Streamly.Internal.Data.Stream.IsStream.Exception
3-- Copyright : (c) 2019 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9module Streamly.Internal.Data.Stream.IsStream.Exception
26import Control.Exception (Exception)
27import Control.Monad.Catch (MonadCatch)
28import Control.Monad.IO.Class (MonadIO(..))
29import Control.Monad.Trans.Control (MonadBaseControl)
30import Data.Map.Strict (Map)
31import Streamly.Internal.Control.Concurrent (MonadAsync)
32import Streamly.Internal.Data.Stream.IsStream.Type
33 (IsStream(..), fromStreamD, toStreamD)
35import qualified Streamly.Internal.Data.Stream.StreamD as D
39-- >>> import qualified Streamly.Prelude as Stream
40-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream (nilM)
42------------------------------------------------------------------------------
44------------------------------------------------------------------------------
46-- | Run the action @m b@ before the stream yields its first element.
48-- Same as the following but more efficient due to fusion:
50-- >>> before action xs = Stream.nilM action <> xs
51-- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)
55before :: (IsStream t, Monad m) => m b -> t m a -> t m a
56before action xs = fromStreamD $ D.before action $ toStreamD xs
58-- | Like 'after', with following differences:
60-- * action @m b@ won't run if the stream is garbage collected
61-- after partial evaluation.
62-- * Monad @m@ does not require any other constraints.
63-- * has slightly better performance than 'after'.
65-- Same as the following, but with stream fusion:
67-- > after_ action xs = xs <> 'nilM' action
72after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
73after_ action xs = fromStreamD $ D.after_ action $ toStreamD xs
75-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
76-- is garbage collected after a partial lazy evaluation.
78-- The semantics of the action @m b@ are similar to the semantics of cleanup
79-- action in 'bracket'.
86after :: (IsStream t, MonadIO m, MonadBaseControl IO m)
87 => m b -> t m a -> t m a
88after action xs = fromStreamD $ D.after action $ toStreamD xs
90-- | Run the action @m b@ if the stream aborts due to an exception. The
91-- exception is not caught, simply rethrown.
93-- /Inhibits stream fusion/
96{-# INLINE onException #-}
97onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
98onException action xs = fromStreamD $ D.onException action $ toStreamD xs
100-- | Like 'finally' with following differences:
102-- * action @m b@ won't run if the stream is garbage collected
103-- after partial evaluation.
104-- * does not require a 'MonadAsync' constraint.
105-- * has slightly better performance than 'finally'.
107-- /Inhibits stream fusion/
111{-# INLINE finally_ #-}
112finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
113finally_ action xs = fromStreamD $ D.finally_ action $ toStreamD xs
115-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
116-- due to an exception or if it is garbage collected after a partial lazy
119-- The semantics of running the action @m b@ are similar to the cleanup action
120-- semantics described in 'bracket'.
123-- finally release = bracket (return ()) (const release)
126-- /See also 'finally_'/
128-- /Inhibits stream fusion/
132{-# INLINE finally #-}
133finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
134finally action xs = fromStreamD $ D.finally action $ toStreamD xs
136-- | Like 'bracket' but with following differences:
138-- * alloc action @m b@ runs with async exceptions enabled
139-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
140-- after partial evaluation.
141-- * does not require a 'MonadAsync' constraint.
142-- * has slightly better performance than 'bracket'.
144-- /Inhibits stream fusion/
148{-# INLINE bracket_ #-}
149bracket_ :: (IsStream t, MonadCatch m)
150 => m b -> (b -> m c) -> (b -> t m a) -> t m a
151bracket_ bef aft bet = fromStreamD $
152 D.bracket_ bef aft (toStreamD . bet)
154-- | Run the alloc action @m b@ with async exceptions disabled but keeping
155-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
156-- output @b@ as input to @b -> t m a@ to generate an output stream.
158-- @b@ is usually a resource under the state of monad @m@, e.g. a file
159-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
160-- runs whenever the stream ends normally, due to a sync or async exception or
161-- if it gets garbage collected after a partial lazy evaluation.
163-- 'bracket' only guarantees that the cleanup action runs, and it runs with
164-- async exceptions enabled. The action must ensure that it can successfully
165-- cleanup the resource in the face of sync or async exceptions.
167-- When the stream ends normally or on a sync exception, cleanup action runs
168-- immediately in the current thread context, whereas in other cases it runs in
169-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
171-- /See also: 'bracket_'/
173-- /Inhibits stream fusion/
177{-# INLINE bracket #-}
178bracket :: (IsStream t, MonadAsync m, MonadCatch m)
179 => m b -> (b -> m c) -> (b -> t m a) -> t m a
180bracket bef aft = bracket' bef aft aft aft
182-- For a use case of this see the "streamly-process" package. It needs to kill
183-- the process in case of exception or garbage collection, but waits for the
184-- process to terminate in normal cases.
186-- | Like 'bracket' but can use separate cleanup actions depending on the mode
187-- of termination. @bracket' before onStop onGC onException action@ runs
188-- @action@ using the result of @before@. If the stream stops, @onStop@ action
189-- is executed, if the stream is abandoned @onGC@ is executed, if the stream
190-- encounters an exception @onException@ is executed.
193{-# INLINE bracket' #-}
194bracket' :: (IsStream t, MonadAsync m, MonadCatch m)
195 => m b -> (b -> m c) -> (b -> m d) -> (b -> m e) -> (b -> t m a) -> t m a
196bracket' bef aft gc exc bet = fromStreamD $
197 D.bracket' bef aft exc gc (toStreamD . bet)
199-- | Like 'handle' but the exception handler is also provided with the stream
200-- that generated the exception as input. The exception handler can thus
201-- re-evaluate the stream to retry the action that failed. The exception
202-- handler can again call 'ghandle' on it to retry the action multiple times.
204-- This is highly experimental. In a stream of actions we can map the stream
205-- with a retry combinator to retry each action on failure.
207-- /Inhibits stream fusion/
211{-# INLINE ghandle #-}
212ghandle :: (IsStream t, MonadCatch m, Exception e)
213 => (e -> t m a -> t m a) -> t m a -> t m a
216 . D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))
219-- | When evaluating a stream if an exception occurs, stream evaluation aborts
220-- and the specified exception handler is run with the exception as argument.
222-- /Inhibits stream fusion/
226handle :: (IsStream t, MonadCatch m, Exception e)
227 => (e -> t m a) -> t m a -> t m a
229 fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs
232-- | @retry@ takes 3 arguments
234-- 1. A map @m@ whose keys are exceptions and values are the number of times to
235-- retry the action given that the exception occurs.
237-- 2. A handler @han@ that decides how to handle an exception when the exception
240-- 3. The stream itself that we want to run this mechanism on.
242-- When evaluating a stream if an exception occurs,
244-- 1. The stream evaluation aborts
246-- 2. The exception is looked up in @m@
248-- a. If the exception exists and the mapped value is > 0 then,
250-- i. The value is decreased by 1.
252-- ii. The stream is resumed from where the exception was called, retrying
255-- b. If the exception exists and the mapped value is == 0 then the stream
258-- c. If the exception does not exist then we handle the exception using
264retry :: (IsStream t, MonadCatch m, Exception e, Ord e)
266 -- ^ map from exception to retry count
268 -- ^ default handler for those exceptions that are not in the map
271retry emap handler inp =
272 fromStreamD $ D.retry emap (toStreamD . handler) $ toStreamD inp
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list