on 2c48600 help sign in
1138 lines 36 kB history raw
1#include "inline.hs"
3-- |
4-- Module : Streamly.Internal.Data.Unfold
5-- Copyright : (c) 2019 Composewell Technologies
6-- License : BSD3
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
9-- Portability : GHC
10--
11-- To run the examples in this module:
12--
13-- >>> import qualified Streamly.Data.Fold as Fold
14-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
15--
16-- = Unfolds and Streams
17--
18-- An 'Unfold' type is the same as the direct style 'Stream' type except that
19-- it uses an inject function to determine the initial state of the stream
20-- based on an input. A stream is a special case of Unfold when the static
21-- input is unit or Void.
22--
23-- This allows an important optimization to occur in several cases, making the
24-- 'Unfold' a more efficient abstraction. Consider the 'concatMap' and
25-- 'unfoldMany' operations, the latter is more efficient. 'concatMap'
26-- generates a new stream object from each element in the stream by applying
27-- the supplied function to the element, the stream object includes the "step"
28-- function as well as the initial "state" of the stream. Since the stream is
29-- generated dynamically the compiler does not know the step function or the
30-- state type statically at compile time, therefore, it cannot inline it. On
31-- the other hand in case of 'unfoldMany' the compiler has visibility into
32-- the unfold's state generation function, therefore, the compiler knows all
33-- the types statically and it can inline the inject as well as the step
34-- functions, generating efficient code. Essentially, the stream is not opaque
35-- to the consumer in case of unfolds, the consumer knows how to generate the
36-- stream from a seed using a known "inject" and "step" functions.
37--
38-- A Stream is like a data object whereas unfold is like a function. Being
39-- function like, an Unfold is an instance of 'Category' and 'Arrow' type
40-- classes.
41--
42-- = Unfolds and Folds
43--
44-- Streams forcing a closed control flow loop can be categorized under
45-- two types, unfolds and folds, both of these are duals of each other.
46--
47-- Unfold streams are really generators of a sequence of elements, we can also
48-- call them pull style streams. These are lazy producers of streams. On each
49-- evaluation the producer generates the next element. A consumer can
50-- therefore pull elements from the stream whenever it wants to. A stream
51-- consumer can multiplex pull streams by pulling elements from the chosen
52-- streams, therefore, pull streams allow merging or multiplexing. On the
53-- other hand, with this representation we cannot split or demultiplex a
54-- stream. So really these are stream sources that can be generated from a
55-- seed and can be merged or zipped into a single stream.
56--
57-- The dual of Unfolds are Folds. Folds can also be called as push style
58-- streams or reducers. These are strict consumers of streams. We keep pushing
59-- elements to a fold and we can extract the result at any point. A driver can
60-- choose which fold to push to and can also push the same element to multiple
61-- folds. Therefore, folds allow splitting or demultiplexing a stream. On the
62-- other hand, we cannot merge streams using this representation. So really
63-- these are stream consumers that reduce the stream to a single value, these
64-- consumers can be composed such that a stream can be split over multiple
65-- consumers.
66--
67-- Performance:
68--
69-- Composing a tree or graph of computations with unfolds can be much more
70-- efficient compared to composing with the Monad instance. The reason is that
71-- unfolds allow the compiler to statically know the state and optimize it
72-- using stream fusion whereas it is not possible with the monad bind because
73-- the state is determined dynamically.
75-- Open control flow style streams can also have two representations. StreamK
76-- is a producer style representation. We can also have a consumer style
77-- representation. We can use that for composable folds in StreamK
78-- representation.
79--
81-- = Performance Notes
82--
83-- 'Unfold' representation is more efficient than using streams when combining
84-- streams. 'Unfold' type allows multiple unfold actions to be composed into a
85-- single unfold function in an efficient manner by enabling the compiler to
86-- perform stream fusion optimization.
87-- @Unfold m a b@ can be considered roughly equivalent to an action @a -> t m
88-- b@ (where @t@ is a stream type). Instead of using an 'Unfold' one could just
89-- use a function of the shape @a -> t m b@. However, working with stream types
90-- like t'Streamly.SerialT' does not allow the compiler to perform stream fusion
91-- optimization when merging, appending or concatenating multiple streams.
92-- Even though stream based combinator have excellent performance, they are
93-- much less efficient when compared to combinators using 'Unfold'. For
94-- example, the 'Streamly.Prelude.concatMap' combinator which uses @a -> t m b@
95-- (where @t@ is a stream type) to generate streams is much less efficient
96-- compared to 'Streamly.Prelude.unfoldMany'.
97--
98-- On the other hand, transformation operations on stream types are as
99-- efficient as transformations on 'Unfold'.
100--
101-- We should note that in some cases working with stream types may be more
102-- convenient compared to working with the 'Unfold' type. However, if extra
103-- performance boost is important then 'Unfold' based composition should be
104-- preferred compared to stream based composition when merging or concatenating
105-- streams.
107module Streamly.Internal.Data.Unfold
108 (
109 -- * Unfold Type
110 Step(..)
111 , Unfold
113 -- * Folding
114 , fold
115 , foldMany
116 -- pipe
118 -- * Unfolds
119 -- One to one correspondence with
120 -- "Streamly.Internal.Data.Stream.IsStream.Generate"
121 -- ** Basic Constructors
122 , mkUnfoldM
123 , mkUnfoldrM
124 , unfoldrM
125 , unfoldr
126 , functionM
127 , function
128 , identity
129 , nilM
130 , consM
132 -- ** From Values
133 , fromEffect
134 , fromPure
136 -- ** Generators
137 -- | Generate a monadic stream from a seed.
138 , repeatM
139 , replicateM
140 , fromIndicesM
141 , iterateM
143 -- ** Enumerations
144 , Enumerable (..)
146 -- ** Enumerate Num
147 , enumerateFromNum
148 , enumerateFromThenNum
149 , enumerateFromStepNum
151 -- ** Enumerating 'Bounded 'Integral' Types
152 , enumerateFromIntegralBounded
153 , enumerateFromThenIntegralBounded
154 , enumerateFromToIntegralBounded
155 , enumerateFromThenToIntegralBounded
157 -- ** Enumerating 'Unounded Integral' Types
158 , enumerateFromIntegral
159 , enumerateFromThenIntegral
160 , enumerateFromToIntegral
161 , enumerateFromThenToIntegral
163 -- ** Enumerating 'Small Integral' Types
164 , enumerateFromSmallBounded
165 , enumerateFromThenSmallBounded
166 , enumerateFromToSmall
167 , enumerateFromThenToSmall
169 -- ** Enumerating 'Fractional' Types
170 , enumerateFromFractional
171 , enumerateFromThenFractional
172 , enumerateFromToFractional
173 , enumerateFromThenToFractional
175 -- ** From Containers
176 , fromList
177 , fromListM
179 , fromStream
180 , fromStreamK
181 , fromStreamD
183 -- * Combinators
184 -- ** Mapping on Input
185 , lmap
186 , lmapM
187 , supply
188 , supplyFirst
189 , supplySecond
190 , discardFirst
191 , discardSecond
192 , swap
193 , lscanlM'
194 -- coapply
195 -- comonad
197 -- ** Mapping on Output
198 , map
199 , mapM
200 , mapMWithInput
201 , scanlM'
203 -- ** Either Wrapped Input
204 , either
206 -- ** Filtering
207 , takeWhileM
208 , takeWhile
209 , take
210 , filter
211 , filterM
212 , drop
213 , dropWhile
214 , dropWhileM
216 -- ** Zipping
217 , zipWithM
218 , zipWith
220 -- ** Cross product
221 , crossWithM
222 , crossWith
223 , cross
224 , apply
226 -- ** Nesting
227 , ConcatState (..)
228 , many
229 , concatMapM
230 , bind
232 -- ** Resource Management
233 , gbracket_
234 , gbracket
235 , before
236 , after
237 , after_
238 , finally
239 , finally_
240 , bracket
241 , bracket_
243 -- ** Exceptions
244 , onException
245 , handle
246 )
247where
249import Control.Exception (Exception, mask_)
250import Control.Monad.Catch (MonadCatch)
251import Control.Monad.IO.Class (MonadIO(..))
252import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
253import GHC.Types (SPEC(..))
254import Streamly.Internal.Control.Concurrent (MonadAsync)
255import Streamly.Internal.Data.Fold.Type (Fold(..))
256import Streamly.Internal.Data.IOFinalizer
257 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
258import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
259import Streamly.Internal.Data.SVar.Type (defState)
261import qualified Control.Monad.Catch as MC
262import qualified Data.Tuple as Tuple
263import qualified Streamly.Internal.Data.Fold.Type as FL
264import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
265import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
266import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
268import Streamly.Internal.Data.Unfold.Enumeration
269import Streamly.Internal.Data.Unfold.Type
270import Prelude
271 hiding (map, mapM, takeWhile, take, filter, const, zipWith
272 , drop, dropWhile, either)
274-- $setup
275-- >>> import qualified Streamly.Data.Fold as Fold
276-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
277-- >>> import qualified Streamly.Prelude as Stream
280-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
281-- using the argument of the original fold as the second element of tuple and
282-- discarding the first element of the tuple.
283--
284-- @
285-- discardFirst = Unfold.lmap snd
286-- @
287--
288-- /Pre-release/
289--
290{-# INLINE_NORMAL discardFirst #-}
291discardFirst :: Unfold m a b -> Unfold m (c, a) b
292discardFirst = lmap snd
294-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
295-- using the argument of the original fold as the first element of tuple and
296-- discarding the second element of the tuple.
297--
298-- @
299-- discardSecond = Unfold.lmap fst
300-- @
301--
302-- /Pre-release/
303--
304{-# INLINE_NORMAL discardSecond #-}
305discardSecond :: Unfold m a b -> Unfold m (a, c) b
306discardSecond = lmap fst
308-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
309-- that accepts a tuple with elements swapped.
310--
311-- @
312-- swap = Unfold.lmap Tuple.swap
313-- @
314--
315-- /Pre-release/
316--
317{-# INLINE_NORMAL swap #-}
318swap :: Unfold m (a, c) b -> Unfold m (c, a) b
319swap = lmap Tuple.swap
321-- See StreamD.scanlM' for implementing this.
322--
323-- | Scan the input of an 'Unfold' to change it in a stateful manner.
324--
325-- /Unimplemented/
326{-# INLINE_NORMAL lscanlM' #-}
327lscanlM' :: -- Monad m =>
328 (b -> a -> m b) -> m b -> Unfold m b c -> Unfold m a c
329lscanlM' = undefined
331-------------------------------------------------------------------------------
332-- Output operations
333-------------------------------------------------------------------------------
335-- XXX Do we need this combinator or the stream based idiom is enough?
337-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
338-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
339-- application of the fold on the unfolded stream.
340--
341-- >>> Unfold.fold Fold.sum Unfold.fromList [1..100]
342-- 5050
343--
344-- >>> fold f u = Stream.fold f . Stream.unfold u
345--
346-- /Pre-release/
347--
348{-# INLINE_NORMAL fold #-}
349fold :: Monad m => Fold m b c -> Unfold m a b -> a -> m c
350fold (Fold fstep initial extract) (Unfold ustep inject) a = do
351 res <- initial
352 case res of
353 FL.Partial x -> inject a >>= go SPEC x
354 FL.Done b -> return b
356 where
358 {-# INLINE_LATE go #-}
359 go !_ !fs st = do
360 r <- ustep st
361 case r of
362 Yield x s -> do
363 res <- fstep fs x
364 case res of
365 FL.Partial fs1 -> go SPEC fs1 s
366 FL.Done c -> return c
367 Skip s -> go SPEC fs s
368 Stop -> extract fs
370-- | Apply a fold multiple times on the output of an unfold.
371--
372-- /Unimplemented/
373foldMany :: -- Monad m =>
374 Fold m b c -> Unfold m a b -> Unfold m a c
375foldMany = undefined
377-- | Apply a monadic function to each element of the stream and replace it
378-- with the output of the resulting action.
379--
380-- /Since: 0.8.0/
381--
382{-# INLINE_NORMAL mapM #-}
383mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
384mapM f (Unfold ustep uinject) = Unfold step uinject
385 where
386 {-# INLINE_LATE step #-}
387 step st = do
388 r <- ustep st
389 case r of
390 Yield x s -> f x >>= \a -> return $ Yield a s
391 Skip s -> return $ Skip s
392 Stop -> return Stop
394{-# INLINE_NORMAL mapMWithInput #-}
395mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
396mapMWithInput f (Unfold ustep uinject) = Unfold step inject
397 where
398 inject a = do
399 r <- uinject a
400 return (a, r)
402 {-# INLINE_LATE step #-}
403 step (inp, st) = do
404 r <- ustep st
405 case r of
406 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
407 Skip s -> return $ Skip (inp, s)
408 Stop -> return Stop
410-------------------------------------------------------------------------------
411-- Either
412-------------------------------------------------------------------------------
414-- | Make an unfold operate on values wrapped in an @Either a a@ type. 'Right
415-- a' translates to 'Right b' and 'Left a' translates to 'Left b'.
416--
417-- /Internal/
418{-# INLINE_NORMAL either #-}
419either :: Monad m => Unfold m a b -> Unfold m (Either a a) (Either b b)
420either (Unfold step1 inject1) = Unfold step inject
422 where
424 inject (Left a) = do
425 r <- inject1 a
426 return (r, Left)
427 inject (Right a) = do
428 r <- inject1 a
429 return (r, Right)
431 {-# INLINE_LATE step #-}
432 step (st, f) = do
433 r <- step1 st
434 return $ case r of
435 Yield x s -> Yield (f x) (s, f)
436 Skip s -> Skip (s, f)
437 Stop -> Stop
439-- See StreamD.scanlM' for implementing this.
440--
441-- | Scan the output of an 'Unfold' to change it in a stateful manner.
442--
443-- /Unimplemented/
444{-# INLINE_NORMAL scanlM' #-}
445scanlM' :: -- Monad m =>
446 (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b
447scanlM' = undefined
449-------------------------------------------------------------------------------
450-- Convert streams into unfolds
451-------------------------------------------------------------------------------
453{-# INLINE_NORMAL fromStreamD #-}
454fromStreamD :: Monad m => Unfold m (Stream m a) a
455fromStreamD = Unfold step return
456 where
458 {-# INLINE_LATE step #-}
459 step (UnStream step1 state1) = do
460 r <- step1 defState state1
461 return $ case r of
462 Yield x s -> Yield x (Stream step1 s)
463 Skip s -> Skip (Stream step1 s)
464 Stop -> Stop
466{-# INLINE_NORMAL fromStreamK #-}
467fromStreamK :: Monad m => Unfold m (K.Stream m a) a
468fromStreamK = Unfold step return
470 where
472 {-# INLINE_LATE step #-}
473 step stream = do
474 r <- K.uncons stream
475 return $ case r of
476 Just (x, xs) -> Yield x xs
477 Nothing -> Stop
479-- XXX Using Unfold.fromStreamD seems to be faster (using cross product test
480-- case) than using fromStream even if it is implemented using fromStreamD.
481--
482-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
483-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
484--
485-- /Since: 0.8.0/
486--
487{-# INLINE_NORMAL fromStream #-}
488fromStream :: (K.IsStream t, Monad m) => Unfold m (t m a) a
489fromStream = lmap K.toStream fromStreamK
491-------------------------------------------------------------------------------
492-- Unfolds
493-------------------------------------------------------------------------------
495-- | Lift a monadic function into an unfold generating a nil stream with a side
496-- effect.
497--
498{-# INLINE nilM #-}
499nilM :: Monad m => (a -> m c) -> Unfold m a b
500nilM f = Unfold step return
501 where
502 {-# INLINE_LATE step #-}
503 step x = f x >> return Stop
505-- | Prepend a monadic single element generator function to an 'Unfold'. The
506-- same seed is used in the action as well as the unfold.
507--
508-- /Pre-release/
509{-# INLINE_NORMAL consM #-}
510consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
511consM action unf = Unfold step inject
513 where
515 inject = return . Left
517 {-# INLINE_LATE step #-}
518 step (Left a) =
519 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
520 step (Right (UnStream step1 st)) = do
521 res <- step1 defState st
522 case res of
523 Yield x s -> return $ Yield x (Right (Stream step1 s))
524 Skip s -> return $ Skip (Right (Stream step1 s))
525 Stop -> return Stop
527-- | Convert a list of pure values to a 'Stream'
528--
529-- /Since: 0.8.0/
530--
531{-# INLINE_LATE fromList #-}
532fromList :: Monad m => Unfold m [a] a
533fromList = Unfold step inject
534 where
535 inject = return
536 {-# INLINE_LATE step #-}
537 step (x:xs) = return $ Yield x xs
538 step [] = return Stop
540-- | Convert a list of monadic values to a 'Stream'
541--
542-- /Since: 0.8.0/
543--
544{-# INLINE_LATE fromListM #-}
545fromListM :: Monad m => Unfold m [m a] a
546fromListM = Unfold step inject
547 where
548 inject = return
549 {-# INLINE_LATE step #-}
550 step (x:xs) = x >>= \r -> return $ Yield r xs
551 step [] = return Stop
553------------------------------------------------------------------------------
554-- Specialized Generation
555------------------------------------------------------------------------------
557-- | Generates a stream replicating the seed @n@ times.
558--
559-- /Since: 0.8.0/
560--
561{-# INLINE replicateM #-}
562replicateM :: Monad m => Int -> Unfold m (m a) a
563replicateM n = Unfold step inject
565 where
567 inject x = return (x, n)
569 {-# INLINE_LATE step #-}
570 step (x, i) =
571 if i <= 0
572 then return Stop
573 else do
574 x1 <- x
575 return $ Yield x1 (x, i - 1)
577-- | Generates an infinite stream repeating the seed.
578--
579-- /Since: 0.8.0/
580--
581{-# INLINE repeatM #-}
582repeatM :: Monad m => Unfold m (m a) a
583repeatM = Unfold step return
584 where
585 {-# INLINE_LATE step #-}
586 step x = x >>= \x1 -> return $ Yield x1 x
588-- | Generates an infinite stream starting with the given seed and applying the
589-- given function repeatedly.
590--
591-- /Since: 0.8.0/
592--
593{-# INLINE iterateM #-}
594iterateM :: Monad m => (a -> m a) -> Unfold m (m a) a
595iterateM f = Unfold step id
596 where
597 {-# INLINE_LATE step #-}
598 step x = do
599 fx <- f x
600 return $ Yield x fx
602-- | @fromIndicesM gen@ generates an infinite stream of values using @gen@
603-- starting from the seed.
604--
605-- @
606-- fromIndicesM f = Unfold.mapM f $ Unfold.enumerateFrom 0
607-- @
608--
609-- /Pre-release/
610--
611{-# INLINE_NORMAL fromIndicesM #-}
612fromIndicesM :: Monad m => (Int -> m a) -> Unfold m Int a
613fromIndicesM gen = Unfold step return
614 where
615 {-# INLINE_LATE step #-}
616 step i = do
617 x <- gen i
618 return $ Yield x (i + 1)
620-------------------------------------------------------------------------------
621-- Filtering
622-------------------------------------------------------------------------------
624-- |
625-- >>> u = Unfold.take 2 Unfold.fromList
626-- >>> Unfold.fold Fold.toList u [1..100]
627-- [1,2]
628--
629-- /Since: 0.8.0/
630--
631{-# INLINE_NORMAL take #-}
632take :: Monad m => Int -> Unfold m a b -> Unfold m a b
633take n (Unfold step1 inject1) = Unfold step inject
634 where
635 inject x = do
636 s <- inject1 x
637 return (s, 0)
638 {-# INLINE_LATE step #-}
639 step (st, i) | i < n = do
640 r <- step1 st
641 return $ case r of
642 Yield x s -> Yield x (s, i + 1)
643 Skip s -> Skip (s, i)
644 Stop -> Stop
645 step (_, _) = return Stop
647-- | Same as 'filter' but with a monadic predicate.
648--
649-- /Since: 0.8.0/
650--
651{-# INLINE_NORMAL filterM #-}
652filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
653filterM f (Unfold step1 inject1) = Unfold step inject1
654 where
655 {-# INLINE_LATE step #-}
656 step st = do
657 r <- step1 st
658 case r of
659 Yield x s -> do
660 b <- f x
661 return $ if b then Yield x s else Skip s
662 Skip s -> return $ Skip s
663 Stop -> return Stop
665-- | Include only those elements that pass a predicate.
666--
667-- /Since: 0.8.0/
668--
669{-# INLINE filter #-}
670filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
671filter f = filterM (return . f)
673-- | @drop n unf@ drops @n@ elements from the stream generated by @unf@.
674--
675-- /Since: 0.8.0/
676--
677{-# INLINE_NORMAL drop #-}
678drop :: Monad m => Int -> Unfold m a b -> Unfold m a b
679drop n (Unfold step inject) = Unfold step' inject'
681 where
683 inject' a = do
684 b <- inject a
685 return (b, n)
687 {-# INLINE_LATE step' #-}
688 step' (st, i)
689 | i > 0 = do
690 r <- step st
691 return
692 $ case r of
693 Yield _ s -> Skip (s, i - 1)
694 Skip s -> Skip (s, i)
695 Stop -> Stop
696 | otherwise = do
697 r <- step st
698 return
699 $ case r of
700 Yield x s -> Yield x (s, 0)
701 Skip s -> Skip (s, 0)
702 Stop -> Stop
704-- | @dropWhileM f unf@ drops elements from the stream generated by @unf@ while
705-- the condition holds true. The condition function @f@ is /monadic/ in nature.
706--
707-- /Since: 0.8.0/
708--
709{-# INLINE_NORMAL dropWhileM #-}
710dropWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
711dropWhileM f (Unfold step inject) = Unfold step' inject'
713 where
715 inject' a = do
716 b <- inject a
717 return $ Left b
719 {-# INLINE_LATE step' #-}
720 step' (Left st) = do
721 r <- step st
722 case r of
723 Yield x s -> do
724 b <- f x
725 return
726 $ if b
727 then Skip (Left s)
728 else Yield x (Right s)
729 Skip s -> return $ Skip (Left s)
730 Stop -> return Stop
731 step' (Right st) = do
732 r <- step st
733 return
734 $ case r of
735 Yield x s -> Yield x (Right s)
736 Skip s -> Skip (Right s)
737 Stop -> Stop
739-- | Similar to 'dropWhileM' but with a pure condition function.
740--
741-- /Since: 0.8.0/
742--
743{-# INLINE dropWhile #-}
744dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
745dropWhile f = dropWhileM (return . f)
747------------------------------------------------------------------------------
748-- Exceptions
749------------------------------------------------------------------------------
751-- | Like 'gbracket' but with following differences:
752--
753-- * alloc action @a -> m c@ runs with async exceptions enabled
754-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
755-- after partial evaluation.
756-- * does not require a 'MonadAsync' constraint.
757--
758-- /Inhibits stream fusion/
759--
760-- /Pre-release/
761--
762{-# INLINE_NORMAL gbracket_ #-}
763gbracket_
764 :: Monad m
765 => (a -> m c) -- ^ before
766 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
767 -> (c -> m d) -- ^ after, on normal stop
768 -> Unfold m (c, e) b -- ^ on exception
769 -> Unfold m c b -- ^ unfold to run
770 -> Unfold m a b
771gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
772 Unfold step inject
774 where
776 inject x = do
777 r <- bef x
778 s <- inject1 r
779 return $ Right (s, r)
781 {-# INLINE_LATE step #-}
782 step (Right (st, v)) = do
783 res <- exc $ step1 st
784 case res of
785 Right r -> case r of
786 Yield x s -> return $ Yield x (Right (s, v))
787 Skip s -> return $ Skip (Right (s, v))
788 Stop -> aft v >> return Stop
789 -- XXX Do not handle async exceptions, just rethrow them.
790 Left e -> do
791 r <- einject (v, e)
792 return $ Skip (Left r)
793 step (Left st) = do
794 res <- estep st
795 case res of
796 Yield x s -> return $ Yield x (Left s)
797 Skip s -> return $ Skip (Left s)
798 Stop -> return Stop
800-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
801-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
802-- output @c@ as input to @Unfold m c b@ to generate an output stream. When
803-- unfolding use the supplied @try@ operation @forall s. m s -> m (Either e s)@
804-- to catch synchronous exceptions. If an exception occurs run the exception
805-- handling unfold @Unfold m (c, e) b@.
806--
807-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
808-- to a sync or async exception or if it gets garbage collected after a partial
809-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
810--
811-- 'gbracket' can express all other exception handling combinators.
812--
813-- /Inhibits stream fusion/
814--
815-- /Pre-release/
816{-# INLINE_NORMAL gbracket #-}
817gbracket
818 :: (MonadIO m, MonadBaseControl IO m)
819 => (a -> m c) -- ^ before
820 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
821 -> (c -> m d) -- ^ after, on normal stop, or GC
822 -> Unfold m (c, e) b -- ^ on exception
823 -> Unfold m c b -- ^ unfold to run
824 -> Unfold m a b
825gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
826 Unfold step inject
828 where
830 inject x = do
831 -- Mask asynchronous exceptions to make the execution of 'bef' and
832 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
833 (r, ref) <- liftBaseOp_ mask_ $ do
834 r <- bef x
835 ref <- newIOFinalizer (aft r)
836 return (r, ref)
837 s <- inject1 r
838 return $ Right (s, r, ref)
840 {-# INLINE_LATE step #-}
841 step (Right (st, v, ref)) = do
842 res <- exc $ step1 st
843 case res of
844 Right r -> case r of
845 Yield x s -> return $ Yield x (Right (s, v, ref))
846 Skip s -> return $ Skip (Right (s, v, ref))
847 Stop -> do
848 runIOFinalizer ref
849 return Stop
850 -- XXX Do not handle async exceptions, just rethrow them.
851 Left e -> do
852 -- Clearing of finalizer and running of exception handler must
853 -- be atomic wrt async exceptions. Otherwise if we have cleared
854 -- the finalizer and have not run the exception handler then we
855 -- may leak the resource.
856 r <- clearingIOFinalizer ref (einject (v, e))
857 return $ Skip (Left r)
858 step (Left st) = do
859 res <- estep st
860 case res of
861 Yield x s -> return $ Yield x (Left s)
862 Skip s -> return $ Skip (Left s)
863 Stop -> return Stop
865-- | Run a side effect @a -> m c@ on the input @a@ before unfolding it using
866-- @Unfold m a b@.
867--
868-- > before f = lmapM (\a -> f a >> return a)
869--
870-- /Pre-release/
871{-# INLINE_NORMAL before #-}
872before :: (a -> m c) -> Unfold m a b -> Unfold m a b
873before action (Unfold step inject) = Unfold step (action >> inject)
875-- The custom implementation of "after_" is slightly faster (5-7%) than
876-- "_after". This is just to document and make sure that we can always use
877-- gbracket to implement after_ The same applies to other combinators as well.
878--
879{-# INLINE_NORMAL _after #-}
880_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
881_after aft = gbracket_ return (fmap Right) aft undefined
883-- | Like 'after' with following differences:
884--
885-- * action @a -> m c@ won't run if the stream is garbage collected
886-- after partial evaluation.
887-- * Monad @m@ does not require any other constraints.
888--
889-- /Pre-release/
890{-# INLINE_NORMAL after_ #-}
891after_ :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
892after_ action (Unfold step1 inject1) = Unfold step inject
894 where
896 inject x = do
897 s <- inject1 x
898 return (s, x)
900 {-# INLINE_LATE step #-}
901 step (st, v) = do
902 res <- step1 st
903 case res of
904 Yield x s -> return $ Yield x (s, v)
905 Skip s -> return $ Skip (s, v)
906 Stop -> action v >> return Stop
908-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
909-- the unfold stops normally, or if it is garbage collected after a partial
910-- lazy evaluation.
911--
912-- The semantics of the action @a -> m c@ are similar to the cleanup action
913-- semantics in 'bracket'.
914--
915-- /See also 'after_'/
916--
917-- /Pre-release/
918{-# INLINE_NORMAL after #-}
919after :: (MonadIO m, MonadBaseControl IO m)
920 => (a -> m c) -> Unfold m a b -> Unfold m a b
921after action (Unfold step1 inject1) = Unfold step inject
923 where
925 inject x = do
926 s <- inject1 x
927 ref <- newIOFinalizer (action x)
928 return (s, ref)
930 {-# INLINE_LATE step #-}
931 step (st, ref) = do
932 res <- step1 st
933 case res of
934 Yield x s -> return $ Yield x (s, ref)
935 Skip s -> return $ Skip (s, ref)
936 Stop -> do
937 runIOFinalizer ref
938 return Stop
940{-# INLINE_NORMAL _onException #-}
941_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
942_onException action =
943 gbracket_ return MC.try
944 (\_ -> return ())
945 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
947-- | Unfold the input @a@ using @Unfold m a b@, run the action @a -> m c@ on
948-- @a@ if the unfold aborts due to an exception.
949--
950-- /Inhibits stream fusion/
951--
952-- /Pre-release/
953{-# INLINE_NORMAL onException #-}
954onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
955onException action (Unfold step1 inject1) = Unfold step inject
957 where
959 inject x = do
960 s <- inject1 x
961 return (s, x)
963 {-# INLINE_LATE step #-}
964 step (st, v) = do
965 res <- step1 st `MC.onException` action v
966 case res of
967 Yield x s -> return $ Yield x (s, v)
968 Skip s -> return $ Skip (s, v)
969 Stop -> return Stop
971{-# INLINE_NORMAL _finally #-}
972_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
973_finally action =
974 gbracket_ return MC.try action
975 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
977-- | Like 'finally' with following differences:
978--
979-- * action @a -> m c@ won't run if the stream is garbage collected
980-- after partial evaluation.
981-- * does not require a 'MonadAsync' constraint.
982--
983-- /Inhibits stream fusion/
984--
985-- /Pre-release/
986{-# INLINE_NORMAL finally_ #-}
987finally_ :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
988finally_ action (Unfold step1 inject1) = Unfold step inject
990 where
992 inject x = do
993 s <- inject1 x
994 return (s, x)
996 {-# INLINE_LATE step #-}
997 step (st, v) = do
998 res <- step1 st `MC.onException` action v
999 case res of
1000 Yield x s -> return $ Yield x (s, v)
1001 Skip s -> return $ Skip (s, v)
1002 Stop -> action v >> return Stop
1004-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1005-- the unfold stops normally, aborts due to an exception or if it is garbage
1006-- collected after a partial lazy evaluation.
1008-- The semantics of the action @a -> m c@ are similar to the cleanup action
1009-- semantics in 'bracket'.
1011-- @
1012-- finally release = bracket return release
1013-- @
1015-- /See also 'finally_'/
1017-- /Inhibits stream fusion/
1019-- /Pre-release/
1020{-# INLINE_NORMAL finally #-}
1021finally :: (MonadAsync m, MonadCatch m)
1022 => (a -> m c) -> Unfold m a b -> Unfold m a b
1023finally action (Unfold step1 inject1) = Unfold step inject
1025 where
1027 inject x = do
1028 s <- inject1 x
1029 ref <- newIOFinalizer (action x)
1030 return (s, ref)
1032 {-# INLINE_LATE step #-}
1033 step (st, ref) = do
1034 res <- step1 st `MC.onException` runIOFinalizer ref
1035 case res of
1036 Yield x s -> return $ Yield x (s, ref)
1037 Skip s -> return $ Skip (s, ref)
1038 Stop -> do
1039 runIOFinalizer ref
1040 return Stop
1042{-# INLINE_NORMAL _bracket #-}
1043_bracket :: MonadCatch m
1044 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1045_bracket bef aft =
1046 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1047 MC.throwM e))
1049-- | Like 'bracket' but with following differences:
1051-- * alloc action @a -> m c@ runs with async exceptions enabled
1052-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
1053-- after partial evaluation.
1054-- * does not require a 'MonadAsync' constraint.
1056-- /Inhibits stream fusion/
1058-- /Pre-release/
1059{-# INLINE_NORMAL bracket_ #-}
1060bracket_ :: MonadCatch m
1061 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1062bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
1064 where
1066 inject x = do
1067 r <- bef x
1068 s <- inject1 r
1069 return (s, r)
1071 {-# INLINE_LATE step #-}
1072 step (st, v) = do
1073 res <- step1 st `MC.onException` aft v
1074 case res of
1075 Yield x s -> return $ Yield x (s, v)
1076 Skip s -> return $ Skip (s, v)
1077 Stop -> aft v >> return Stop
1079-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1080-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1081-- output @c@ as input to @Unfold m c b@ to generate an output stream.
1083-- @c@ is usually a resource under the state of monad @m@, e.g. a file
1084-- handle, that requires a cleanup after use. The cleanup action @c -> m d@,
1085-- runs whenever the stream ends normally, due to a sync or async exception or
1086-- if it gets garbage collected after a partial lazy evaluation.
1088-- 'bracket' only guarantees that the cleanup action runs, and it runs with
1089-- async exceptions enabled. The action must ensure that it can successfully
1090-- cleanup the resource in the face of sync or async exceptions.
1092-- When the stream ends normally or on a sync exception, cleanup action runs
1093-- immediately in the current thread context, whereas in other cases it runs in
1094-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
1096-- /See also: 'bracket_', 'gbracket'/
1098-- /Inhibits stream fusion/
1100-- /Pre-release/
1101{-# INLINE_NORMAL bracket #-}
1102bracket :: (MonadAsync m, MonadCatch m)
1103 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1104bracket bef aft (Unfold step1 inject1) = Unfold step inject
1106 where
1108 inject x = do
1109 -- Mask asynchronous exceptions to make the execution of 'bef' and
1110 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1111 (r, ref) <- liftBaseOp_ mask_ $ do
1112 r <- bef x
1113 ref <- newIOFinalizer (aft r)
1114 return (r, ref)
1115 s <- inject1 r
1116 return (s, ref)
1118 {-# INLINE_LATE step #-}
1119 step (st, ref) = do
1120 res <- step1 st `MC.onException` runIOFinalizer ref
1121 case res of
1122 Yield x s -> return $ Yield x (s, ref)
1123 Skip s -> return $ Skip (s, ref)
1124 Stop -> do
1125 runIOFinalizer ref
1126 return Stop
1128-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1129-- @Unfold m e b@.
1131-- /Inhibits stream fusion/
1133-- /Pre-release/
1134{-# INLINE_NORMAL handle #-}
1135handle :: (MonadCatch m, Exception e)
1136 => Unfold m e b -> Unfold m a b -> Unfold m a b
1137handle exc =
1138 gbracket_ return MC.try (\_ -> return ()) (discardFirst exc)