4-- Module : Streamly.Internal.Data.Unfold
5-- Copyright : (c) 2019 Composewell Technologies
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- To run the examples in this module:
13-- >>> import qualified Streamly.Data.Fold as Fold
14-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
16-- = Unfolds and Streams
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.
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.
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
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.
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.
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
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
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'.
98-- On the other hand, transformation operations on stream types are as
99-- efficient as transformations on 'Unfold'.
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
107module Streamly.Internal.Data.Unfold
114 -- One to one correspondence with
115 -- "Streamly.Internal.Data.Stream.IsStream.Generate"
116 -- ** Basic Constructors
132 -- | Generate a monadic stream from a seed.
143 , enumerateFromThenNum
144 , enumerateFromStepNum
146 -- ** Enumerating 'Bounded 'Integral' Types
147 , enumerateFromIntegralBounded
148 , enumerateFromThenIntegralBounded
149 , enumerateFromToIntegralBounded
150 , enumerateFromThenToIntegralBounded
152 -- ** Enumerating 'Unounded Integral' Types
153 , enumerateFromIntegral
154 , enumerateFromThenIntegral
155 , enumerateFromToIntegral
156 , enumerateFromThenToIntegral
158 -- ** Enumerating 'Small Integral' Types
159 , enumerateFromSmallBounded
160 , enumerateFromThenSmallBounded
161 , enumerateFromToSmall
162 , enumerateFromThenToSmall
164 -- ** Enumerating 'Fractional' Types
165 , enumerateFromFractional
166 , enumerateFromThenFractional
167 , enumerateFromToFractional
168 , enumerateFromThenToFractional
170 -- ** From Containers
179 -- ** Mapping on Input
194 -- ** Mapping on Output
203 -- ** Either Wrapped Input
232 -- ** Resource Management
249import Control.Exception (Exception, mask_)
250import Control.Monad.Catch (MonadCatch)
251import Control.Monad.IO.Class (MonadIO(..))
252import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
253import Data.Functor (($>))
254import GHC.Types (SPEC(..))
255import Streamly.Internal.Control.Concurrent (MonadAsync)
256import Streamly.Internal.Data.Fold.Type (Fold(..))
257import Streamly.Internal.Data.IOFinalizer
258 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
259import Streamly.Internal.Data.Stream.IsStream.Type (IsStream)
260import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
261import Streamly.Internal.Data.SVar.Type (defState)
263import qualified Control.Monad.Catch as MC
264import qualified Data.Tuple as Tuple
265import qualified Streamly.Internal.Data.Fold.Type as FL
266import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
267import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
268import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
270import Streamly.Internal.Data.Unfold.Enumeration
271import Streamly.Internal.Data.Unfold.Type
273 hiding (map, mapM, takeWhile, take, filter, const, zipWith
274 , drop, dropWhile, either)
277-- >>> import qualified Streamly.Data.Fold as Fold
278-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
279-- >>> import qualified Streamly.Prelude as Stream
282-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
283-- using the argument of the original fold as the second element of tuple and
284-- discarding the first element of the tuple.
287-- discardFirst = Unfold.lmap snd
292{-# INLINE_NORMAL discardFirst #-}
293discardFirst :: Unfold m a b -> Unfold m (c, a) b
294discardFirst = lmap snd
296-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
297-- using the argument of the original fold as the first element of tuple and
298-- discarding the second element of the tuple.
301-- discardSecond = Unfold.lmap fst
306{-# INLINE_NORMAL discardSecond #-}
307discardSecond :: Unfold m a b -> Unfold m (a, c) b
308discardSecond = lmap fst
310-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
311-- that accepts a tuple with elements swapped.
314-- swap = Unfold.lmap Tuple.swap
319{-# INLINE_NORMAL swap #-}
320swap :: Unfold m (a, c) b -> Unfold m (c, a) b
321swap = lmap Tuple.swap
323-------------------------------------------------------------------------------
325-------------------------------------------------------------------------------
327-- XXX Do we need this combinator or the stream based idiom is enough?
329-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
330-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
331-- application of the fold on the unfolded stream.
333-- >>> Unfold.fold Fold.sum Unfold.fromList [1..100]
336-- >>> fold f u = Stream.fold f . Stream.unfold u
340{-# INLINE_NORMAL fold #-}
341fold :: Monad m => Fold m b c -> Unfold m a b -> a -> m c
342fold (Fold fstep initial extract) (Unfold ustep inject) a = do
345 FL.Partial x -> inject a >>= go SPEC x
346 FL.Done b -> return b
350 {-# INLINE_LATE go #-}
357 FL.Partial fs1 -> go SPEC fs1 s
358 FL.Done c -> return c
359 Skip s -> go SPEC fs s
362-- | Apply a fold multiple times on the output of an unfold.
365foldMany :: -- Monad m =>
366 Fold m b c -> Unfold m a b -> Unfold m a c
369-- | Apply a monadic function to each element of the stream and replace it
370-- with the output of the resulting action.
374{-# INLINE_NORMAL mapM #-}
375mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
376mapM f (Unfold ustep uinject) = Unfold step uinject
378 {-# INLINE_LATE step #-}
382 Yield x s -> f x >>= \a -> return $ Yield a s
383 Skip s -> return $ Skip s
386{-# INLINE_NORMAL mapMWithInput #-}
387mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
388mapMWithInput f (Unfold ustep uinject) = Unfold step inject
394 {-# INLINE_LATE step #-}
398 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
399 Skip s -> return $ Skip (inp, s)
402-------------------------------------------------------------------------------
404-------------------------------------------------------------------------------
406-- | Make an unfold operate on values wrapped in an @Either a a@ type. 'Right
407-- a' translates to 'Right b' and 'Left a' translates to 'Left b'.
410{-# INLINE_NORMAL either #-}
411either :: Applicative m => Unfold m a b -> Unfold m (Either a a) (Either b b)
412either (Unfold step1 inject1) = Unfold step inject
416 inject (Left a) = (, Left) <$> inject1 a
417 inject (Right a) = (, Right) <$> inject1 a
419 {-# INLINE_LATE step #-}
422 Yield x s -> Yield (f x) (s, f)
423 Skip s -> Skip (s, f)
424 Stop -> Stop) <$> step1 st
426-- | Scan the output of an 'Unfold' to change it in a stateful manner.
429{-# INLINE_NORMAL scan #-}
430scan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c
431scan (Fold stepF initial extract) (Unfold stepU injectU) =
439 FL.Partial fs -> Just . (fs,) <$> injectU a
440 FL.Done _ -> return Nothing
442 {-# INLINE_LATE step #-}
443 step (Just (fs, us)) = do
449 FL.Done v -> return $ Yield v Nothing
452 return $ Yield v (Just (fs1, s))
453 Skip s -> return $ Skip (Just (fs, s))
456 step Nothing = return Stop
458-- | Scan the output of an 'Unfold' to change it in a stateful manner.
461{-# INLINE_NORMAL scanlM' #-}
462scanlM' :: Monad m => (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b
463scanlM' f z = scan (FL.foldlM' f z)
465-------------------------------------------------------------------------------
466-- Convert streams into unfolds
467-------------------------------------------------------------------------------
469{-# INLINE_NORMAL fromStreamD #-}
470fromStreamD :: Applicative m => Unfold m (Stream m a) a
471fromStreamD = Unfold step pure
475 {-# INLINE_LATE step #-}
476 step (UnStream step1 state1) =
478 Yield x s -> Yield x (Stream step1 s)
479 Skip s -> Skip (Stream step1 s)
480 Stop -> Stop) <$> step1 defState state1
482{-# INLINE_NORMAL fromStreamK #-}
483fromStreamK :: Applicative m => Unfold m (K.Stream m a) a
484fromStreamK = Unfold step pure
488 {-# INLINE_LATE step #-}
491 Just (x, xs) -> Yield x xs
492 Nothing -> Stop) <$> K.uncons stream
494-- XXX Using Unfold.fromStreamD seems to be faster (using cross product test
495-- case) than using fromStream even if it is implemented using fromStreamD.
496-- Check if StreamK to StreamD rewrite rules are working correctly when
497-- implementing fromStream using fromStreamD.
499-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
500-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
504{-# INLINE_NORMAL fromStream #-}
505fromStream :: (IsStream t, Monad m) => Unfold m (t m a) a
506fromStream = lmap IsStream.toStream fromStreamK
508-------------------------------------------------------------------------------
510-------------------------------------------------------------------------------
512-- | Lift a monadic function into an unfold generating a nil stream with a side
516nilM :: Applicative m => (a -> m c) -> Unfold m a b
517nilM f = Unfold step pure
521 {-# INLINE_LATE step #-}
524-- | Prepend a monadic single element generator function to an 'Unfold'. The
525-- same seed is used in the action as well as the unfold.
528{-# INLINE_NORMAL consM #-}
529consM :: Applicative m => (a -> m b) -> Unfold m a b -> Unfold m a b
530consM action unf = Unfold step inject
536 {-# INLINE_LATE step #-}
537 step (Left a) = (`Yield` Right (D.unfold unf a)) <$> action a
538 step (Right (UnStream step1 st)) = do
540 Yield x s -> Yield x (Right (Stream step1 s))
541 Skip s -> Skip (Right (Stream step1 s))
542 Stop -> Stop) <$> step1 defState st
544-- XXX Check if "unfold (fromList [1..10])" fuses, if it doesn't we can use
545-- rewrite rules to rewrite list enumerations to unfold enumerations.
547-- | Convert a list of pure values to a 'Stream'
551{-# INLINE_LATE fromList #-}
552fromList :: Monad m => Unfold m [a] a
553fromList = Unfold step pure
557 {-# INLINE_LATE step #-}
558 step (x:xs) = pure $ Yield x xs
561-- | Convert a list of monadic values to a 'Stream'
565{-# INLINE_LATE fromListM #-}
566fromListM :: Monad m => Unfold m [m a] a
567fromListM = Unfold step pure
571 {-# INLINE_LATE step #-}
572 step (x:xs) = (`Yield` xs) <$> x
575------------------------------------------------------------------------------
576-- Specialized Generation
577------------------------------------------------------------------------------
579-- | Generates a stream replicating the seed @n@ times.
583{-# INLINE replicateM #-}
584replicateM :: Monad m => Int -> Unfold m (m a) a
585replicateM n = Unfold step inject
589 inject action = pure (action, n)
591 {-# INLINE_LATE step #-}
595 else (\x -> Yield x (action, i - 1)) <$> action
597-- | Generates an infinite stream repeating the seed.
601{-# INLINE repeatM #-}
602repeatM :: Monad m => Unfold m (m a) a
603repeatM = Unfold step pure
607 {-# INLINE_LATE step #-}
608 step action = (`Yield` action) <$> action
610-- | Generates an infinite stream starting with the given seed and applying the
611-- given function repeatedly.
615{-# INLINE iterateM #-}
616iterateM :: Monad m => (a -> m a) -> Unfold m (m a) a
617iterateM f = Unfold step id
621 {-# INLINE_LATE step #-}
622 step x = Yield x <$> f x
624-- | @fromIndicesM gen@ generates an infinite stream of values using @gen@
625-- starting from the seed.
628-- fromIndicesM f = Unfold.mapM f $ Unfold.enumerateFrom 0
633{-# INLINE_NORMAL fromIndicesM #-}
634fromIndicesM :: Applicative m => (Int -> m a) -> Unfold m Int a
635fromIndicesM gen = Unfold step pure
639 {-# INLINE_LATE step #-}
640 step i = (`Yield` (i + 1)) <$> gen i
642-------------------------------------------------------------------------------
644-------------------------------------------------------------------------------
647-- >>> u = Unfold.take 2 Unfold.fromList
648-- >>> Unfold.fold Fold.toList u [1..100]
653{-# INLINE_NORMAL take #-}
654take :: Monad m => Int -> Unfold m a b -> Unfold m a b
655take n (Unfold step1 inject1) = Unfold step inject
659 inject x = (, 0) <$> inject1 x
661 {-# INLINE_LATE step #-}
662 step (st, i) | i < n = do
664 Yield x s -> Yield x (s, i + 1)
665 Skip s -> Skip (s, i)
666 Stop -> Stop) <$> step1 st
667 step (_, _) = pure Stop
669-- | Same as 'filter' but with a monadic predicate.
673{-# INLINE_NORMAL filterM #-}
674filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
675filterM f (Unfold step1 inject1) = Unfold step inject1
677 {-# INLINE_LATE step #-}
683 return $ if b then Yield x s else Skip s
684 Skip s -> return $ Skip s
687-- | Include only those elements that pass a predicate.
692filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
693filter f = filterM (return . f)
695-- | @drop n unf@ drops @n@ elements from the stream generated by @unf@.
699{-# INLINE_NORMAL drop #-}
700drop :: Monad m => Int -> Unfold m a b -> Unfold m a b
701drop n (Unfold step inject) = Unfold step' inject'
705 inject' a = (, n) <$> inject a
707 {-# INLINE_LATE step' #-}
711 Yield _ s -> Skip (s, i - 1)
712 Skip s -> Skip (s, i)
713 Stop -> Stop) <$> step st
716 Yield x s -> Yield x (s, 0)
717 Skip s -> Skip (s, 0)
718 Stop -> Stop) <$> step st
720-- | @dropWhileM f unf@ drops elements from the stream generated by @unf@ while
721-- the condition holds true. The condition function @f@ is /monadic/ in nature.
725{-# INLINE_NORMAL dropWhileM #-}
726dropWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
727dropWhileM f (Unfold step inject) = Unfold step' inject'
735 {-# INLINE_LATE step' #-}
744 else Yield x (Right s)
745 Skip s -> return $ Skip (Left s)
747 step' (Right st) = do
751 Yield x s -> Yield x (Right s)
752 Skip s -> Skip (Right s)
755-- | Similar to 'dropWhileM' but with a pure condition function.
759{-# INLINE dropWhile #-}
760dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
761dropWhile f = dropWhileM (return . f)
763------------------------------------------------------------------------------
765------------------------------------------------------------------------------
767-- | Like 'gbracket' but with following differences:
769-- * alloc action @a -> m c@ runs with async exceptions enabled
770-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
771-- after partial evaluation.
772-- * does not require a 'MonadAsync' constraint.
774-- /Inhibits stream fusion/
778{-# INLINE_NORMAL gbracket_ #-}
781 => (a -> m c) -- ^ before
782 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
783 -> (c -> m d) -- ^ after, on normal stop
784 -> Unfold m (c, e) b -- ^ on exception
785 -> Unfold m c b -- ^ unfold to run
787gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
795 return $ Right (s, r)
797 {-# INLINE_LATE step #-}
798 step (Right (st, v)) = do
799 res <- exc $ step1 st
802 Yield x s -> return $ Yield x (Right (s, v))
803 Skip s -> return $ Skip (Right (s, v))
804 Stop -> aft v >> return Stop
805 -- XXX Do not handle async exceptions, just rethrow them.
808 return $ Skip (Left r)
812 Yield x s -> Yield x (Left s)
813 Skip s -> Skip (Left s)
816-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
817-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
818-- output @c@ as input to @Unfold m c b@ to generate an output stream. When
819-- unfolding use the supplied @try@ operation @forall s. m s -> m (Either e s)@
820-- to catch synchronous exceptions. If an exception occurs run the exception
821-- handling unfold @Unfold m (c, e) b@.
823-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
824-- to a sync or async exception or if it gets garbage collected after a partial
825-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
827-- 'gbracket' can express all other exception handling combinators.
829-- /Inhibits stream fusion/
832{-# INLINE_NORMAL gbracket #-}
834 :: (MonadIO m, MonadBaseControl IO m)
835 => (a -> m c) -- ^ before
836 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
837 -> (c -> m d) -- ^ after, on normal stop, or GC
838 -> Unfold m (c, e) b -- ^ on exception
839 -> Unfold m c b -- ^ unfold to run
841gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
847 -- Mask asynchronous exceptions to make the execution of 'bef' and
848 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
849 (r, ref) <- liftBaseOp_ mask_ $ do
851 ref <- newIOFinalizer (aft r)
854 return $ Right (s, r, ref)
856 {-# INLINE_LATE step #-}
857 step (Right (st, v, ref)) = do
858 res <- exc $ step1 st
861 Yield x s -> return $ Yield x (Right (s, v, ref))
862 Skip s -> return $ Skip (Right (s, v, ref))
866 -- XXX Do not handle async exceptions, just rethrow them.
868 -- Clearing of finalizer and running of exception handler must
869 -- be atomic wrt async exceptions. Otherwise if we have cleared
870 -- the finalizer and have not run the exception handler then we
871 -- may leak the resource.
872 r <- clearingIOFinalizer ref (einject (v, e))
873 return $ Skip (Left r)
877 Yield x s -> Yield x (Left s)
878 Skip s -> Skip (Left s)
881-- | Run a side effect @a -> m c@ on the input @a@ before unfolding it using
884-- > before f = lmapM (\a -> f a >> return a)
887{-# INLINE_NORMAL before #-}
888before :: (a -> m c) -> Unfold m a b -> Unfold m a b
889before action (Unfold step inject) = Unfold step (action >> inject)
891-- The custom implementation of "after_" is slightly faster (5-7%) than
892-- "_after". This is just to document and make sure that we can always use
893-- gbracket to implement after_ The same applies to other combinators as well.
895{-# INLINE_NORMAL _after #-}
896_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
897_after aft = gbracket_ return (fmap Right) aft undefined
899-- | Like 'after' with following differences:
901-- * action @a -> m c@ won't run if the stream is garbage collected
902-- after partial evaluation.
903-- * Monad @m@ does not require any other constraints.
906{-# INLINE_NORMAL after_ #-}
907after_ :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
908after_ action (Unfold step1 inject1) = Unfold step inject
916 {-# INLINE_LATE step #-}
920 Yield x s -> return $ Yield x (s, v)
921 Skip s -> return $ Skip (s, v)
922 Stop -> action v >> return Stop
924-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
925-- the unfold stops normally, or if it is garbage collected after a partial
928-- The semantics of the action @a -> m c@ are similar to the cleanup action
929-- semantics in 'bracket'.
931-- /See also 'after_'/
934{-# INLINE_NORMAL after #-}
935after :: (MonadIO m, MonadBaseControl IO m)
936 => (a -> m c) -> Unfold m a b -> Unfold m a b
937after action (Unfold step1 inject1) = Unfold step inject
943 ref <- newIOFinalizer (action x)
946 {-# INLINE_LATE step #-}
950 Yield x s -> return $ Yield x (s, ref)
951 Skip s -> return $ Skip (s, ref)
956{-# INLINE_NORMAL _onException #-}
957_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
959 gbracket_ return MC.try
961 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
963-- | Unfold the input @a@ using @Unfold m a b@, run the action @a -> m c@ on
964-- @a@ if the unfold aborts due to an exception.
966-- /Inhibits stream fusion/
969{-# INLINE_NORMAL onException #-}
970onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
971onException action (Unfold step1 inject1) = Unfold step inject
979 {-# INLINE_LATE step #-}
981 res <- step1 st `MC.onException` action v
983 Yield x s -> Yield x (s, v)
984 Skip s -> Skip (s, v)
987{-# INLINE_NORMAL _finally #-}
988_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
990 gbracket_ return MC.try action
991 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
993-- | Like 'finally' with following differences:
995-- * action @a -> m c@ won't run if the stream is garbage collected
996-- after partial evaluation.
997-- * does not require a 'MonadAsync' constraint.
999-- /Inhibits stream fusion/
1002{-# INLINE_NORMAL finally_ #-}
1003finally_ :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
1004finally_ action (Unfold step1 inject1) = Unfold step inject
1012 {-# INLINE_LATE step #-}
1014 res <- step1 st `MC.onException` action v
1016 Yield x s -> return $ Yield x (s, v)
1017 Skip s -> return $ Skip (s, v)
1018 Stop -> action v >> return Stop
1020-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1021-- the unfold stops normally, aborts due to an exception or if it is garbage
1022-- collected after a partial lazy evaluation.
1024-- The semantics of the action @a -> m c@ are similar to the cleanup action
1025-- semantics in 'bracket'.
1028-- finally release = bracket return release
1031-- /See also 'finally_'/
1033-- /Inhibits stream fusion/
1036{-# INLINE_NORMAL finally #-}
1037finally :: (MonadAsync m, MonadCatch m)
1038 => (a -> m c) -> Unfold m a b -> Unfold m a b
1039finally action (Unfold step1 inject1) = Unfold step inject
1045 ref <- newIOFinalizer (action x)
1048 {-# INLINE_LATE step #-}
1050 res <- step1 st `MC.onException` runIOFinalizer ref
1052 Yield x s -> return $ Yield x (s, ref)
1053 Skip s -> return $ Skip (s, ref)
1058{-# INLINE_NORMAL _bracket #-}
1059_bracket :: MonadCatch m
1060 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1062 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1065-- | Like 'bracket' but with following differences:
1067-- * alloc action @a -> m c@ runs with async exceptions enabled
1068-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
1069-- after partial evaluation.
1070-- * does not require a 'MonadAsync' constraint.
1072-- /Inhibits stream fusion/
1075{-# INLINE_NORMAL bracket_ #-}
1076bracket_ :: MonadCatch m
1077 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1078bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
1087 {-# INLINE_LATE step #-}
1089 res <- step1 st `MC.onException` aft v
1091 Yield x s -> return $ Yield x (s, v)
1092 Skip s -> return $ Skip (s, v)
1093 Stop -> aft v >> return Stop
1095-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1096-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1097-- output @c@ as input to @Unfold m c b@ to generate an output stream.
1099-- @c@ is usually a resource under the state of monad @m@, e.g. a file
1100-- handle, that requires a cleanup after use. The cleanup action @c -> m d@,
1101-- runs whenever the stream ends normally, due to a sync or async exception or
1102-- if it gets garbage collected after a partial lazy evaluation.
1104-- 'bracket' only guarantees that the cleanup action runs, and it runs with
1105-- async exceptions enabled. The action must ensure that it can successfully
1106-- cleanup the resource in the face of sync or async exceptions.
1108-- When the stream ends normally or on a sync exception, cleanup action runs
1109-- immediately in the current thread context, whereas in other cases it runs in
1110-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
1112-- /See also: 'bracket_', 'gbracket'/
1114-- /Inhibits stream fusion/
1117{-# INLINE_NORMAL bracket #-}
1118bracket :: (MonadAsync m, MonadCatch m)
1119 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1120bracket bef aft (Unfold step1 inject1) = Unfold step inject
1125 -- Mask asynchronous exceptions to make the execution of 'bef' and
1126 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1127 (r, ref) <- liftBaseOp_ mask_ $ do
1129 ref <- newIOFinalizer (aft r)
1134 {-# INLINE_LATE step #-}
1136 res <- step1 st `MC.onException` runIOFinalizer ref
1138 Yield x s -> return $ Yield x (s, ref)
1139 Skip s -> return $ Skip (s, ref)
1144-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1147-- /Inhibits stream fusion/
1150{-# INLINE_NORMAL handle #-}
1151handle :: (MonadCatch m, Exception e)
1152 => Unfold m e b -> Unfold m a b -> Unfold m a b
1154 gbracket_ return MC.try (\_ -> return ()) (discardFirst exc)
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list