on master help sign in
Improve documentation of exception handling ops
862c47fHarendra Kumar
src/Streamly/Internal/Data/Stream/IsStream.hs+92−24
@@ -537,6 +537,7 @@ module Streamly.Internal.Data.Stream.IsStream
537 , onException
537 , onException
538 , finally_
538 , finally_
539 , finally
539 , finally
540+ , ghandle
540 , handle
541 , handle
541
542
542 -- * Generalize Inner Monad
543 -- * Generalize Inner Monad
@@ -5021,7 +5022,7 @@ classifySessionsOf interval =
5021-- Exceptions
5022-- Exceptions
5022------------------------------------------------------------------------------
5023------------------------------------------------------------------------------
5023
5024
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.
5025--
5026--
5026-- > before action xs = 'nilM' action <> xs
5027-- > before action xs = 'nilM' action <> xs
5027--
5028--
@@ -5030,12 +5031,16 @@ classifySessionsOf interval =
5030before :: (IsStream t, Monad m) => m b -> t m a -> t m a
5031before :: (IsStream t, Monad m) => m b -> t m a -> t m a
5031before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
5032before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
5032
5033
5033−-- | Run a side effect at the end of the stream. The side effect won't run if
5034+-- | Like 'after', with following differences:
5034−-- the stream is garbage collected before it reached the end.
5035--
5035--
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:
5037--
5042--
5038−-- This has slightly better performance than 'after'.
5043+-- > after_ action xs = xs <> 'nilM' action
5039--
5044--
5040-- /Internal/
5045-- /Internal/
5041--
5046--
@@ -5043,8 +5048,13 @@ before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
5043after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
5048after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
5044after_ action xs = D.fromStreamD $ D.after_ action $ D.toStreamD xs
5049after_ action xs = D.fromStreamD $ D.after_ action $ D.toStreamD xs
5045
5050
5046−-- | Run a side effect at the end of the stream or if it is garbage collected
5051+-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
5047−-- even before reaching the end.
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_'/
5048--
5058--
5049-- @since 0.7.0
5059-- @since 0.7.0
5050--
5060--
@@ -5053,18 +5063,24 @@ after :: (IsStream t, MonadIO m, MonadBaseControl IO m)
5053 => m b -> t m a -> t m a
5063 => m b -> t m a -> t m a
5054after action xs = D.fromStreamD $ D.after action $ D.toStreamD xs
5064after action xs = D.fromStreamD $ D.after action $ D.toStreamD xs
5055
5065
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/
5057--
5070--
5058-- @since 0.7.0
5071-- @since 0.7.0
5059{-# INLINE onException #-}
5072{-# INLINE onException #-}
5060onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
5073onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
5061onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
5074onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
5062
5075
5063−-- | Run a side effect at the end of the stream or if it aborts due to an
5076+-- | Like 'finally' with following differences:
5064−-- exception before it could reach the end. The side effect is not run if the
5077+--
5065−-- stream is garbage collected before reaching the end.
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'.
5066--
5082--
5067−-- This has slightly better performance than 'finally'.
5083+-- /Inhibits stream fusion/
5068--
5084--
5069-- /Internal/
5085-- /Internal/
5070--
5086--
@@ -5072,8 +5088,20 @@ onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
5072finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
5088finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
5073finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
5089finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
5074
5090
5075−-- | Run a side effect at the end of the stream, or if it aborts due to an
5091+-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
5076−-- exception or if it is garbage collected before it could reach the end.
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/
5077--
5105--
5078-- @since 0.7.0
5106-- @since 0.7.0
5079--
5107--
@@ -5081,13 +5109,15 @@ finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
5081finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
5109finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
5082finally action xs = D.fromStreamD $ D.finally action $ D.toStreamD xs
5110finally action xs = D.fromStreamD $ D.finally action $ D.toStreamD xs
5083
5111
5084−-- | Run the first action before the stream starts and remember its output,
5112+-- | Like 'bracket' but with following differences:
5085−-- generate a stream using the output, run the second action using the
5113+--
5086−-- remembered value as an argument whenever the stream ends normally or due to
5114+-- * alloc action @m b@ runs with async exceptions enabled
5087−-- an exception. The second action won't run if the stream is garbage collected
5115+-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
5088−-- before it could reach the end.
5116+-- after partial evaluation.
5117+-- * does not require a 'MonadAsync' constraint.
5118+-- * has slightly better performance than 'bracket'.
5089--
5119--
5090−-- This has slightly better performance than 'bracket'.
5120+-- /Inhibits stream fusion/
5091--
5121--
5092-- /Internal/
5122-- /Internal/
5093--
5123--
@@ -5097,10 +5127,26 @@ bracket_ :: (IsStream t, MonadCatch m)
5097bracket_ bef aft bet = D.fromStreamD $
5127bracket_ bef aft bet = D.fromStreamD $
5098 D.bracket_ bef aft (toStreamD . bet)
5128 D.bracket_ bef aft (toStreamD . bet)
5099
5129
5100−-- | Run the first action before the stream starts and remember its output,
5130+-- | Run the alloc action @m b@ with async exceptions disabled but keeping
5101−-- generate a stream using the output, run the second action using the
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@,
5102−-- remembered value as an argument whenever the stream ends normally, due to
5136+-- runs whenever the stream ends normally, due to a sync or async exception or
5103−-- an exception or if it is garbage collected after a partial lazy evaluation.
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/
5104--
5150--
5105-- @since 0.7.0
5151-- @since 0.7.0
5106--
5152--
@@ -5110,9 +5156,31 @@ bracket :: (IsStream t, MonadAsync m, MonadCatch m)
5110bracket bef aft bet = D.fromStreamD $
5156bracket bef aft bet = D.fromStreamD $
5111 D.bracket bef aft (toStreamD . bet)
5157 D.bracket bef aft (toStreamD . bet)
5112
5158
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+
5113-- | When evaluating a stream if an exception occurs, stream evaluation aborts
5179-- | When evaluating a stream if an exception occurs, stream evaluation aborts
5114-- and the specified exception handler is run with the exception as argument.
5180-- and the specified exception handler is run with the exception as argument.
5115--
5181--
5182+-- /Inhibits stream fusion/
5183+--
5116-- @since 0.7.0
5184-- @since 0.7.0
5117{-# INLINE handle #-}
5185{-# INLINE handle #-}
5118handle :: (IsStream t, MonadCatch m, Exception e)
5186handle :: (IsStream t, MonadCatch m, Exception e)
src/Streamly/Internal/Data/Stream/StreamD.hs+49−42
@@ -3266,9 +3266,14 @@ data GbracketState s1 s2 v
3266 | GBracketNormal s1 v
3266 | GBracketNormal s1 v
3267 | GBracketException s2
3267 | GBracketException s2
3268
3268
3269−-- | The most general bracketing and exception combinator. All other
3269+-- | Like 'gbracket' but with following differences:
3270−-- combinators can be expressed in terms of this combinator. This can also be
3270+--
3271−-- used for cases which are not covered by the standard combinators.
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/
3272--
3277--
3273-- /Internal/
3278-- /Internal/
3274--
3279--
@@ -3299,6 +3304,7 @@ gbracket_ bef exc aft fexc fnormal =
3299 return $ Yield x (GBracketNormal (Stream step1 s) v)
3304 return $ Yield x (GBracketNormal (Stream step1 s) v)
3300 Skip s -> return $ Skip (GBracketNormal (Stream step1 s) v)
3305 Skip s -> return $ Skip (GBracketNormal (Stream step1 s) v)
3301 Stop -> aft v >> return Stop
3306 Stop -> aft v >> return Stop
3307+ -- XXX Do not handle async exceptions, just rethrow them.
3302 Left e ->
3308 Left e ->
3303 return $ Skip (GBracketException (fexc v e (UnStream step1 st)))
3309 return $ Skip (GBracketException (fexc v e (UnStream step1 st)))
3304 step gst (GBracketException (UnStream step1 st)) = do
3310 step gst (GBracketException (UnStream step1 st)) = do
@@ -3380,15 +3386,22 @@ data GbracketIOState s1 s2 v wref
3380 | GBracketIONormal s1 v wref
3386 | GBracketIONormal s1 v wref
3381 | GBracketIOException s2
3387 | GBracketIOException s2
3382
3388
3383−-- | The most general bracketing and exception combinator. All other
3389+-- | Run the alloc action @m c@ with async exceptions disabled but keeping
3384−-- combinators can be expressed in terms of this combinator. This can also be
3390+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
3385−-- used for cases which are not covered by the standard combinators.
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)@.
3386--
3395--
3387−-- | It uses a finalizer to make sure we run the finalizing action
3396+-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
3388−-- when the stream is garbage collected.
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.
3389--
3399--
3390−-- /Internal/
3400+-- 'gbracket' can express all other exception handling combinators.
3391--
3401--
3402+-- /Inhibits stream fusion/
3403+--
3404+-- /Internal/
3392{-# INLINE_NORMAL gbracket #-}
3405{-# INLINE_NORMAL gbracket #-}
3393gbracket
3406gbracket
3394 :: (MonadIO m, MonadBaseControl IO m)
3407 :: (MonadIO m, MonadBaseControl IO m)
@@ -3429,6 +3442,7 @@ gbracket bef exc aft fexc fnormal =
3429 Stop -> do
3442 Stop -> do
3430 runIORefFinalizer ref
3443 runIORefFinalizer ref
3431 return Stop
3444 return Stop
3445+ -- XXX Do not handle async exceptions, just rethrow them.
3432 Left e -> do
3446 Left e -> do
3433 -- Clearing of finalizer and running of exception handler must
3447 -- Clearing of finalizer and running of exception handler must
3434 -- be atomic wrt async exceptions. Otherwise if we have cleared
3448 -- be atomic wrt async exceptions. Otherwise if we have cleared
@@ -3444,7 +3458,8 @@ gbracket bef exc aft fexc fnormal =
3444 Skip s -> return $ Skip (GBracketIOException (Stream step1 s))
3458 Skip s -> return $ Skip (GBracketIOException (Stream step1 s))
3445 Stop -> return Stop
3459 Stop -> return Stop
3446
3460
3447−-- | Run a side effect before the stream yields its first element.
3461+-- | See 'Streamly.Internal.Data.Stream.IsStream.after_'.
3462+--
3448{-# INLINE_NORMAL before #-}
3463{-# INLINE_NORMAL before #-}
3449before :: Monad m => m b -> Stream m a -> Stream m a
3464before :: Monad m => m b -> Stream m a -> Stream m a
3450before action (Stream step state) = Stream step' Nothing
3465before action (Stream step state) = Stream step' Nothing
@@ -3461,7 +3476,8 @@ before action (Stream step state) = Stream step' Nothing
3461 Skip s -> return $ Skip (Just s)
3476 Skip s -> return $ Skip (Just s)
3462 Stop -> return Stop
3477 Stop -> return Stop
3463
3478
3464−-- | Run a side effect whenever the stream stops normally.
3479+-- | See 'Streamly.Internal.Data.Stream.IsStream.after_'.
3480+--
3465{-# INLINE_NORMAL after_ #-}
3481{-# INLINE_NORMAL after_ #-}
3466after_ :: Monad m => m b -> Stream m a -> Stream m a
3482after_ :: Monad m => m b -> Stream m a -> Stream m a
3467after_ action (Stream step state) = Stream step' state
3483after_ action (Stream step state) = Stream step' state
@@ -3476,6 +3492,8 @@ after_ action (Stream step state) = Stream step' state
3476 Skip s -> return $ Skip s
3492 Skip s -> return $ Skip s
3477 Stop -> action >> return Stop
3493 Stop -> action >> return Stop
3478
3494
3495+-- | See 'Streamly.Internal.Data.Stream.IsStream.after'.
3496+--
3479{-# INLINE_NORMAL after #-}
3497{-# INLINE_NORMAL after #-}
3480after :: (MonadIO m, MonadBaseControl IO m)
3498after :: (MonadIO m, MonadBaseControl IO m)
3481 => m b -> Stream m a -> Stream m a
3499 => m b -> Stream m a -> Stream m a
@@ -3496,17 +3514,11 @@ after action (Stream step state) = Stream step' Nothing
3496 runIORefFinalizer ref
3514 runIORefFinalizer ref
3497 return Stop
3515 return Stop
3498
3516
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−--
3505-- XXX For high performance error checks in busy streams we may need another
3517-- XXX For high performance error checks in busy streams we may need another
3506-- Error constructor in step.
3518-- Error constructor in step.
3507--
3519--
3508−-- | Run a side effect whenever the stream aborts due to an exception. The
3520+-- | See 'Streamly.Internal.Data.Stream.IsStream.onException'.
3509−-- exception is not caught, simply rethrown.
3521+--
3510{-# INLINE_NORMAL onException #-}
3522{-# INLINE_NORMAL onException #-}
3511onException :: MonadCatch m => m b -> Stream m a -> Stream m a
3523onException :: MonadCatch m => m b -> Stream m a -> Stream m a
3512onException action str =
3524onException action str =
@@ -3528,24 +3540,16 @@ _onException action (Stream step state) = Stream step' state
3528 Skip s -> return $ Skip s
3540 Skip s -> return $ Skip s
3529 Stop -> return Stop
3541 Stop -> return Stop
3530
3542
3531−-- XXX bracket is like concatMap, it generates a stream and then flattens it.
3543+-- | See 'Streamly.Internal.Data.Stream.IsStream.bracket_'.
3532−-- Like concatMap it has 10x worse performance compared to linear fused
3533−-- compositions.
3534--
3544--
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.
3539{-# INLINE_NORMAL bracket_ #-}
3545{-# INLINE_NORMAL bracket_ #-}
3546+bracket_ :: MonadCatch m
3540−bracket_ :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3547+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3541bracket_ bef aft bet =
3548bracket_ bef aft bet =
3542 gbracket_ bef MC.try aft
3549 gbracket_ bef MC.try aft
3543 (\a (e :: SomeException) _ -> nilM (aft a >> MC.throwM e)) bet
3550 (\a (e :: SomeException) _ -> nilM (aft a >> MC.throwM e)) bet
3544
3551
3545−-- | Run the first action before the stream starts and remember its output,
3552+-- | See 'Streamly.Internal.Data.Stream.IsStream.bracket'.
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.
3549--
3553--
3550{-# INLINE_NORMAL bracket #-}
3554{-# INLINE_NORMAL bracket #-}
3551bracket :: (MonadAsync m, MonadCatch m)
3555bracket :: (MonadAsync m, MonadCatch m)
@@ -3556,8 +3560,11 @@ bracket bef aft bet =
3556
3560
3557data BracketState s v = BracketInit | BracketRun s v
3561data BracketState s v = BracketInit | BracketRun s v
3558
3562
3563+-- | Alternate (custom) implementation of 'bracket'.
3564+--
3559{-# INLINE_NORMAL _bracket #-}
3565{-# INLINE_NORMAL _bracket #-}
3566+_bracket :: MonadCatch m
3560−_bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3567+ => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a
3561_bracket bef aft bet = Stream step' BracketInit
3568_bracket bef aft bet = Stream step' BracketInit
3562
3569
3563 where
3570 where
@@ -3577,23 +3584,21 @@ _bracket bef aft bet = Stream step' BracketInit
3577 Skip s -> return $ Skip (BracketRun (Stream step s) v)
3584 Skip s -> return $ Skip (BracketRun (Stream step s) v)
3578 Stop -> aft v >> return Stop
3585 Stop -> aft v >> return Stop
3579
3586
3580−-- | Run a side effect whenever the stream stops normally or aborts due to an
3587+-- | See 'Streamly.Internal.Data.Stream.IsStream.finally_'.
3581−-- exception.
3588+--
3582{-# INLINE finally_ #-}
3589{-# INLINE finally_ #-}
3583finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
3590finally_ :: MonadCatch m => m b -> Stream m a -> Stream m a
3584−-- finally action xs = after action $ onException action xs
3585finally_ action xs = bracket_ (return ()) (\_ -> action) (const xs)
3591finally_ action xs = bracket_ (return ()) (\_ -> action) (const xs)
3586
3592
3587−-- | Run a side effect whenever the stream stops normally or aborts due to an
3593+-- | See 'Streamly.Internal.Data.Stream.IsStream.finally'.
3588−-- exception or gets garbage collected.
3594+--
3595+-- finally action xs = after action $ onException action xs
3589--
3596--
3590{-# INLINE finally #-}
3597{-# INLINE finally #-}
3591finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
3598finally :: (MonadAsync m, MonadCatch m) => m b -> Stream m a -> Stream m a
3592finally action xs = bracket (return ()) (\_ -> action) (const xs)
3599finally action xs = bracket (return ()) (\_ -> action) (const xs)
3593
3600
3594−-- | When evaluating a stream if an exception occurs, stream
3601+-- | See 'Streamly.Internal.Data.Stream.IsStream.ghandle'.
3595−-- evaluation aborts and the specified exception handler is run with
3596−-- the exception and the Stream which threw the exception as argument.
3597--
3602--
3598{-# INLINE_NORMAL ghandle #-}
3603{-# INLINE_NORMAL ghandle #-}
3599ghandle :: (MonadCatch m, Exception e)
3604ghandle :: (MonadCatch m, Exception e)
@@ -3601,14 +3606,16 @@ ghandle :: (MonadCatch m, Exception e)
3601ghandle f str =
3606ghandle f str =
3602 gbracket_ (return ()) MC.try return (\_ -> f) (\_ -> str)
3607 gbracket_ (return ()) MC.try return (\_ -> f) (\_ -> str)
3603
3608
3604−-- | When evaluating a stream if an exception occurs, stream evaluation aborts
3609+-- | See 'Streamly.Internal.Data.Stream.IsStream.handle'.
3605−-- and the specified exception handler is run with the exception as argument.
3610+--
3606{-# INLINE_NORMAL handle #-}
3611{-# INLINE_NORMAL handle #-}
3607handle :: (MonadCatch m, Exception e)
3612handle :: (MonadCatch m, Exception e)
3608 => (e -> Stream m a) -> Stream m a -> Stream m a
3613 => (e -> Stream m a) -> Stream m a -> Stream m a
3609handle f str =
3614handle f str =
3610 gbracket_ (return ()) MC.try return (\_ e _ -> f e) (\_ -> str)
3615 gbracket_ (return ()) MC.try return (\_ e _ -> f e) (\_ -> str)
3611
3616
3617+-- | Alternate (custom) implementation of 'handle'.
3618+--
3612{-# INLINE_NORMAL _handle #-}
3619{-# INLINE_NORMAL _handle #-}
3613_handle :: (MonadCatch m, Exception e)
3620_handle :: (MonadCatch m, Exception e)
3614 => (e -> Stream m a) -> Stream m a -> Stream m a
3621 => (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
522 step x = x >>= \x1 -> return $ Yield x1 x
522 step x = x >>= \x1 -> return $ Yield x1 x
523
523
524-- | Generates an infinite stream starting with the given seed and applying the
524-- | Generates an infinite stream starting with the given seed and applying the
525−-- given function repeteadly.
525+-- given function repeatedly.
526--
526--
527-- /Internal/
527-- /Internal/
528--
528--
@@ -1027,7 +1027,7 @@ apDiscardSnd (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined
1027data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
1027data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
1028
1028
1029-- | Map an unfold generating action to each element of an unfold and
1029-- | 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.
1031--
1031--
1032{-# INLINE_NORMAL concatMapM #-}
1032{-# INLINE_NORMAL concatMapM #-}
1033concatMapM :: Monad m
1033concatMapM :: Monad m
@@ -1061,9 +1061,14 @@ concatMapM f (Unfold step1 inject1) = Unfold step inject
1061-- Exceptions
1061-- Exceptions
1062------------------------------------------------------------------------------
1062------------------------------------------------------------------------------
1063
1063
1064−-- | The most general bracketing and exception combinator. All other
1064+-- | Like 'gbracket' but with following differences:
1065−-- combinators can be expressed in terms of this combinator. This can also be
1065+--
1066−-- used for cases which are not covered by the standard combinators.
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/
1067--
1072--
1068-- /Internal/
1073-- /Internal/
1069--
1074--
@@ -1094,6 +1099,7 @@ gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
1094 Yield x s -> return $ Yield x (Right (s, v))
1099 Yield x s -> return $ Yield x (Right (s, v))
1095 Skip s -> return $ Skip (Right (s, v))
1100 Skip s -> return $ Skip (Right (s, v))
1096 Stop -> aft v >> return Stop
1101 Stop -> aft v >> return Stop
1102+ -- XXX Do not handle async exceptions, just rethrow them.
1097 Left e -> do
1103 Left e -> do
1098 r <- einject (v, e)
1104 r <- einject (v, e)
1099 return $ Skip (Left r)
1105 return $ Skip (Left r)
@@ -1104,12 +1110,22 @@ gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
1104 Skip s -> return $ Skip (Left s)
1110 Skip s -> return $ Skip (Left s)
1105 Stop -> return Stop
1111 Stop -> return Stop
1106
1112
1107−-- | The most general bracketing and exception combinator. All other
1113+-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1108−-- combinators can be expressed in terms of this combinator. This can also be
1114+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1109−-- used for cases which are not covered by the standard combinators.
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@.
1110--
1119--
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.
1112--
1123--
1124+-- 'gbracket' can express all other exception handling combinators.
1125+--
1126+-- /Inhibits stream fusion/
1127+--
1128+-- /Internal/
1113{-# INLINE_NORMAL gbracket #-}
1129{-# INLINE_NORMAL gbracket #-}
1114gbracket
1130gbracket
1115 :: (MonadIO m, MonadBaseControl IO m)
1131 :: (MonadIO m, MonadBaseControl IO m)
@@ -1144,6 +1160,7 @@ gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
1144 Stop -> do
1160 Stop -> do
1145 D.runIORefFinalizer ref
1161 D.runIORefFinalizer ref
1146 return Stop
1162 return Stop
1163+ -- XXX Do not handle async exceptions, just rethrow them.
1147 Left e -> do
1164 Left e -> do
1148 -- Clearing of finalizer and running of exception handler must
1165 -- Clearing of finalizer and running of exception handler must
1149 -- be atomic wrt async exceptions. Otherwise if we have cleared
1166 -- 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
1167_before action = gbracket_ (\x -> action x >> return x) (fmap Right)
1184_before action = gbracket_ (\x -> action x >> return x) (fmap Right)
1168 (\_ -> return ()) undefined
1185 (\_ -> return ()) undefined
1169
1186
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@.
1171--
1189--
1172-- /Internal/
1190-- /Internal/
1173{-# INLINE_NORMAL before #-}
1191{-# INLINE_NORMAL before #-}
@@ -1192,11 +1210,11 @@ before action (Unfold step1 inject1) = Unfold step inject
1192_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
1210_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
1193_after aft = gbracket_ return (fmap Right) aft undefined
1211_after aft = gbracket_ return (fmap Right) aft undefined
1194
1212
1195−-- | Run a side effect whenever the unfold stops normally.
1213+-- | Like 'after' with following differences:
1196--
1214--
1197−-- Prefer 'after' over this as the @after@ action in this combinator is not
1215+-- * action @a -> m c@ won't run if the stream is garbage collected
1198−-- executed if the unfold is partially evaluated lazily and then garbage
1216+-- after partial evaluation.
1199−-- collected.
1217+-- * Monad @m@ does not require any other constraints.
1200--
1218--
1201-- /Internal/
1219-- /Internal/
1202{-# INLINE_NORMAL after_ #-}
1220{-# INLINE_NORMAL after_ #-}
@@ -1217,8 +1235,14 @@ after_ action (Unfold step1 inject1) = Unfold step inject
1217 Skip s -> return $ Skip (s, v)
1235 Skip s -> return $ Skip (s, v)
1218 Stop -> action v >> return Stop
1236 Stop -> action v >> return Stop
1219
1237
1220−-- | Run a side effect whenever the unfold stops normally
1238+-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1221−-- or is garbage collected after a partial lazy evaluation.
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_'/
1222--
1246--
1223-- /Internal/
1247-- /Internal/
1224{-# INLINE_NORMAL after #-}
1248{-# INLINE_NORMAL after #-}
@@ -1250,7 +1274,10 @@ _onException action =
1250 (\_ -> return ())
1274 (\_ -> return ())
1251 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
1275 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
1252
1276
1277+-- | Unfold the input @a@ using @Unfold m a b@, run the action @a -> m c@ on
1253−-- | Run a side effect whenever the unfold aborts due to an exception.
1278+-- @a@ if the unfold aborts due to an exception.
1279+--
1280+-- /Inhibits stream fusion/
1254--
1281--
1255-- /Internal/
1282-- /Internal/
1256{-# INLINE_NORMAL onException #-}
1283{-# INLINE_NORMAL onException #-}
@@ -1277,12 +1304,13 @@ _finally action =
1277 gbracket_ return MC.try action
1304 gbracket_ return MC.try action
1278 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
1305 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
1279
1306
1280−-- | Run a side effect whenever the unfold stops normally or aborts due to an
1307+-- | Like 'finally' with following differences:
1281−-- exception.
1282--
1308--
1283−-- Prefer 'finally' over this as the @after@ action in this combinator is not
1309+-- * action @a -> m c@ won't run if the stream is garbage collected
1284−-- executed if the unfold is partially evaluated lazily and then garbage
1310+-- after partial evaluation.
1285−-- collected.
1311+-- * does not require a 'MonadAsync' constraint.
1312+--
1313+-- /Inhibits stream fusion/
1286--
1314--
1287-- /Internal/
1315-- /Internal/
1288{-# INLINE_NORMAL finally_ #-}
1316{-# INLINE_NORMAL finally_ #-}
@@ -1303,8 +1331,20 @@ finally_ action (Unfold step1 inject1) = Unfold step inject
1303 Skip s -> return $ Skip (s, v)
1331 Skip s -> return $ Skip (s, v)
1304 Stop -> action v >> return Stop
1332 Stop -> action v >> return Stop
1305
1333
1334+-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1306−-- | Run a side effect whenever the unfold stops normally, aborts due to an
1335+-- the unfold stops normally, aborts due to an exception or if it is garbage
1307−-- exception or if it is garbage collected after a partial lazy evaluation.
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/
1308--
1348--
1309-- /Internal/
1349-- /Internal/
1310{-# INLINE_NORMAL finally #-}
1350{-# INLINE_NORMAL finally #-}
@@ -1336,14 +1376,14 @@ _bracket bef aft =
1336 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1376 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1337 MC.throwM e))
1377 MC.throwM e))
1338
1378
1339−-- | @bracket_ before after between@ runs the @before@ action and then unfolds
1379+-- | Like 'bracket' but with following differences:
1340−-- its output using the @between@ unfold. When the @between@ unfold is done or
1380+--
1341−-- if an exception occurs then the @after@ action is run with the output of
1381+-- * alloc action @a -> m c@ runs with async exceptions enabled
1342−-- @before@ as argument.
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.
1343--
1385--
1344−-- Prefer 'bracket' over this as the @after@ action in this combinator is not
1386+-- /Inhibits stream fusion/
1345−-- executed if the unfold is partially evaluated lazily and then garbage
1346−-- collected.
1347--
1387--
1348-- /Internal/
1388-- /Internal/
1349{-# INLINE_NORMAL bracket_ #-}
1389{-# INLINE_NORMAL bracket_ #-}
@@ -1366,11 +1406,26 @@ bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
1366 Skip s -> return $ Skip (s, v)
1406 Skip s -> return $ Skip (s, v)
1367 Stop -> aft v >> return Stop
1407 Stop -> aft v >> return Stop
1368
1408
1369−-- | @bracket before after between@ runs the @before@ action and then unfolds
1409+-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1370−-- its output using the @between@ unfold. When the @between@ unfold is done or
1410+-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1371−-- if an exception occurs then the @after@ action is run with the output of
1411+-- output @c@ as input to @Unfold m c b@ to generate an output stream.
1372−-- @before@ as argument. The after action is also executed if the unfold is
1412+--
1373−-- paritally evaluated and then garbage collected.
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/
1374--
1429--
1375-- /Internal/
1430-- /Internal/
1376{-# INLINE_NORMAL bracket #-}
1431{-# INLINE_NORMAL bracket #-}
@@ -1400,8 +1455,10 @@ bracket bef aft (Unfold step1 inject1) = Unfold step inject
1400 D.runIORefFinalizer ref
1455 D.runIORefFinalizer ref
1401 return Stop
1456 return Stop
1402
1457
1403−-- | When unfolding if an exception occurs, unfold the exception using the
1458+-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1404−-- exception unfold supplied as the first argument to 'handle'.
1459+-- @Unfold m e b@.
1460+--
1461+-- /Inhibits stream fusion/
1405--
1462--
1406-- /Internal/
1463-- /Internal/
1407{-# INLINE_NORMAL handle #-}
1464{-# INLINE_NORMAL handle #-}
src/Streamly/Prelude.hs+5
@@ -606,6 +606,11 @@ module Streamly.Prelude
606 , concatForFoldableWith
606 , concatForFoldableWith
607
607
608 -- * Exceptions
608 -- * 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+ --
609 , before
614 , before
610 , after
615 , after
611 , bracket
616 , bracket