4-- Module : Streamly.Internal.Data.Unfold
5-- Copyright : (c) 2019 Composewell Technologies
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
11-- Streams forcing a closed control flow loop can be categorized under
12-- two types, unfolds and folds, both of these are duals of each other.
14-- Unfold streams are really generators of a sequence of elements, we can also
15-- call them pull style streams. These are lazy producers of streams. On each
16-- evaluation the producer generates the next element. A consumer can
17-- therefore pull elements from the stream whenever it wants to. A stream
18-- consumer can multiplex pull streams by pulling elements from the chosen
19-- streams, therefore, pull streams allow merging or multiplexing. On the
20-- other hand, with this representation we cannot split or demultiplex a
21-- stream. So really these are stream sources that can be generated from a
22-- seed and can be merged or zipped into a single stream.
24-- The dual of Unfolds are Folds. Folds can also be called as push style
25-- streams or reducers. These are strict consumers of streams. We keep pushing
26-- elements to a fold and we can extract the result at any point. A driver can
27-- choose which fold to push to and can also push the same element to multiple
28-- folds. Therefore, folds allow splitting or demultiplexing a stream. On the
29-- other hand, we cannot merge streams using this representation. So really
30-- these are stream consumers that reduce the stream to a single value, these
31-- consumers can be composed such that a stream can be split over multiple
36-- Composing a tree or graph of computations with unfolds can be much more
37-- efficient compared to composing with the Monad instance. The reason is that
38-- unfolds allow the compiler to statically know the state and optimize it
39-- using stream fusion whereas it is not possible with the monad bind because
40-- the state is determined dynamically.
42-- Open control flow style streams can also have two representations. StreamK
43-- is a producer style representation. We can also have a consumer style
44-- representation. We can use that for composable folds in StreamK
47module Streamly.Internal.Data.Unfold
52 -- * Operations on Input
64 -- * Operations on Output
83 , enumerateFromStepIntegral
84 , enumerateFromToIntegral
85 , enumerateFromIntegral
128import Control.Exception (Exception, mask_)
129import Control.Monad ((>=>))
130import Control.Monad.IO.Class (MonadIO(..))
131import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
132import Data.Void (Void)
133import GHC.Types (SPEC(..))
135 hiding (concat, map, mapM, takeWhile, take, filter, const, zipWith)
137import Fusion.Plugin.Types (Fuse(..))
138import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
139import Streamly.Internal.Data.Unfold.Types (Unfold(..))
140import Streamly.Internal.Data.Fold.Types (Fold(..))
141import Streamly.Internal.Data.SVar (defState, MonadAsync)
142import Control.Monad.Catch (MonadCatch)
144import qualified Prelude
145import qualified Control.Monad.Catch as MC
146import qualified Data.Tuple as Tuple
147import qualified Streamly.Internal.Data.Stream.StreamK as K
148import qualified Streamly.Internal.Data.Stream.StreamD as D
150-------------------------------------------------------------------------------
152-------------------------------------------------------------------------------
154-- | Map a function on the input argument of the 'Unfold'.
157-- lmap f = concat (singleton f)
161{-# INLINE_NORMAL lmap #-}
162lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
163lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
165-- | Map an action on the input argument of the 'Unfold'.
168-- lmapM f = concat (singletonM f)
172{-# INLINE_NORMAL lmapM #-}
173lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
174lmapM f (Unfold ustep uinject) = Unfold ustep (f >=> uinject)
176-- XXX change the signature to the following?
177-- supply :: a -> Unfold m a b -> Unfold m Void b
179-- | Supply the seed to an unfold closing the input end of the unfold.
183{-# INLINE_NORMAL supply #-}
184supply :: Unfold m a b -> a -> Unfold m Void b
185supply unf a = lmap (Prelude.const a) unf
187-- XXX change the signature to the following?
188-- supplyFirst :: a -> Unfold m (a, b) c -> Unfold m b c
190-- | Supply the first component of the tuple to an unfold that accepts a tuple
191-- as a seed resulting in a fold that accepts the second component of the tuple
196{-# INLINE_NORMAL supplyFirst #-}
197supplyFirst :: Unfold m (a, b) c -> a -> Unfold m b c
198supplyFirst unf a = lmap (a, ) unf
200-- XXX change the signature to the following?
201-- supplySecond :: b -> Unfold m (a, b) c -> Unfold m a c
203-- | Supply the second component of the tuple to an unfold that accepts a tuple
204-- as a seed resulting in a fold that accepts the first component of the tuple
209{-# INLINE_NORMAL supplySecond #-}
210supplySecond :: Unfold m (a, b) c -> b -> Unfold m a c
211supplySecond unf b = lmap (, b) unf
213-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
214-- using the argument of the original fold as the second element of tuple and
215-- discarding the first element of the tuple.
219{-# INLINE_NORMAL discardFirst #-}
220discardFirst :: Unfold m a b -> Unfold m (c, a) b
221discardFirst = lmap snd
223-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
224-- using the argument of the original fold as the first element of tuple and
225-- discarding the second element of the tuple.
229{-# INLINE_NORMAL discardSecond #-}
230discardSecond :: Unfold m a b -> Unfold m (a, c) b
231discardSecond = lmap fst
233-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
234-- that accepts a tuple with elements swapped.
238{-# INLINE_NORMAL swap #-}
239swap :: Unfold m (a, c) b -> Unfold m (c, a) b
240swap = lmap Tuple.swap
242-------------------------------------------------------------------------------
244-------------------------------------------------------------------------------
246-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
247-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
248-- application of the fold on the unfolded stream.
252{-# INLINE_NORMAL fold #-}
253fold :: Monad m => Unfold m a b -> Fold m b c -> a -> m c
254fold (Unfold ustep inject) (Fold fstep initial extract) a =
255 initial >>= \x -> inject a >>= go SPEC x
258 {-# INLINE_LATE go #-}
259 go !_ acc st = acc `seq` do
265 Skip s -> go SPEC acc s
268{-# INLINE_NORMAL map #-}
269map :: Monad m => (b -> c) -> Unfold m a b -> Unfold m a c
270map f (Unfold ustep uinject) = Unfold step uinject
272 {-# INLINE_LATE step #-}
276 Yield x s -> Yield (f x) s
280{-# INLINE_NORMAL mapM #-}
281mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
282mapM f (Unfold ustep uinject) = Unfold step uinject
284 {-# INLINE_LATE step #-}
288 Yield x s -> f x >>= \a -> return $ Yield a s
289 Skip s -> return $ Skip s
292{-# INLINE_NORMAL mapMWithInput #-}
293mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
294mapMWithInput f (Unfold ustep uinject) = Unfold step inject
300 {-# INLINE_LATE step #-}
304 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
305 Skip s -> return $ Skip (inp, s)
308-------------------------------------------------------------------------------
309-- Convert streams into unfolds
310-------------------------------------------------------------------------------
312-- this is horribly slow
313{-# INLINE_NORMAL fromStreamD #-}
314fromStreamD :: Monad m => Unfold m (Stream m a) a
315fromStreamD = Unfold step return
318 {-# INLINE_LATE step #-}
319 step (Stream step1 state1) = do
320 r <- step1 defState state1
322 Yield x s -> Yield x (Stream step1 s)
323 Skip s -> Skip (Stream step1 s)
326{-# INLINE_NORMAL fromStreamK #-}
327fromStreamK :: Monad m => Unfold m (K.Stream m a) a
328fromStreamK = Unfold step return
332 {-# INLINE_LATE step #-}
336 Just (x, xs) -> Yield x xs
339-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
340-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
343{-# INLINE_NORMAL fromStream #-}
344fromStream :: (K.IsStream t, Monad m) => Unfold m (t m a) a
345fromStream = lmap K.toStream fromStreamK
347-------------------------------------------------------------------------------
349-------------------------------------------------------------------------------
351-- | Lift a monadic function into an unfold generating a nil stream with a side
355nilM :: Monad m => (a -> m c) -> Unfold m a b
356nilM f = Unfold step return
358 {-# INLINE_LATE step #-}
359 step x = f x >> return Stop
361-- | Prepend a monadic single element generator function to an 'Unfold'.
364{-# INLINE_NORMAL consM #-}
365consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
366consM action unf = Unfold step inject
370 inject = return . Left
372 {-# INLINE_LATE step #-}
374 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
375 step (Right (UnStream step1 st)) = do
376 res <- step1 defState st
378 Yield x s -> return $ Yield x (Right (Stream step1 s))
379 Skip s -> return $ Skip (Right (Stream step1 s))
382-- | Lift a monadic effect into an unfold generating a singleton stream.
385effect :: Monad m => m b -> Unfold m Void b
386effect eff = Unfold step inject
388 inject _ = return True
389 {-# INLINE_LATE step #-}
390 step True = eff >>= \r -> return $ Yield r False
391 step False = return Stop
393-- XXX change it to yieldM or change yieldM in Prelude to singletonM
395-- | Lift a monadic function into an unfold generating a singleton stream.
397{-# INLINE singletonM #-}
398singletonM :: Monad m => (a -> m b) -> Unfold m a b
399singletonM f = Unfold step inject
401 inject x = return $ Just x
402 {-# INLINE_LATE step #-}
403 step (Just x) = f x >>= \r -> return $ Yield r Nothing
404 step Nothing = return Stop
406-- | Lift a pure function into an unfold generating a singleton stream.
408{-# INLINE singleton #-}
409singleton :: Monad m => (a -> b) -> Unfold m a b
410singleton f = singletonM $ return . f
412-- | Identity unfold. Generates a singleton stream with the seed as the only
413-- element in the stream.
415-- > identity = singletonM return
417{-# INLINE identity #-}
418identity :: Monad m => Unfold m a a
419identity = singletonM return
421const :: Monad m => m b -> Unfold m a b
422const m = Unfold step inject
425 step () = m >>= \r -> return $ Yield r ()
427-- | Generates a stream replicating the seed @n@ times.
429{-# INLINE replicateM #-}
430replicateM :: Monad m => Int -> Unfold m a a
431replicateM n = Unfold step inject
433 inject x = return (x, n)
434 {-# INLINE_LATE step #-}
435 step (x, i) = return $
438 else Yield x (x, i - 1)
440-- | Generates an infinite stream repeating the seed.
442{-# INLINE repeatM #-}
443repeatM :: Monad m => Unfold m a a
444repeatM = Unfold step return
446 {-# INLINE_LATE step #-}
447 step x = return $ Yield x x
449-- | Convert a list of pure values to a 'Stream'
450{-# INLINE_LATE fromList #-}
451fromList :: Monad m => Unfold m [a] a
452fromList = Unfold step inject
455 {-# INLINE_LATE step #-}
456 step (x:xs) = return $ Yield x xs
457 step [] = return Stop
459-- | Convert a list of monadic values to a 'Stream'
460{-# INLINE_LATE fromListM #-}
461fromListM :: Monad m => Unfold m [m a] a
462fromListM = Unfold step inject
465 {-# INLINE_LATE step #-}
466 step (x:xs) = x >>= \r -> return $ Yield r xs
467 step [] = return Stop
469-------------------------------------------------------------------------------
471-------------------------------------------------------------------------------
473{-# INLINE_NORMAL take #-}
474take :: Monad m => Int -> Unfold m a b -> Unfold m a b
475take n (Unfold step1 inject1) = Unfold step inject
480 {-# INLINE_LATE step #-}
481 step (st, i) | i < n = do
484 Yield x s -> Yield x (s, i + 1)
485 Skip s -> Skip (s, i)
487 step (_, _) = return Stop
489{-# INLINE_NORMAL takeWhileM #-}
490takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
491takeWhileM f (Unfold step1 inject1) = Unfold step inject1
493 {-# INLINE_LATE step #-}
499 return $ if b then Yield x s else Stop
500 Skip s -> return $ Skip s
503{-# INLINE takeWhile #-}
504takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
505takeWhile f = takeWhileM (return . f)
507{-# INLINE_NORMAL filterM #-}
508filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
509filterM f (Unfold step1 inject1) = Unfold step inject1
511 {-# INLINE_LATE step #-}
517 return $ if b then Yield x s else Skip s
518 Skip s -> return $ Skip s
522filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
523filter f = filterM (return . f)
525-------------------------------------------------------------------------------
527-------------------------------------------------------------------------------
529-- | Can be used to enumerate unbounded integrals. This does not check for
530-- overflow or underflow for bounded integrals.
531{-# INLINE_NORMAL enumerateFromStepIntegral #-}
532enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
533enumerateFromStepIntegral = Unfold step inject
535 inject (from, stride) = from `seq` stride `seq` return (from, stride)
536 {-# INLINE_LATE step #-}
537 step (x, stride) = return $ Yield x $! (x + stride, stride)
539-- We are assuming that "to" is constrained by the type to be within
541{-# INLINE enumerateFromToIntegral #-}
542enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
543enumerateFromToIntegral to =
544 takeWhile (<= to) $ supplySecond enumerateFromStepIntegral 1
546{-# INLINE enumerateFromIntegral #-}
547enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
548enumerateFromIntegral = enumerateFromToIntegral maxBound
550-------------------------------------------------------------------------------
552-------------------------------------------------------------------------------
554{-# INLINE_NORMAL zipWithM #-}
556 => (a -> b -> m c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
557zipWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
564 return (s1, s2, Nothing)
566 {-# INLINE_LATE step #-}
567 step (s1, s2, Nothing) = do
571 Yield x s -> Skip (s, s2, Just x)
572 Skip s -> Skip (s, s2, Nothing)
575 step (s1, s2, Just x) = do
580 return $ Yield z (s1, s, Nothing)
581 Skip s -> return $ Skip (s1, s, Just x)
584-- | Divide the input into two unfolds and then zip the outputs to a single
589-- $ S.concatUnfold (UF.zipWith (,) UF.identity (UF.singleton sqrt))
590-- $ S.map (\x -> (x,x))
591-- $ S.fromList [1..10]
596{-# INLINE zipWith #-}
598 => (a -> b -> c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
599zipWith f = zipWithM (\a b -> return (f a b))
601-- | Distribute the input to two unfolds and then zip the outputs to a single
605-- S.mapM_ print $ S.concatUnfold (UF.teeZipWith (,) UF.identity (UF.singleton sqrt)) $ S.fromList [1..10]
610{-# INLINE_NORMAL teeZipWith #-}
612 => (a -> b -> c) -> Unfold m x a -> Unfold m x b -> Unfold m x c
613teeZipWith f unf1 unf2 = lmap (\x -> (x,x)) $ zipWith f unf1 unf2
615-------------------------------------------------------------------------------
617-------------------------------------------------------------------------------
619{-# ANN type ConcatState Fuse #-}
620data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
622-- | Apply the second unfold to each output element of the first unfold and
623-- flatten the output in a single stream.
627{-# INLINE_NORMAL concat #-}
628concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
629concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
633 return $ ConcatOuter s
635 {-# INLINE_LATE step #-}
636 step (ConcatOuter st) = do
641 return $ Skip (ConcatInner s innerSt)
642 Skip s -> return $ Skip (ConcatOuter s)
645 step (ConcatInner ost ist) = do
648 Yield x s -> Yield x (ConcatInner ost s)
649 Skip s -> Skip (ConcatInner ost s)
650 Stop -> Skip (ConcatOuter ost)
652data OuterProductState s1 s2 sy x y =
653 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
655-- | Create an outer product (vector product or cartesian product) of the
656-- output streams of two unfolds.
658{-# INLINE_NORMAL outerProduct #-}
659outerProduct :: Monad m
660 => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d)
661outerProduct (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
665 return $ OuterProductOuter s1 y
667 {-# INLINE_LATE step #-}
668 step (OuterProductOuter st1 sy) = do
673 return $ Skip (OuterProductInner s sy s2 x)
674 Skip s -> return $ Skip (OuterProductOuter s sy)
677 step (OuterProductInner ost sy ist x) = do
680 Yield y s -> Yield (x, y) (OuterProductInner ost sy s x)
681 Skip s -> Skip (OuterProductInner ost sy s x)
682 Stop -> Skip (OuterProductOuter ost sy)
684-- Special cases of outer product
685-- | Outer product with a function application.
689{-# INLINE_NORMAL ap #-}
691 Unfold m a (b -> c) -> Unfold m d b -> Unfold m (a, d) c
692ap (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined
694-- | Outer product discarding the first element.
698{-# INLINE_NORMAL apDiscardFst #-}
699apDiscardFst :: -- Monad m =>
700 Unfold m a b -> Unfold m c d -> Unfold m (a, c) d
701apDiscardFst (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined
703-- | Outer product discarding the second element.
707{-# INLINE_NORMAL apDiscardSnd #-}
708apDiscardSnd :: -- Monad m =>
709 Unfold m a b -> Unfold m c d -> Unfold m (a, c) b
710apDiscardSnd (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined
712-- XXX This can be used to implement a Monad instance for "Unfold m ()".
714data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
716-- | Map an unfold generating action to each element of an unfold and
717-- flattern the results into a single stream.
719{-# INLINE_NORMAL concatMapM #-}
721 => (b -> m (Unfold m () c)) -> Unfold m a b -> Unfold m a c
722concatMapM f (Unfold step1 inject1) = Unfold step inject
726 return $ ConcatMapOuter s
728 {-# INLINE_LATE step #-}
729 step (ConcatMapOuter st) = do
733 Unfold step2 inject2 <- f x
734 innerSt <- inject2 ()
735 return $ Skip (ConcatMapInner s (Stream (\_ ss -> step2 ss)
737 Skip s -> return $ Skip (ConcatMapOuter s)
740 step (ConcatMapInner ost (UnStream istep ist)) = do
741 r <- istep defState ist
743 Yield x s -> Yield x (ConcatMapInner ost (Stream istep s))
744 Skip s -> Skip (ConcatMapInner ost (Stream istep s))
745 Stop -> Skip (ConcatMapOuter ost)
747------------------------------------------------------------------------------
749------------------------------------------------------------------------------
751-- | The most general bracketing and exception combinator. All other
752-- combinators can be expressed in terms of this combinator. This can also be
753-- used for cases which are not covered by the standard combinators.
757{-# INLINE_NORMAL gbracket_ #-}
760 => (a -> m c) -- ^ before
761 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
762 -> (c -> m d) -- ^ after, on normal stop
763 -> Unfold m (c, e) b -- ^ on exception
764 -> Unfold m c b -- ^ unfold to run
766gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
774 return $ Right (s, r)
776 {-# INLINE_LATE step #-}
777 step (Right (st, v)) = do
778 res <- exc $ step1 st
781 Yield x s -> return $ Yield x (Right (s, v))
782 Skip s -> return $ Skip (Right (s, v))
783 Stop -> aft v >> return Stop
786 return $ Skip (Left r)
790 Yield x s -> return $ Yield x (Left s)
791 Skip s -> return $ Skip (Left s)
794-- | The most general bracketing and exception combinator. All other
795-- combinators can be expressed in terms of this combinator. This can also be
796-- used for cases which are not covered by the standard combinators.
800{-# INLINE_NORMAL gbracket #-}
802 :: (MonadIO m, MonadBaseControl IO m)
803 => (a -> m c) -- ^ before
804 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
805 -> (c -> m d) -- ^ after, on normal stop, or GC
806 -> Unfold m (c, e) b -- ^ on exception
807 -> Unfold m c b -- ^ unfold to run
809gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
815 -- Mask asynchronous exceptions to make the execution of 'bef' and
816 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
817 (r, ref) <- liftBaseOp_ mask_ $ do
819 ref <- D.newFinalizedIORef (aft r)
822 return $ Right (s, r, ref)
824 {-# INLINE_LATE step #-}
825 step (Right (st, v, ref)) = do
826 res <- exc $ step1 st
829 Yield x s -> return $ Yield x (Right (s, v, ref))
830 Skip s -> return $ Skip (Right (s, v, ref))
832 D.runIORefFinalizer ref
835 -- Clearing of finalizer and running of exception handler must
836 -- be atomic wrt async exceptions. Otherwise if we have cleared
837 -- the finalizer and have not run the exception handler then we
838 -- may leak the resource.
839 r <- D.withIORefFinalizer ref (einject (v, e))
840 return $ Skip (Left r)
844 Yield x s -> return $ Yield x (Left s)
845 Skip s -> return $ Skip (Left s)
848-- The custom implementation of "before" is slightly faster (5-7%) than
849-- "_before". This is just to document and make sure that we can always use
850-- gbracket to implement before. The same applies to other combinators as well.
852{-# INLINE_NORMAL _before #-}
853_before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
854_before action = gbracket_ (\x -> action x >> return x) (fmap Right)
855 (\_ -> return ()) undefined
857-- | Run a side effect before the unfold yields its first element.
860{-# INLINE_NORMAL before #-}
861before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
862before action (Unfold step1 inject1) = Unfold step inject
870 {-# INLINE_LATE step #-}
874 Yield x s -> return $ Yield x s
875 Skip s -> return $ Skip s
878{-# INLINE_NORMAL _after #-}
879_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
880_after aft = gbracket_ return (fmap Right) aft undefined
882-- | Run a side effect whenever the unfold stops normally.
884-- Prefer 'after' over this as the @after@ action in this combinator is not
885-- executed if the unfold is partially evaluated lazily and then garbage
889{-# INLINE_NORMAL after_ #-}
890after_ :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
891after_ action (Unfold step1 inject1) = Unfold step inject
899 {-# INLINE_LATE step #-}
903 Yield x s -> return $ Yield x (s, v)
904 Skip s -> return $ Skip (s, v)
905 Stop -> action v >> return Stop
907-- | Run a side effect whenever the unfold stops normally
908-- or is garbage collected after a partial lazy evaluation.
911{-# INLINE_NORMAL after #-}
912after :: (MonadIO m, MonadBaseControl IO m)
913 => (a -> m c) -> Unfold m a b -> Unfold m a b
914after action (Unfold step1 inject1) = Unfold step inject
920 ref <- D.newFinalizedIORef (action x)
923 {-# INLINE_LATE step #-}
927 Yield x s -> return $ Yield x (s, ref)
928 Skip s -> return $ Skip (s, ref)
930 D.runIORefFinalizer ref
933{-# INLINE_NORMAL _onException #-}
934_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
936 gbracket_ return MC.try
938 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
940-- | Run a side effect whenever the unfold aborts due to an exception.
943{-# INLINE_NORMAL onException #-}
944onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
945onException action (Unfold step1 inject1) = Unfold step inject
953 {-# INLINE_LATE step #-}
955 res <- step1 st `MC.onException` action v
957 Yield x s -> return $ Yield x (s, v)
958 Skip s -> return $ Skip (s, v)
961{-# INLINE_NORMAL _finally #-}
962_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
964 gbracket_ return MC.try action
965 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
967-- | Run a side effect whenever the unfold stops normally or aborts due to an
970-- Prefer 'finally' over this as the @after@ action in this combinator is not
971-- executed if the unfold is partially evaluated lazily and then garbage
975{-# INLINE_NORMAL finally_ #-}
976finally_ :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
977finally_ action (Unfold step1 inject1) = Unfold step inject
985 {-# INLINE_LATE step #-}
987 res <- step1 st `MC.onException` action v
989 Yield x s -> return $ Yield x (s, v)
990 Skip s -> return $ Skip (s, v)
991 Stop -> action v >> return Stop
993-- | Run a side effect whenever the unfold stops normally, aborts due to an
994-- exception or if it is garbage collected after a partial lazy evaluation.
997{-# INLINE_NORMAL finally #-}
998finally :: (MonadAsync m, MonadCatch m)
999 => (a -> m c) -> Unfold m a b -> Unfold m a b
1000finally action (Unfold step1 inject1) = Unfold step inject
1006 ref <- D.newFinalizedIORef (action x)
1009 {-# INLINE_LATE step #-}
1011 res <- step1 st `MC.onException` D.runIORefFinalizer ref
1013 Yield x s -> return $ Yield x (s, ref)
1014 Skip s -> return $ Skip (s, ref)
1016 D.runIORefFinalizer ref
1019{-# INLINE_NORMAL _bracket #-}
1020_bracket :: MonadCatch m
1021 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1023 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1026-- | @bracket_ before after between@ runs the @before@ action and then unfolds
1027-- its output using the @between@ unfold. When the @between@ unfold is done or
1028-- if an exception occurs then the @after@ action is run with the output of
1029-- @before@ as argument.
1031-- Prefer 'bracket' over this as the @after@ action in this combinator is not
1032-- executed if the unfold is partially evaluated lazily and then garbage
1036{-# INLINE_NORMAL bracket_ #-}
1037bracket_ :: MonadCatch m
1038 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1039bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
1048 {-# INLINE_LATE step #-}
1050 res <- step1 st `MC.onException` aft v
1052 Yield x s -> return $ Yield x (s, v)
1053 Skip s -> return $ Skip (s, v)
1054 Stop -> aft v >> return Stop
1056-- | @bracket before after between@ runs the @before@ action and then unfolds
1057-- its output using the @between@ unfold. When the @between@ unfold is done or
1058-- if an exception occurs then the @after@ action is run with the output of
1059-- @before@ as argument. The after action is also executed if the unfold is
1060-- paritally evaluated and then garbage collected.
1063{-# INLINE_NORMAL bracket #-}
1064bracket :: (MonadAsync m, MonadCatch m)
1065 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1066bracket bef aft (Unfold step1 inject1) = Unfold step inject
1071 -- Mask asynchronous exceptions to make the execution of 'bef' and
1072 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1073 (r, ref) <- liftBaseOp_ mask_ $ do
1075 ref <- D.newFinalizedIORef (aft r)
1080 {-# INLINE_LATE step #-}
1082 res <- step1 st `MC.onException` D.runIORefFinalizer ref
1084 Yield x s -> return $ Yield x (s, ref)
1085 Skip s -> return $ Skip (s, ref)
1087 D.runIORefFinalizer ref
1090-- | When unfolding if an exception occurs, unfold the exception using the
1091-- exception unfold supplied as the first argument to 'handle'.
1094{-# INLINE_NORMAL handle #-}
1095handle :: (MonadCatch m, Exception e)
1096 => Unfold m e b -> Unfold m a b -> Unfold m a b
1098 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