on ed030cc help sign in
202 lines 6.7 kB history raw
1-- |
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
7-- Portability : GHC
9module Streamly.Internal.Data.Stream.IsStream.Exception
10 (
11 before
12 , after_
13 , after
14 , bracket_
15 , bracket
16 , onException
17 , finally_
18 , finally
19 , ghandle
20 , handle
21 )
22where
24import Control.Exception (Exception)
25import Control.Monad.Catch (MonadCatch)
26import Control.Monad.IO.Class (MonadIO(..))
27import Control.Monad.Trans.Control (MonadBaseControl)
28import Streamly.Internal.Data.Stream.StreamD (toStreamD)
29import Streamly.Internal.Data.Stream.StreamK (IsStream)
30import Streamly.Internal.Data.SVar (MonadAsync)
32import qualified Streamly.Internal.Data.Stream.StreamD as D
34------------------------------------------------------------------------------
35-- Exceptions
36------------------------------------------------------------------------------
38-- | Run the action @m b@ before the stream yields its first element.
39--
40-- > before action xs = 'nilM' action <> xs
41--
42-- @since 0.7.0
43{-# INLINE before #-}
44before :: (IsStream t, Monad m) => m b -> t m a -> t m a
45before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
47-- | Like 'after', with following differences:
48--
49-- * action @m b@ won't run if the stream is garbage collected
50-- after partial evaluation.
51-- * Monad @m@ does not require any other constraints.
52-- * has slightly better performance than 'after'.
53--
54-- Same as the following, but with stream fusion:
55--
56-- > after_ action xs = xs <> 'nilM' action
57--
58-- /Internal/
59--
60{-# INLINE after_ #-}
61after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
62after_ action xs = D.fromStreamD $ D.after_ action $ D.toStreamD xs
64-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
65-- is garbage collected after a partial lazy evaluation.
66--
67-- The semantics of the action @m b@ are similar to the semantics of cleanup
68-- action in 'bracket'.
69--
70-- /See also 'after_'/
71--
72-- @since 0.7.0
73--
74{-# INLINE after #-}
75after :: (IsStream t, MonadIO m, MonadBaseControl IO m)
76 => m b -> t m a -> t m a
77after action xs = D.fromStreamD $ D.after action $ D.toStreamD xs
79-- | Run the action @m b@ if the stream aborts due to an exception. The
80-- exception is not caught, simply rethrown.
81--
82-- /Inhibits stream fusion/
83--
84-- @since 0.7.0
85{-# INLINE onException #-}
86onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
87onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
89-- | Like 'finally' with following differences:
90--
91-- * action @m b@ won't run if the stream is garbage collected
92-- after partial evaluation.
93-- * does not require a 'MonadAsync' constraint.
94-- * has slightly better performance than 'finally'.
95--
96-- /Inhibits stream fusion/
97--
98-- /Internal/
99--
100{-# INLINE finally_ #-}
101finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
102finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
104-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
105-- due to an exception or if it is garbage collected after a partial lazy
106-- evaluation.
107--
108-- The semantics of running the action @m b@ are similar to the cleanup action
109-- semantics described in 'bracket'.
110--
111-- @
112-- finally release = bracket (return ()) (const release)
113-- @
114--
115-- /See also 'finally_'/
116--
117-- /Inhibits stream fusion/
118--
119-- @since 0.7.0
120--
121{-# INLINE finally #-}
122finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
123finally action xs = D.fromStreamD $ D.finally action $ D.toStreamD xs
125-- | Like 'bracket' but with following differences:
126--
127-- * alloc action @m b@ runs with async exceptions enabled
128-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
129-- after partial evaluation.
130-- * does not require a 'MonadAsync' constraint.
131-- * has slightly better performance than 'bracket'.
132--
133-- /Inhibits stream fusion/
134--
135-- /Internal/
136--
137{-# INLINE bracket_ #-}
138bracket_ :: (IsStream t, MonadCatch m)
139 => m b -> (b -> m c) -> (b -> t m a) -> t m a
140bracket_ bef aft bet = D.fromStreamD $
141 D.bracket_ bef aft (toStreamD . bet)
143-- | Run the alloc action @m b@ with async exceptions disabled but keeping
144-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
145-- output @b@ as input to @b -> t m a@ to generate an output stream.
146--
147-- @b@ is usually a resource under the state of monad @m@, e.g. a file
148-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
149-- runs whenever the stream ends normally, due to a sync or async exception or
150-- if it gets garbage collected after a partial lazy evaluation.
151--
152-- 'bracket' only guarantees that the cleanup action runs, and it runs with
153-- async exceptions enabled. The action must ensure that it can successfully
154-- cleanup the resource in the face of sync or async exceptions.
155--
156-- When the stream ends normally or on a sync exception, cleanup action runs
157-- immediately in the current thread context, whereas in other cases it runs in
158-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
159--
160-- /See also: 'bracket_'/
161--
162-- /Inhibits stream fusion/
163--
164-- @since 0.7.0
165--
166{-# INLINE bracket #-}
167bracket :: (IsStream t, MonadAsync m, MonadCatch m)
168 => m b -> (b -> m c) -> (b -> t m a) -> t m a
169bracket bef aft bet = D.fromStreamD $
170 D.bracket bef aft (toStreamD . bet)
172-- | Like 'handle' but the exception handler is also provided with the stream
173-- that generated the exception as input. The exception handler can thus
174-- re-evaluate the stream to retry the action that failed. The exception
175-- handler can again call 'ghandle' on it to retry the action multiple times.
176--
177-- This is highly experimental. In a stream of actions we can map the stream
178-- with a retry combinator to retry each action on failure.
179--
180-- /Inhibits stream fusion/
181--
182-- /Internal/
183--
184{-# INLINE ghandle #-}
185ghandle :: (IsStream t, MonadCatch m, Exception e)
186 => (e -> t m a -> t m a) -> t m a -> t m a
187ghandle handler =
188 D.fromStreamD
189 . D.ghandle (\e xs -> D.toStreamD $ handler e (D.fromStreamD xs))
190 . D.toStreamD
192-- | When evaluating a stream if an exception occurs, stream evaluation aborts
193-- and the specified exception handler is run with the exception as argument.
194--
195-- /Inhibits stream fusion/
196--
197-- @since 0.7.0
198{-# INLINE handle #-}
199handle :: (IsStream t, MonadCatch m, Exception e)
200 => (e -> t m a) -> t m a -> t m a
201handle handler xs =
202 D.fromStreamD $ D.handle (D.toStreamD . handler) $ D.toStreamD xs