on 3e53339 help sign in
228 lines 7.5 kB history raw
1-- |
2-- Module : Streamly.Internal.Data.Stream.IsStream.Exceptions
3-- Copyright : (c) 2020 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
7-- Portability : GHC
9module Streamly.Internal.Data.Stream.IsStream.Exceptions
10 (
11 -- * Exceptions
12 before
13 , after_
14 , after
15 , bracket_
16 , bracket
17 , onException
18 , finally_
19 , finally
20 , ghandle
21 , handle
22 )
23where
25import Control.Exception (Exception)
26import Control.Monad.Catch (MonadCatch)
27import Control.Monad.IO.Class (MonadIO(..))
28import Control.Monad.Trans.Control (MonadBaseControl)
29import Streamly.Internal.Data.SVar (MonadAsync)
30import Streamly.Internal.Data.Stream.StreamD (toStreamD)
31import Streamly.Internal.Data.Stream.StreamK (IsStream)
33import qualified Streamly.Internal.Data.Stream.StreamD as D
35import Prelude hiding
36 ( filter, drop, dropWhile, take, takeWhile, zipWith, foldr
37 , foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem
38 , notElem, maximum, minimum, head, last, tail, length, null
39 , reverse, iterate, init, and, or, lookup, foldr1, (!!)
40 , scanl, scanl1, replicate, concatMap, span, splitAt, break
41 , repeat, concat, mconcat)
43------------------------------------------------------------------------------
44-- Exceptions
45------------------------------------------------------------------------------
47-- | Run the action @m b@ before the stream yields its first element.
48--
49-- > before action xs = 'nilM' action <> xs
50--
51-- @since 0.7.0
52{-# INLINE before #-}
53before :: (IsStream t, Monad m) => m b -> t m a -> t m a
54before action xs = D.fromStreamD $ D.before action $ D.toStreamD xs
56-- | Like 'after', with following differences:
57--
58-- * action @m b@ won't run if the stream is garbage collected
59-- after partial evaluation.
60-- * Monad @m@ does not require any other constraints.
61-- * has slightly better performance than 'after'.
62--
63-- Same as the following, but with stream fusion:
64--
65-- > after_ action xs = xs <> 'nilM' action
66--
67-- /Internal/
68--
69{-# INLINE after_ #-}
70after_ :: (IsStream t, Monad m) => m b -> t m a -> t m a
71after_ action xs = D.fromStreamD $ D.after_ action $ D.toStreamD xs
73-- | Run the action @m b@ whenever the stream @t m a@ stops normally, or if it
74-- is garbage collected after a partial lazy evaluation.
75--
76-- The semantics of the action @m b@ are similar to the semantics of cleanup
77-- action in 'bracket'.
78--
79-- /See also 'after_'/
80--
81-- @since 0.7.0
82--
83{-# INLINE after #-}
84after :: (IsStream t, MonadIO m, MonadBaseControl IO m)
85 => m b -> t m a -> t m a
86after action xs = D.fromStreamD $ D.after action $ D.toStreamD xs
88-- | Run the action @m b@ if the stream aborts due to an exception. The
89-- exception is not caught, simply rethrown.
90--
91-- /Inhibits stream fusion/
92--
93-- @since 0.7.0
94{-# INLINE onException #-}
95onException :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
96onException action xs = D.fromStreamD $ D.onException action $ D.toStreamD xs
98-- | Like 'finally' with following differences:
99--
100-- * action @m b@ won't run if the stream is garbage collected
101-- after partial evaluation.
102-- * does not require a 'MonadAsync' constraint.
103-- * has slightly better performance than 'finally'.
104--
105-- /Inhibits stream fusion/
106--
107-- /Internal/
108--
109{-# INLINE finally_ #-}
110finally_ :: (IsStream t, MonadCatch m) => m b -> t m a -> t m a
111finally_ action xs = D.fromStreamD $ D.finally_ action $ D.toStreamD xs
113-- | Run the action @m b@ whenever the stream @t m a@ stops normally, aborts
114-- due to an exception or if it is garbage collected after a partial lazy
115-- evaluation.
116--
117-- The semantics of running the action @m b@ are similar to the cleanup action
118-- semantics described in 'bracket'.
119--
120-- @
121-- finally release = bracket (return ()) (const release)
122-- @
123--
124-- /See also 'finally_'/
125--
126-- /Inhibits stream fusion/
127--
128-- @since 0.7.0
129--
130{-# INLINE finally #-}
131finally :: (IsStream t, MonadAsync m, MonadCatch m) => m b -> t m a -> t m a
132finally action xs = D.fromStreamD $ D.finally action $ D.toStreamD xs
134-- | Like 'bracket' but with following differences:
135--
136-- * alloc action @m b@ runs with async exceptions enabled
137-- * cleanup action @b -> m c@ won't run if the stream is garbage collected
138-- after partial evaluation.
139-- * does not require a 'MonadAsync' constraint.
140-- * has slightly better performance than 'bracket'.
141--
142-- /Inhibits stream fusion/
143--
144-- /Internal/
145--
146{-# INLINE bracket_ #-}
147bracket_ :: (IsStream t, MonadCatch m)
148 => m b -> (b -> m c) -> (b -> t m a) -> t m a
149bracket_ bef aft bet = D.fromStreamD $
150 D.bracket_ bef aft (toStreamD . bet)
152-- | Run the alloc action @m b@ with async exceptions disabled but keeping
153-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
154-- output @b@ as input to @b -> t m a@ to generate an output stream.
155--
156-- @b@ is usually a resource under the state of monad @m@, e.g. a file
157-- handle, that requires a cleanup after use. The cleanup action @b -> m c@,
158-- runs whenever the stream ends normally, due to a sync or async exception or
159-- if it gets garbage collected after a partial lazy evaluation.
160--
161-- 'bracket' only guarantees that the cleanup action runs, and it runs with
162-- async exceptions enabled. The action must ensure that it can successfully
163-- cleanup the resource in the face of sync or async exceptions.
164--
165-- When the stream ends normally or on a sync exception, cleanup action runs
166-- immediately in the current thread context, whereas in other cases it runs in
167-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
168--
169-- /See also: 'bracket_'/
170--
171-- /Inhibits stream fusion/
172--
173-- @since 0.7.0
174--
175{-# INLINE bracket #-}
176bracket :: (IsStream t, MonadAsync m, MonadCatch m)
177 => m b -> (b -> m c) -> (b -> t m a) -> t m a
178bracket bef aft bet = D.fromStreamD $
179 D.bracket bef aft (toStreamD . bet)
181-- | Like 'handle' but the exception handler is also provided with the stream
182-- that generated the exception as input. The exception handler can thus
183-- re-evaluate the stream to retry the action that failed. The exception
184-- handler can again call 'ghandle' on it to retry the action multiple times.
185--
186-- This is highly experimental. In a stream of actions we can map the stream
187-- with a retry combinator to retry each action on failure.
188--
189-- /Inhibits stream fusion/
190--
191-- /Internal/
192--
193{-# INLINE ghandle #-}
194ghandle :: (IsStream t, MonadCatch m, Exception e)
195 => (e -> t m a -> t m a) -> t m a -> t m a
196ghandle handler =
197 D.fromStreamD
198 . D.ghandle (\e xs -> D.toStreamD $ handler e (D.fromStreamD xs))
199 . D.toStreamD
201-- | When evaluating a stream if an exception occurs, stream evaluation aborts
202-- and the specified exception handler is run with the exception as argument.
203--
204-- /Inhibits stream fusion/
205--
206-- @since 0.7.0
207{-# INLINE handle #-}
208handle :: (IsStream t, MonadCatch m, Exception e)
209 => (e -> t m a) -> t m a -> t m a
210handle handler xs =
211 D.fromStreamD $ D.handle (D.toStreamD . handler) $ D.toStreamD xs
213-- Keep concating either streams as long as rights are generated, stop as soon
214-- as a left is generated and concat the left stream.
215--
216-- See also: 'handle'
217--
218-- /Unimplemented/
219--
220{-
221concatMapEitherWith
222 :: -- (IsStream t, MonadAsync m) =>
223 (forall x. t m x -> t m x -> t m x)
224 -> (a -> t m (Either (t m b) b))
225 -> t m a
226 -> t m b
227concatMapEitherWith = undefined
228-}