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
119 -- One to one correspondence with
120 -- "Streamly.Internal.Data.Stream.IsStream.Generate"
121 -- ** Basic Constructors
137 -- | Generate a monadic stream from a seed.
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
184 -- ** Mapping on Input
197 -- ** 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 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
271 hiding (map, mapM, takeWhile, take, filter, const, zipWith
272 , drop, dropWhile, either)
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.
285-- discardFirst = Unfold.lmap snd
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.
299-- discardSecond = Unfold.lmap fst
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.
312-- swap = Unfold.lmap Tuple.swap
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.
323-- | Scan the input of an 'Unfold' to change it in a stateful manner.
326{-# INLINE_NORMAL lscanlM' #-}
327lscanlM' :: -- Monad m =>
328 (b -> a -> m b) -> m b -> Unfold m b c -> Unfold m a c
331-------------------------------------------------------------------------------
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.
341-- >>> Unfold.fold Fold.sum Unfold.fromList [1..100]
344-- >>> fold f u = Stream.fold f . Stream.unfold u
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
353 FL.Partial x -> inject a >>= go SPEC x
354 FL.Done b -> return b
358 {-# INLINE_LATE go #-}
365 FL.Partial fs1 -> go SPEC fs1 s
366 FL.Done c -> return c
367 Skip s -> go SPEC fs s
370-- | Apply a fold multiple times on the output of an unfold.
373foldMany :: -- Monad m =>
374 Fold m b c -> Unfold m a b -> Unfold m a c
377-- | Apply a monadic function to each element of the stream and replace it
378-- with the output of the resulting action.
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
386 {-# INLINE_LATE step #-}
390 Yield x s -> f x >>= \a -> return $ Yield a s
391 Skip s -> return $ Skip s
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
402 {-# INLINE_LATE step #-}
406 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
407 Skip s -> return $ Skip (inp, s)
410-------------------------------------------------------------------------------
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'.
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
427 inject (Right a) = do
431 {-# INLINE_LATE step #-}
435 Yield x s -> Yield (f x) (s, f)
436 Skip s -> Skip (s, f)
439-- See StreamD.scanlM' for implementing this.
441-- | Scan the output of an 'Unfold' to change it in a stateful manner.
444{-# INLINE_NORMAL scanlM' #-}
445scanlM' :: -- Monad m =>
446 (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b
449-------------------------------------------------------------------------------
450-- Convert streams into unfolds
451-------------------------------------------------------------------------------
453{-# INLINE_NORMAL fromStreamD #-}
454fromStreamD :: Monad m => Unfold m (Stream m a) a
455fromStreamD = Unfold step return
458 {-# INLINE_LATE step #-}
459 step (UnStream step1 state1) = do
460 r <- step1 defState state1
462 Yield x s -> Yield x (Stream step1 s)
463 Skip s -> Skip (Stream step1 s)
466{-# INLINE_NORMAL fromStreamK #-}
467fromStreamK :: Monad m => Unfold m (K.Stream m a) a
468fromStreamK = Unfold step return
472 {-# INLINE_LATE step #-}
476 Just (x, xs) -> Yield x xs
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.
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.
487{-# INLINE_NORMAL fromStream #-}
488fromStream :: (K.IsStream t, Monad m) => Unfold m (t m a) a
489fromStream = lmap K.toStream fromStreamK
491-------------------------------------------------------------------------------
493-------------------------------------------------------------------------------
495-- | Lift a monadic function into an unfold generating a nil stream with a side
499nilM :: Monad m => (a -> m c) -> Unfold m a b
500nilM f = Unfold step return
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.
509{-# INLINE_NORMAL consM #-}
510consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
511consM action unf = Unfold step inject
515 inject = return . Left
517 {-# INLINE_LATE step #-}
519 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
520 step (Right (UnStream step1 st)) = do
521 res <- step1 defState st
523 Yield x s -> return $ Yield x (Right (Stream step1 s))
524 Skip s -> return $ Skip (Right (Stream step1 s))
527-- | Convert a list of pure values to a 'Stream'
531{-# INLINE_LATE fromList #-}
532fromList :: Monad m => Unfold m [a] a
533fromList = Unfold step inject
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'
544{-# INLINE_LATE fromListM #-}
545fromListM :: Monad m => Unfold m [m a] a
546fromListM = Unfold step inject
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.
561{-# INLINE replicateM #-}
562replicateM :: Monad m => Int -> Unfold m (m a) a
563replicateM n = Unfold step inject
567 inject x = return (x, n)
569 {-# INLINE_LATE step #-}
575 return $ Yield x1 (x, i - 1)
577-- | Generates an infinite stream repeating the seed.
581{-# INLINE repeatM #-}
582repeatM :: Monad m => Unfold m (m a) a
583repeatM = Unfold step return
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.
593{-# INLINE iterateM #-}
594iterateM :: Monad m => (a -> m a) -> Unfold m (m a) a
595iterateM f = Unfold step id
597 {-# INLINE_LATE step #-}
602-- | @fromIndicesM gen@ generates an infinite stream of values using @gen@
603-- starting from the seed.
606-- fromIndicesM f = Unfold.mapM f $ Unfold.enumerateFrom 0
611{-# INLINE_NORMAL fromIndicesM #-}
612fromIndicesM :: Monad m => (Int -> m a) -> Unfold m Int a
613fromIndicesM gen = Unfold step return
615 {-# INLINE_LATE step #-}
618 return $ Yield x (i + 1)
620-------------------------------------------------------------------------------
622-------------------------------------------------------------------------------
625-- >>> u = Unfold.take 2 Unfold.fromList
626-- >>> Unfold.fold Fold.toList u [1..100]
631{-# INLINE_NORMAL take #-}
632take :: Monad m => Int -> Unfold m a b -> Unfold m a b
633take n (Unfold step1 inject1) = Unfold step inject
638 {-# INLINE_LATE step #-}
639 step (st, i) | i < n = do
642 Yield x s -> Yield x (s, i + 1)
643 Skip s -> Skip (s, i)
645 step (_, _) = return Stop
647-- | Same as 'filter' but with a monadic predicate.
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
655 {-# INLINE_LATE step #-}
661 return $ if b then Yield x s else Skip s
662 Skip s -> return $ Skip s
665-- | Include only those elements that pass a predicate.
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@.
677{-# INLINE_NORMAL drop #-}
678drop :: Monad m => Int -> Unfold m a b -> Unfold m a b
679drop n (Unfold step inject) = Unfold step' inject'
687 {-# INLINE_LATE step' #-}
693 Yield _ s -> Skip (s, i - 1)
694 Skip s -> Skip (s, i)
700 Yield x s -> Yield x (s, 0)
701 Skip s -> Skip (s, 0)
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.
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'
719 {-# INLINE_LATE step' #-}
728 else Yield x (Right s)
729 Skip s -> return $ Skip (Left s)
731 step' (Right st) = do
735 Yield x s -> Yield x (Right s)
736 Skip s -> Skip (Right s)
739-- | Similar to 'dropWhileM' but with a pure condition function.
743{-# INLINE dropWhile #-}
744dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
745dropWhile f = dropWhileM (return . f)
747------------------------------------------------------------------------------
749------------------------------------------------------------------------------
751-- | Like 'gbracket' but with following differences:
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.
758-- /Inhibits stream fusion/
762{-# INLINE_NORMAL gbracket_ #-}
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
771gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
779 return $ Right (s, r)
781 {-# INLINE_LATE step #-}
782 step (Right (st, v)) = do
783 res <- exc $ step1 st
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.
792 return $ Skip (Left r)
796 Yield x s -> return $ Yield x (Left s)
797 Skip s -> return $ Skip (Left s)
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@.
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.
811-- 'gbracket' can express all other exception handling combinators.
813-- /Inhibits stream fusion/
816{-# INLINE_NORMAL gbracket #-}
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
825gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
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
835 ref <- newIOFinalizer (aft r)
838 return $ Right (s, r, ref)
840 {-# INLINE_LATE step #-}
841 step (Right (st, v, ref)) = do
842 res <- exc $ step1 st
845 Yield x s -> return $ Yield x (Right (s, v, ref))
846 Skip s -> return $ Skip (Right (s, v, ref))
850 -- XXX Do not handle async exceptions, just rethrow them.
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)
861 Yield x s -> return $ Yield x (Left s)
862 Skip s -> return $ Skip (Left s)
865-- | Run a side effect @a -> m c@ on the input @a@ before unfolding it using
868-- > before f = lmapM (\a -> f a >> return a)
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.
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:
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.
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
900 {-# INLINE_LATE step #-}
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
912-- The semantics of the action @a -> m c@ are similar to the cleanup action
913-- semantics in 'bracket'.
915-- /See also 'after_'/
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
927 ref <- newIOFinalizer (action x)
930 {-# INLINE_LATE step #-}
934 Yield x s -> return $ Yield x (s, ref)
935 Skip s -> return $ Skip (s, ref)
940{-# INLINE_NORMAL _onException #-}
941_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
943 gbracket_ return MC.try
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.
950-- /Inhibits stream fusion/
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
963 {-# INLINE_LATE step #-}
965 res <- step1 st `MC.onException` action v
967 Yield x s -> return $ Yield x (s, v)
968 Skip s -> return $ Skip (s, v)
971{-# INLINE_NORMAL _finally #-}
972_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
974 gbracket_ return MC.try action
975 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
977-- | Like 'finally' with following differences:
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.
983-- /Inhibits stream fusion/
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
996 {-# INLINE_LATE step #-}
998 res <- step1 st `MC.onException` action v
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'.
1012-- finally release = bracket return release
1015-- /See also 'finally_'/
1017-- /Inhibits stream fusion/
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
1029 ref <- newIOFinalizer (action x)
1032 {-# INLINE_LATE step #-}
1034 res <- step1 st `MC.onException` runIOFinalizer ref
1036 Yield x s -> return $ Yield x (s, ref)
1037 Skip s -> return $ Skip (s, ref)
1042{-# INLINE_NORMAL _bracket #-}
1043_bracket :: MonadCatch m
1044 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1046 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
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/
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
1071 {-# INLINE_LATE step #-}
1073 res <- step1 st `MC.onException` aft v
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/
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
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
1113 ref <- newIOFinalizer (aft r)
1118 {-# INLINE_LATE step #-}
1120 res <- step1 st `MC.onException` runIOFinalizer ref
1122 Yield x s -> return $ Yield x (s, ref)
1123 Skip s -> return $ Skip (s, ref)
1128-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1131-- /Inhibits stream fusion/
1134{-# INLINE_NORMAL handle #-}
1135handle :: (MonadCatch m, Exception e)
1136 => Unfold m e b -> Unfold m a b -> Unfold m a b
1138 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