on master help sign in
Improve documentation of exception handling ops
commit862c47ffc96490d562ed79401023e0b871fcebdb authorHarendra Kumar <harendra@composewell.com> dateSat 31 Oct 2020, 12:54 (6 years ago) parentfa492b8
4 files changed+241−104
src/Streamly/Internal/Data/Stream/IsStream.hs+92−24 src/Streamly/Internal/Data/Stream/StreamD.hs+49−42 src/Streamly/Internal/Data/Unfold.hs+95−38 src/Streamly/Prelude.hs+5
src/Streamly/Internal/Data/Stream/IsStream.hs+92−24
@@ -537,6 +537,7 @@ module Streamly.Internal.Data.Stream.IsStream
537537 , onException
538538 , finally_
539539 , finally
540+ , ghandle
540541 , handle
541542
542543 -- * Generalize Inner Monad
@@ -5021,7 +5022,7 @@ classifySessionsOf interval =
50215022-- Exceptions
50225023------------------------------------------------------------------------------
50235024
5024−-- | Run a side effect before the stream yields its first element.
5025+-- | Run the action @m b@ before the stream yields its first element.
50255026--
50265027-- > before action xs = 'nilM' action <> xs
50275028--
@@ -5030,12 +5031,16 @@ classifySessionsOf interval =
50305031before :: (IsStream t, Monad m) => m b -> t m a -> t m a
50315032before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
50325033
5033−-- | Run a side effect at the end of the stream. The side effect won't run if
5034−-- the stream is garbage collected before it reached the end.
5034+-- | Like 'after', with following differences:
50355035--
5036−-- > after_ action xs = xs <> 'nilM' action
5036+-- * action @m b@ won't run if the stream is garbage collected
5037+-- after partial evaluation.
5038+-- * Monad @m@ does not require any other constraints.
5039+-- * has slightly better performance than 'after'.
5040+--
5041+-- Same as the following, but with stream fusion:
50375042--
5038−-- This has slightly better performance than 'after'.
5043+-- > after_ action xs = xs <> 'nilM' action
50395044--
50405045-- /Internal/
50415046--
@@ -5043,8 +5048,13 @@ before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
50435048after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
50445049after_ action xs = D.fromStreamD $ D.after_ action $ D.toStreamD xs
50455050
5046−-- | Run a side effect at the end of the stream or if it is garbage collected
5047−-- even before reaching the end.
5051+-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
5052+-- is garbage collected after a partial lazy evaluation.
5053+--
5054+-- The semantics of the action @m b@ are similar to the semantics of cleanup
5055+-- action in 'bracket'.
5056+--
5057+-- /See also 'after_'/
50485058--
50495059-- @since 0.7.0
50505060--
@@ -5053,18 +5063,24 @@ after :: (IsStream t, MonadIO m, MonadBaseControl IO m)
50535063 => m b -> t m a -> t m a
50545064after action xs = D.fromStreamD $ D.after action $ D.toStreamD xs
50555065
5056−-- | Run a side effect whenever the stream aborts due to an exception.
5066+-- | Run the action @m b@ if the stream aborts due to an exception. The
5067+-- exception is not caught, simply rethrown.
5068+--
5069+-- /Inhibits stream fusion/
50575070--
50585071-- @since 0.7.0
50595072{-# INLINE onException #-}
50605073onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
50615074onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
50625075
5063−-- | Run a side effect at the end of the stream or if it aborts due to an
5064−-- exception before it could reach the end. The side effect is not run if the
5065−-- stream is garbage collected before reaching the end.
5076+-- | Like 'finally' with following differences:
5077+--
5078+-- * action @m b@ won't run if the stream is garbage collected
5079+-- after partial evaluation.
5080+-- * does not require a 'MonadAsync' constraint.
5081+-- * has slightly better performance than 'finally'.
50665082--
5067−-- This has slightly better performance than 'finally'.
5083+-- /Inhibits stream fusion/
50685084--
50695085-- /Internal/
50705086--
@@ -5072,8 +5088,20 @@ onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
50725088finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
50735089finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
50745090
5075−-- | Run a side effect at the end of the stream, or if it aborts due to an
5076−-- exception or if it is garbage collected before it could reach the end.
5091+-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
5092+-- due to an exception or if it is garbage collected after a partial lazy
5093+-- evaluation.
5094+--
5095+-- The semantics of running the action @m b@ are similar to the cleanup action
5096+-- semantics described in 'bracket'.
5097+--
5098+-- @
5099+-- finally release = bracket (return ()) (const release)
5100+-- @
5101+--
5102+-- /See also 'finally_'/
5103+--
5104+-- /Inhibits stream fusion/
50775105--
50785106-- @since 0.7.0
50795107--
@@ -5081,13 +5109,15 @@ finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
50815109finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
50825110finally action xs = D.fromStreamD $ D.finally action $ D.toStreamD xs
50835111
5084−-- | Run the first action before the stream starts and remember its output,
5085−-- generate a stream using the output, run the second action using the
5086−-- remembered value as an argument whenever the stream ends normally or due to
5087−-- an exception. The second action won't run if the stream is garbage collected
5088−-- before it could reach the end.
5112+-- | Like 'bracket' but with following differences:
5113+--
5114+-- * alloc action @m b@ runs with async exceptions enabled
5115+-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
5116+-- after partial evaluation.
5117+-- * does not require a 'MonadAsync' constraint.
5118+-- * has slightly better performance than 'bracket'.
50895119--
5090−-- This has slightly better performance than 'bracket'.
5120+-- /Inhibits stream fusion/
50915121--
50925122-- /Internal/
50935123--
@@ -5097,10 +5127,26 @@ bracket_ :: (IsStream t, MonadCatch m)
50975127bracket_ bef aft bet = D.fromStreamD $
50985128 D.bracket_ bef aft (toStreamD . bet)
50995129
5100−-- | Run the first action before the stream starts and remember its output,
5101−-- generate a stream using the output, run the second action using the
5102−-- remembered value as an argument whenever the stream ends normally, due to
5103−-- an exception or if it is garbage collected after a partial lazy evaluation.
5130+-- | Run the alloc action @m b@ with async exceptions disabled but keeping
5131+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
5132+-- output @b@ as input to @b -> t m a@ to generate an output stream.
5133+--
5134+-- @b@ is usually a resource under the state of monad @m@, e.g. a file
5135+-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
5136+-- runs whenever the stream ends normally, due to a sync or async exception or
5137+-- if it gets garbage collected after a partial lazy evaluation.
5138+--
5139+-- 'bracket' only guarantees that the cleanup action runs, and it runs with
5140+-- async exceptions enabled. The action must ensure that it can successfully
5141+-- cleanup the resource in the face of sync or async exceptions.
5142+--
5143+-- When the stream ends normally or on a sync exception, cleanup action runs
5144+-- immediately in the current thread context, whereas in other cases it runs in
5145+-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
5146+--
5147+-- /See also: 'bracket_'/
5148+--
5149+-- /Inhibits stream fusion/
51045150--
51055151-- @since 0.7.0
51065152--
@@ -5110,9 +5156,31 @@ bracket :: (IsStream t, MonadAsync m, MonadCatch m)
51105156bracket bef aft bet = D.fromStreamD $
51115157 D.bracket bef aft (toStreamD . bet)
51125158
5159+-- | Like 'handle' but the exception handler is also provided with the stream
5160+-- that generated the exception as input. The exception handler can thus
5161+-- re-evaluate the stream to retry the action that failed. The exception
5162+-- handler can again call 'ghandle' on it to retry the action multiple times.
5163+--
5164+-- This is highly experimental. In a stream of actions we can map the stream
5165+-- with a retry combinator to retry each action on failure.
5166+--
5167+-- /Inhibits stream fusion/
5168+--
5169+-- /Internal/
5170+--
5171+{-# INLINE ghandle #-}
5172+ghandle :: (IsStream t, MonadCatch m, Exception e)
5173+ => (e -> t m a -> t m a) -> t m a -> t m a
5174+ghandle handler =
5175+ D.fromStreamD
5176+ . D.ghandle (\e xs -> D.toStreamD $ handler e (D.fromStreamD xs))
5177+ . D.toStreamD
5178+
51135179-- | When evaluating a stream if an exception occurs, stream evaluation aborts
51145180-- and the specified exception handler is run with the exception as argument.
51155181--
5182+-- /Inhibits stream fusion/
5183+--
51165184-- @since 0.7.0
51175185{-# INLINE handle #-}
51185186handle :: (IsStream t, MonadCatch m, Exception e)
src/Streamly/Internal/Data/Stream/StreamD.hs+49−42
@@ -3266,9 +3266,14 @@ data GbracketState s1 s2 v
32663266 | GBracketNormal s1 v
32673267 | GBracketException s2
32683268
3269−-- | The most general bracketing and exception combinator. All other
3270−-- combinators can be expressed in terms of this combinator. This can also be
3271−-- used for cases which are not covered by the standard combinators.
3269+-- | Like 'gbracket' but with following differences:
3270+--
3271+-- * alloc action @m c@ runs with async exceptions enabled
3272+-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
3273+-- after partial evaluation.
3274+-- * does not require a 'MonadAsync' constraint.
3275+--
3276+-- /Inhibits stream fusion/
32723277--
32733278-- /Internal/
32743279--
@@ -3299,6 +3304,7 @@ gbracket_ bef exc aft fexc fnormal =
32993304 return $ Yield x (GBracketNormal (Stream step1 s) v)
33003305 Skip s -> return $ Skip (GBracketNormal (Stream step1 s) v)
33013306 Stop -> aft v >> return Stop
3307+ -- XXX Do not handle async exceptions, just rethrow them.
33023308 Left e ->
33033309 return $ Skip (GBracketException (fexc v e (UnStream step1 st)))
33043310 step gst (GBracketException (UnStream step1 st)) = do
@@ -3380,15 +3386,22 @@ data GbracketIOState s1 s2 v wref
33803386 | GBracketIONormal s1 v wref
33813387 | GBracketIOException s2
33823388
3383−-- | The most general bracketing and exception combinator. All other
3384−-- combinators can be expressed in terms of this combinator. This can also be
3385−-- used for cases which are not covered by the standard combinators.
3389+-- | Run the alloc action @m c@ with async exceptions disabled but keeping
3390+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
3391+-- output @c@ as input to @c -> Stream m b@ to generate an output stream. When
3392+-- generating the stream use the supplied @try@ operation @forall s. m s -> m
3393+-- (Either e s)@ to catch synchronous exceptions. If an exception occurs run
3394+-- the exception handler @c -> e -> Stream m b -> m (Stream m b)@.
33863395--
3387−-- | It uses a finalizer to make sure we run the finalizing action
3388−-- when the stream is garbage collected.
3396+-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
3397+-- to a sync or async exception or if it gets garbage collected after a partial
3398+-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
33893399--
3390−-- /Internal/
3400+-- 'gbracket' can express all other exception handling combinators.
33913401--
3402+-- /Inhibits stream fusion/
3403+--
3404+-- /Internal/
33923405{-# INLINE_NORMAL gbracket #-}
33933406gbracket
33943407 :: (MonadIO m, MonadBaseControl IO m)
@@ -3429,6 +3442,7 @@ gbracket bef exc aft fexc fnormal =
34293442 Stop -> do
34303443 runIORefFinalizer ref
34313444 return Stop
3445+ -- XXX Do not handle async exceptions, just rethrow them.
34323446 Left e -> do
34333447 -- Clearing of finalizer and running of exception handler must
34343448 -- be atomic wrt async exceptions. Otherwise if we have cleared
@@ -3444,7 +3458,8 @@ gbracket bef exc aft fexc fnormal =
34443458 Skip s -> return $ Skip (GBracketIOException (Stream step1 s))
34453459 Stop -> return Stop
34463460
3447−-- | Run a side effect before the stream yields its first element.
3461+-- | See 'Streamly.Internal.Data.Stream.IsStream.after_'.
3462+--
34483463{-# INLINE_NORMAL before #-}
34493464before :: Monad m => m b -> Stream m a -> Stream m a
34503465before action (Stream step state) = Stream step' Nothing
@@ -3461,7 +3476,8 @@ before action (Stream step state) = Stream step' Nothing
34613476 Skip s -> return $ Skip (Just s)
34623477 Stop -> return Stop
34633478
3464−-- | Run a side effect whenever the stream stops normally.
3479+-- | See 'Streamly.Internal.Data.Stream.IsStream.after_'.
3480+--
34653481{-# INLINE_NORMAL after_ #-}
34663482after_ :: Monad m => m b -> Stream m a -> Stream m a
34673483after_ action (Stream step state) = Stream step' state
@@ -3476,6 +3492,8 @@ after_ action (Stream step state) = Stream step' state
34763492 Skip s -> return $ Skip s
34773493 Stop -> action >> return Stop
34783494
3495+-- | See 'Streamly.Internal.Data.Stream.IsStream.after'.
3496+--
34793497{-# INLINE_NORMAL after #-}
34803498after :: (MonadIO m, MonadBaseControl IO m)
34813499 => m b -> Stream m a -> Stream m a
@@ -3496,17 +3514,11 @@ after action (Stream step state) = Stream step' Nothing
34963514 runIORefFinalizer ref
34973515 return Stop
34983516
3499−-- XXX These combinators are expensive due to the call to
3500−-- onException/handle/try on each step. Therefore, when possible, they should
3501−-- be called in an outer loop where we perform less iterations. For example, we
3502−-- cannot call them on each iteration in a char stream, instead we can call
3503−-- them when doing an IO on an array.
3504−--
35053517-- XXX For high performance error checks in busy streams we may need another
35063518-- Error constructor in step.
35073519--
3508−-- | Run a side effect whenever the stream aborts due to an exception. The
3509−-- exception is not caught, simply rethrown.
3520+-- | See 'Streamly.Internal.Data.Stream.IsStream.onException'.
3521+--
35103522{-# INLINE_NORMAL onException #-}
35113523onException :: MonadCatch m => m b -> Stream m a -> Stream m a
35123524onException action str =
@@ -3528,24 +3540,16 @@ _onException action (Stream step state) = Stream step' state
35283540 Skip s -> return $ Skip s
35293541 Stop -> return Stop
35303542
3531−-- XXX bracket is like concatMap, it generates a stream and then flattens it.
3532−-- Like concatMap it has 10x worse performance compared to linear fused
3533−-- compositions.
3543+-- | See 'Streamly.Internal.Data.Stream.IsStream.bracket_'.
35343544--
3535−-- | Run the first action before the stream starts and remember its output,
3536−-- generate a stream using the output, run the second action providing the
3537−-- remembered value as an argument whenever the stream ends normally or due to
3538−-- an exception.
35393545{-# INLINE_NORMAL bracket_ #-}
3540−bracket_ :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3546+bracket_ :: MonadCatch m
3547+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
35413548bracket_ bef aft bet =
35423549 gbracket_ bef MC.try aft
35433550 (\a (e :: SomeException) _ -> nilM (aft a >> MC.throwM e)) bet
35443551
3545−-- | Run the first action before the stream starts and remember its output,
3546−-- generate a stream using the output, run the second action providing the
3547−-- remembered value as an argument whenever the stream ends normally or due to
3548−-- an exception or gets garbage collected.
3552+-- | See 'Streamly.Internal.Data.Stream.IsStream.bracket'.
35493553--
35503554{-# INLINE_NORMAL bracket #-}
35513555bracket :: (MonadAsync m, MonadCatch m)
@@ -3556,8 +3560,11 @@ bracket bef aft bet =
35563560
35573561data BracketState s v = BracketInit | BracketRun s v
35583562
3563+-- | Alternate (custom) implementation of 'bracket'.
3564+--
35593565{-# INLINE_NORMAL _bracket #-}
3560−_bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3566+_bracket :: MonadCatch m
3567+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
35613568_bracket bef aft bet = Stream step' BracketInit
35623569
35633570 where
@@ -3577,23 +3584,21 @@ _bracket bef aft bet = Stream step' BracketInit
35773584 Skip s -> return $ Skip (BracketRun (Stream step s) v)
35783585 Stop -> aft v >> return Stop
35793586
3580−-- | Run a side effect whenever the stream stops normally or aborts due to an
3581−-- exception.
3587+-- | See 'Streamly.Internal.Data.Stream.IsStream.finally_'.
3588+--
35823589{-# INLINE finally_ #-}
35833590finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
3584−-- finally action xs = after action $ onException action xs
35853591finally_ action xs = bracket_ (return ()) (\_ -> action) (const xs)
35863592
3587−-- | Run a side effect whenever the stream stops normally or aborts due to an
3588−-- exception or gets garbage collected.
3593+-- | See 'Streamly.Internal.Data.Stream.IsStream.finally'.
3594+--
3595+-- finally action xs = after action $ onException action xs
35893596--
35903597{-# INLINE finally #-}
35913598finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
35923599finally action xs = bracket (return ()) (\_ -> action) (const xs)
35933600
3594−-- | When evaluating a stream if an exception occurs, stream
3595−-- evaluation aborts and the specified exception handler is run with
3596−-- the exception and the Stream which threw the exception as argument.
3601+-- | See 'Streamly.Internal.Data.Stream.IsStream.ghandle'.
35973602--
35983603{-# INLINE_NORMAL ghandle #-}
35993604ghandle :: (MonadCatch m, Exception e)
@@ -3601,14 +3606,16 @@ ghandle :: (MonadCatch m, Exception e)
36013606ghandle f str =
36023607 gbracket_ (return ()) MC.try return (\_ -> f) (\_ -> str)
36033608
3604−-- | When evaluating a stream if an exception occurs, stream evaluation aborts
3605−-- and the specified exception handler is run with the exception as argument.
3609+-- | See 'Streamly.Internal.Data.Stream.IsStream.handle'.
3610+--
36063611{-# INLINE_NORMAL handle #-}
36073612handle :: (MonadCatch m, Exception e)
36083613 => (e -> Stream m a) -> Stream m a -> Stream m a
36093614handle f str =
36103615 gbracket_ (return ()) MC.try return (\_ e _ -> f e) (\_ -> str)
36113616
3617+-- | Alternate (custom) implementation of 'handle'.
3618+--
36123619{-# INLINE_NORMAL _handle #-}
36133620_handle :: (MonadCatch m, Exception e)
36143621 => (e -> Stream m a) -> Stream m a -> Stream m a
src/Streamly/Internal/Data/Unfold.hs+95−38
@@ -522,7 +522,7 @@ repeatM = Unfold step return
522522 step x = x >>= \x1 -> return $ Yield x1 x
523523
524524-- | Generates an infinite stream starting with the given seed and applying the
525−-- given function repeteadly.
525+-- given function repeatedly.
526526--
527527-- /Internal/
528528--
@@ -1027,7 +1027,7 @@ apDiscardSnd (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined
10271027data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
10281028
10291029-- | Map an unfold generating action to each element of an unfold and
1030−-- flattern the results into a single stream.
1030+-- flatten the results into a single stream.
10311031--
10321032{-# INLINE_NORMAL concatMapM #-}
10331033concatMapM :: Monad m
@@ -1061,9 +1061,14 @@ concatMapM f (Unfold step1 inject1) = Unfold step inject
10611061-- Exceptions
10621062------------------------------------------------------------------------------
10631063
1064−-- | The most general bracketing and exception combinator. All other
1065−-- combinators can be expressed in terms of this combinator. This can also be
1066−-- used for cases which are not covered by the standard combinators.
1064+-- | Like 'gbracket' but with following differences:
1065+--
1066+-- * alloc action @a -> m c@ runs with async exceptions enabled
1067+-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
1068+-- after partial evaluation.
1069+-- * does not require a 'MonadAsync' constraint.
1070+--
1071+-- /Inhibits stream fusion/
10671072--
10681073-- /Internal/
10691074--
@@ -1094,6 +1099,7 @@ gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
10941099 Yield x s -> return $ Yield x (Right (s, v))
10951100 Skip s -> return $ Skip (Right (s, v))
10961101 Stop -> aft v >> return Stop
1102+ -- XXX Do not handle async exceptions, just rethrow them.
10971103 Left e -> do
10981104 r <- einject (v, e)
10991105 return $ Skip (Left r)
@@ -1104,12 +1110,22 @@ gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
11041110 Skip s -> return $ Skip (Left s)
11051111 Stop -> return Stop
11061112
1107−-- | The most general bracketing and exception combinator. All other
1108−-- combinators can be expressed in terms of this combinator. This can also be
1109−-- used for cases which are not covered by the standard combinators.
1113+-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1114+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1115+-- output @c@ as input to @Unfold m c b@ to generate an output stream. When
1116+-- unfolding use the supplied @try@ operation @forall s. m s -> m (Either e s)@
1117+-- to catch synchronous exceptions. If an exception occurs run the exception
1118+-- handling unfold @Unfold m (c, e) b@.
11101119--
1111−-- /Internal/
1120+-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
1121+-- to a sync or async exception or if it gets garbage collected after a partial
1122+-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
11121123--
1124+-- 'gbracket' can express all other exception handling combinators.
1125+--
1126+-- /Inhibits stream fusion/
1127+--
1128+-- /Internal/
11131129{-# INLINE_NORMAL gbracket #-}
11141130gbracket
11151131 :: (MonadIO m, MonadBaseControl IO m)
@@ -1144,6 +1160,7 @@ gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
11441160 Stop -> do
11451161 D.runIORefFinalizer ref
11461162 return Stop
1163+ -- XXX Do not handle async exceptions, just rethrow them.
11471164 Left e -> do
11481165 -- Clearing of finalizer and running of exception handler must
11491166 -- be atomic wrt async exceptions. Otherwise if we have cleared
@@ -1167,7 +1184,8 @@ _before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
11671184_before action = gbracket_ (\x -> action x >> return x) (fmap Right)
11681185 (\_ -> return ()) undefined
11691186
1170−-- | Run a side effect before the unfold yields its first element.
1187+-- | Run a side effect @a -> m c@ on the input @a@ before unfolding it using
1188+-- @Unfold m a b@.
11711189--
11721190-- /Internal/
11731191{-# INLINE_NORMAL before #-}
@@ -1192,11 +1210,11 @@ before action (Unfold step1 inject1) = Unfold step inject
11921210_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
11931211_after aft = gbracket_ return (fmap Right) aft undefined
11941212
1195−-- | Run a side effect whenever the unfold stops normally.
1213+-- | Like 'after' with following differences:
11961214--
1197−-- Prefer 'after' over this as the @after@ action in this combinator is not
1198−-- executed if the unfold is partially evaluated lazily and then garbage
1199−-- collected.
1215+-- * action @a -> m c@ won't run if the stream is garbage collected
1216+-- after partial evaluation.
1217+-- * Monad @m@ does not require any other constraints.
12001218--
12011219-- /Internal/
12021220{-# INLINE_NORMAL after_ #-}
@@ -1217,8 +1235,14 @@ after_ action (Unfold step1 inject1) = Unfold step inject
12171235 Skip s -> return $ Skip (s, v)
12181236 Stop -> action v >> return Stop
12191237
1220−-- | Run a side effect whenever the unfold stops normally
1221−-- or is garbage collected after a partial lazy evaluation.
1238+-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1239+-- the unfold stops normally, or if it is garbage collected after a partial
1240+-- lazy evaluation.
1241+--
1242+-- The semantics of the action @a -> m c@ are similar to the cleanup action
1243+-- semantics in 'bracket'.
1244+--
1245+-- /See also 'after_'/
12221246--
12231247-- /Internal/
12241248{-# INLINE_NORMAL after #-}
@@ -1250,7 +1274,10 @@ _onException action =
12501274 (\_ -> return ())
12511275 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
12521276
1253−-- | Run a side effect whenever the unfold aborts due to an exception.
1277+-- | Unfold the input @a@ using @Unfold m a b@, run the action @a -> m c@ on
1278+-- @a@ if the unfold aborts due to an exception.
1279+--
1280+-- /Inhibits stream fusion/
12541281--
12551282-- /Internal/
12561283{-# INLINE_NORMAL onException #-}
@@ -1277,12 +1304,13 @@ _finally action =
12771304 gbracket_ return MC.try action
12781305 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
12791306
1280−-- | Run a side effect whenever the unfold stops normally or aborts due to an
1281−-- exception.
1307+-- | Like 'finally' with following differences:
12821308--
1283−-- Prefer 'finally' over this as the @after@ action in this combinator is not
1284−-- executed if the unfold is partially evaluated lazily and then garbage
1285−-- collected.
1309+-- * action @a -> m c@ won't run if the stream is garbage collected
1310+-- after partial evaluation.
1311+-- * does not require a 'MonadAsync' constraint.
1312+--
1313+-- /Inhibits stream fusion/
12861314--
12871315-- /Internal/
12881316{-# INLINE_NORMAL finally_ #-}
@@ -1303,8 +1331,20 @@ finally_ action (Unfold step1 inject1) = Unfold step inject
13031331 Skip s -> return $ Skip (s, v)
13041332 Stop -> action v >> return Stop
13051333
1306−-- | Run a side effect whenever the unfold stops normally, aborts due to an
1307−-- exception or if it is garbage collected after a partial lazy evaluation.
1334+-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1335+-- the unfold stops normally, aborts due to an exception or if it is garbage
1336+-- collected after a partial lazy evaluation.
1337+--
1338+-- The semantics of the action @a -> m c@ are similar to the cleanup action
1339+-- semantics in 'bracket'.
1340+--
1341+-- @
1342+-- finally release = bracket return release
1343+-- @
1344+--
1345+-- /See also 'finally_'/
1346+--
1347+-- /Inhibits stream fusion/
13081348--
13091349-- /Internal/
13101350{-# INLINE_NORMAL finally #-}
@@ -1336,14 +1376,14 @@ _bracket bef aft =
13361376 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
13371377 MC.throwM e))
13381378
1339−-- | @bracket_ before after between@ runs the @before@ action and then unfolds
1340−-- its output using the @between@ unfold. When the @between@ unfold is done or
1341−-- if an exception occurs then the @after@ action is run with the output of
1342−-- @before@ as argument.
1379+-- | Like 'bracket' but with following differences:
1380+--
1381+-- * alloc action @a -> m c@ runs with async exceptions enabled
1382+-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
1383+-- after partial evaluation.
1384+-- * does not require a 'MonadAsync' constraint.
13431385--
1344−-- Prefer 'bracket' over this as the @after@ action in this combinator is not
1345−-- executed if the unfold is partially evaluated lazily and then garbage
1346−-- collected.
1386+-- /Inhibits stream fusion/
13471387--
13481388-- /Internal/
13491389{-# INLINE_NORMAL bracket_ #-}
@@ -1366,11 +1406,26 @@ bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
13661406 Skip s -> return $ Skip (s, v)
13671407 Stop -> aft v >> return Stop
13681408
1369−-- | @bracket before after between@ runs the @before@ action and then unfolds
1370−-- its output using the @between@ unfold. When the @between@ unfold is done or
1371−-- if an exception occurs then the @after@ action is run with the output of
1372−-- @before@ as argument. The after action is also executed if the unfold is
1373−-- paritally evaluated and then garbage collected.
1409+-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1410+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1411+-- output @c@ as input to @Unfold m c b@ to generate an output stream.
1412+--
1413+-- @c@ is usually a resource under the state of monad @m@, e.g. a file
1414+-- handle, that requires a cleanup after use. The cleanup action @c -> m d@,
1415+-- runs whenever the stream ends normally, due to a sync or async exception or
1416+-- if it gets garbage collected after a partial lazy evaluation.
1417+--
1418+-- 'bracket' only guarantees that the cleanup action runs, and it runs with
1419+-- async exceptions enabled. The action must ensure that it can successfully
1420+-- cleanup the resource in the face of sync or async exceptions.
1421+--
1422+-- When the stream ends normally or on a sync exception, cleanup action runs
1423+-- immediately in the current thread context, whereas in other cases it runs in
1424+-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
1425+--
1426+-- /See also: 'bracket_', 'gbracket'/
1427+--
1428+-- /Inhibits stream fusion/
13741429--
13751430-- /Internal/
13761431{-# INLINE_NORMAL bracket #-}
@@ -1400,8 +1455,10 @@ bracket bef aft (Unfold step1 inject1) = Unfold step inject
14001455 D.runIORefFinalizer ref
14011456 return Stop
14021457
1403−-- | When unfolding if an exception occurs, unfold the exception using the
1404−-- exception unfold supplied as the first argument to 'handle'.
1458+-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1459+-- @Unfold m e b@.
1460+--
1461+-- /Inhibits stream fusion/
14051462--
14061463-- /Internal/
14071464{-# INLINE_NORMAL handle #-}
src/Streamly/Prelude.hs+5
@@ -606,6 +606,11 @@ module Streamly.Prelude
606606 , concatForFoldableWith
607607
608608 -- * Exceptions
609+ -- | Most of these combinators inhibit stream fusion, therefore, when
610+ -- possible, they should be called in an outer loop to mitigate the cost.
611+ -- For example, instead of calling them on a stream of chars call them on a
612+ -- stream of arrays before flattening it to a stream of chars.
613+ --
609614 , before
610615 , after
611616 , bracket