1{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE ExistentialQuantification #-}
4{-# LANGUAGE FlexibleContexts #-}
5{-# LANGUAGE PatternSynonyms #-}
6{-# LANGUAGE RankNTypes #-}
7{-# LANGUAGE RecordWildCards #-}
8{-# LANGUAGE ScopedTypeVariables #-}
9{-# LANGUAGE TupleSections #-}
14-- Module : Streamly.Internal.Data.Unfold
15-- Copyright : (c) 2019 Composewell Technologies
17-- Maintainer : streamly@composewell.com
18-- Stability : experimental
21-- Streams forcing a closed control flow loop can be categorized under
22-- two types, unfolds and folds, both of these are duals of each other.
24-- Unfold streams are really generators of a sequence of elements, we can also
25-- call them pull style streams. These are lazy producers of streams. On each
26-- evaluation the producer generates the next element. A consumer can
27-- therefore pull elements from the stream whenever it wants to. A stream
28-- consumer can multiplex pull streams by pulling elements from the chosen
29-- streams, therefore, pull streams allow merging or multiplexing. On the
30-- other hand, with this representation we cannot split or demultiplex a
31-- stream. So really these are stream sources that can be generated from a
32-- seed and can be merged or zipped into a single stream.
34-- The dual of Unfolds are Folds. Folds can also be called as push style
35-- streams or reducers. These are strict consumers of streams. We keep pushing
36-- elements to a fold and we can extract the result at any point. A driver can
37-- choose which fold to push to and can also push the same element to multiple
38-- folds. Therefore, folds allow splitting or demultiplexing a stream. On the
39-- other hand, we cannot merge streams using this representation. So really
40-- these are stream consumers that reduce the stream to a single value, these
41-- consumers can be composed such that a stream can be split over multiple
46-- Composing a tree or graph of computations with unfolds can be much more
47-- efficient compared to composing with the Monad instance. The reason is that
48-- unfolds allow the compiler to statically know the state and optimize it
49-- using stream fusion whereas it is not possible with the monad bind because
50-- the state is determined dynamically.
52-- Open control flow style streams can also have two representations. StreamK
53-- is a producer style representation. We can also have a consumer style
54-- representation. We can use that for composable folds in StreamK
57module Streamly.Internal.Data.Unfold
62 -- * Operations on Input
74 -- * Operations on Output
93 , enumerateFromStepIntegral
94 , enumerateFromToIntegral
95 , enumerateFromIntegral
134import Control.Exception (Exception, mask_)
135import Control.Monad.IO.Class (MonadIO(..))
136import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
137import Data.Void (Void)
138import GHC.Types (SPEC(..))
140 hiding (concat, map, mapM, takeWhile, take, filter, const, zipWith)
142import Fusion.Plugin.Types (Fuse(..))
143import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
144#if __GLASGOW_HASKELL__ < 800
145import Streamly.Internal.Data.Stream.StreamD.Type (pattern Stream)
147import Streamly.Internal.Data.Unfold.Types (Unfold(..))
148import Streamly.Internal.Data.Fold.Types (Fold(..))
149import Streamly.Internal.Data.SVar (defState, MonadAsync)
150import Control.Monad.Catch (MonadCatch)
152import qualified Prelude
153import qualified Control.Monad.Catch as MC
154import qualified Data.Tuple as Tuple
155import qualified Streamly.Internal.Data.Stream.StreamK as K
156import qualified Streamly.Internal.Data.Stream.StreamD as D
158-------------------------------------------------------------------------------
160-------------------------------------------------------------------------------
162-- | Map a function on the input argument of the 'Unfold'.
165-- lmap f = concat (singleton f)
169{-# INLINE_NORMAL lmap #-}
170lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
171lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
173-- | Map an action on the input argument of the 'Unfold'.
176-- lmapM f = concat (singletonM f)
180{-# INLINE_NORMAL lmapM #-}
181lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
182lmapM f (Unfold ustep uinject) = Unfold ustep (\x -> f x >>= uinject)
184-- XXX change the signature to the following?
185-- supply :: a -> Unfold m a b -> Unfold m Void b
187-- | Supply the seed to an unfold closing the input end of the unfold.
191{-# INLINE_NORMAL supply #-}
192supply :: Unfold m a b -> a -> Unfold m Void b
193supply unf a = lmap (Prelude.const a) unf
195-- XXX change the signature to the following?
196-- supplyFirst :: a -> Unfold m (a, b) c -> Unfold m b c
198-- | Supply the first component of the tuple to an unfold that accepts a tuple
199-- as a seed resulting in a fold that accepts the second component of the tuple
204{-# INLINE_NORMAL supplyFirst #-}
205supplyFirst :: Unfold m (a, b) c -> a -> Unfold m b c
206supplyFirst unf a = lmap (a, ) unf
208-- XXX change the signature to the following?
209-- supplySecond :: b -> Unfold m (a, b) c -> Unfold m a c
211-- | Supply the second component of the tuple to an unfold that accepts a tuple
212-- as a seed resulting in a fold that accepts the first component of the tuple
217{-# INLINE_NORMAL supplySecond #-}
218supplySecond :: Unfold m (a, b) c -> b -> Unfold m a c
219supplySecond unf b = lmap (, b) unf
221-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
222-- using the argument of the original fold as the second element of tuple and
223-- discarding the first element of the tuple.
227{-# INLINE_NORMAL discardFirst #-}
228discardFirst :: Unfold m a b -> Unfold m (c, a) b
229discardFirst = lmap snd
231-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
232-- using the argument of the original fold as the first element of tuple and
233-- discarding the second element of the tuple.
237{-# INLINE_NORMAL discardSecond #-}
238discardSecond :: Unfold m a b -> Unfold m (a, c) b
239discardSecond = lmap fst
241-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
242-- that accepts a tuple with elements swapped.
246{-# INLINE_NORMAL swap #-}
247swap :: Unfold m (a, c) b -> Unfold m (c, a) b
248swap = lmap Tuple.swap
250-------------------------------------------------------------------------------
252-------------------------------------------------------------------------------
254-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
255-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
256-- application of the fold on the unfolded stream.
260{-# INLINE_NORMAL fold #-}
261fold :: Monad m => Unfold m a b -> Fold m b c -> a -> m c
262fold (Unfold ustep inject) (Fold fstep initial extract) a =
263 initial >>= \x -> inject a >>= go SPEC x
266 {-# INLINE_LATE go #-}
267 go !_ acc st = acc `seq` do
273 Skip s -> go SPEC acc s
276{-# INLINE_NORMAL map #-}
277map :: Monad m => (b -> c) -> Unfold m a b -> Unfold m a c
278map f (Unfold ustep uinject) = Unfold step uinject
280 {-# INLINE_LATE step #-}
284 Yield x s -> Yield (f x) s
288{-# INLINE_NORMAL mapM #-}
289mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
290mapM f (Unfold ustep uinject) = Unfold step uinject
292 {-# INLINE_LATE step #-}
296 Yield x s -> f x >>= \a -> return $ Yield a s
297 Skip s -> return $ Skip s
298 Stop -> return $ Stop
300{-# INLINE_NORMAL mapMWithInput #-}
301mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
302mapMWithInput f (Unfold ustep uinject) = Unfold step inject
308 {-# INLINE_LATE step #-}
312 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
313 Skip s -> return $ Skip (inp, s)
314 Stop -> return $ Stop
316-------------------------------------------------------------------------------
317-- Convert streams into unfolds
318-------------------------------------------------------------------------------
320-- this is horribly slow
321{-# INLINE_NORMAL fromStreamD #-}
322fromStreamD :: Monad m => Unfold m (Stream m a) a
323fromStreamD = Unfold step return
326 {-# INLINE_LATE step #-}
327 step (Stream step1 state1) = do
328 r <- step1 defState state1
330 Yield x s -> Yield x (Stream step1 s)
331 Skip s -> Skip (Stream step1 s)
334{-# INLINE_NORMAL fromStreamK #-}
335fromStreamK :: Monad m => Unfold m (K.Stream m a) a
336fromStreamK = Unfold step return
340 {-# INLINE_LATE step #-}
344 Just (x, xs) -> Yield x xs
347-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
348-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
351{-# INLINE_NORMAL fromStream #-}
352fromStream :: (K.IsStream t, Monad m) => Unfold m (t m a) a
353fromStream = lmap K.toStream fromStreamK
355-------------------------------------------------------------------------------
357-------------------------------------------------------------------------------
359-- | Lift a monadic function into an unfold generating a nil stream with a side
363nilM :: Monad m => (a -> m c) -> Unfold m a b
364nilM f = Unfold step return
366 {-# INLINE_LATE step #-}
367 step x = f x >> return Stop
369-- | Prepend a monadic single element generator function to an 'Unfold'.
372{-# INLINE_NORMAL consM #-}
373consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
374consM action unf = Unfold step inject
378 inject = return . Left
380 {-# INLINE_LATE step #-}
382 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
383 step (Right (UnStream step1 st)) = do
384 res <- step1 defState st
386 Yield x s -> return $ Yield x (Right (Stream step1 s))
387 Skip s -> return $ Skip (Right (Stream step1 s))
390-- | Lift a monadic effect into an unfold generating a singleton stream.
393effect :: Monad m => m b -> Unfold m Void b
394effect eff = Unfold step inject
396 inject _ = return True
397 {-# INLINE_LATE step #-}
398 step True = eff >>= \r -> return $ Yield r False
399 step False = return Stop
401-- XXX change it to yieldM or change yieldM in Prelude to singletonM
403-- | Lift a monadic function into an unfold generating a singleton stream.
405{-# INLINE singletonM #-}
406singletonM :: Monad m => (a -> m b) -> Unfold m a b
407singletonM f = Unfold step inject
409 inject x = return $ Just x
410 {-# INLINE_LATE step #-}
411 step (Just x) = f x >>= \r -> return $ Yield r Nothing
412 step Nothing = return Stop
414-- | Lift a pure function into an unfold generating a singleton stream.
416{-# INLINE singleton #-}
417singleton :: Monad m => (a -> b) -> Unfold m a b
418singleton f = singletonM $ return . f
420-- | Identity unfold. Generates a singleton stream with the seed as the only
421-- element in the stream.
423-- > identity = singletonM return
425{-# INLINE identity #-}
426identity :: Monad m => Unfold m a a
427identity = singletonM return
429const :: Monad m => m b -> Unfold m a b
430const m = Unfold step inject
433 step () = m >>= \r -> return $ Yield r ()
435-- | Generates a stream replicating the seed @n@ times.
437{-# INLINE replicateM #-}
438replicateM :: Monad m => Int -> Unfold m a a
439replicateM n = Unfold step inject
441 inject x = return (x, n)
442 {-# INLINE_LATE step #-}
443 step (x, i) = return $
446 else Yield x (x, (i - 1))
448-- | Generates an infinite stream repeating the seed.
450{-# INLINE repeatM #-}
451repeatM :: Monad m => Unfold m a a
452repeatM = Unfold step return
454 {-# INLINE_LATE step #-}
455 step x = return $ Yield x x
457-- | Convert a list of pure values to a 'Stream'
458{-# INLINE_LATE fromList #-}
459fromList :: Monad m => Unfold m [a] a
460fromList = Unfold step inject
463 {-# INLINE_LATE step #-}
464 step (x:xs) = return $ Yield x xs
465 step [] = return Stop
467-- | Convert a list of monadic values to a 'Stream'
468{-# INLINE_LATE fromListM #-}
469fromListM :: Monad m => Unfold m [m a] a
470fromListM = Unfold step inject
473 {-# INLINE_LATE step #-}
474 step (x:xs) = x >>= \r -> return $ Yield r xs
475 step [] = return Stop
477-------------------------------------------------------------------------------
479-------------------------------------------------------------------------------
481{-# INLINE_NORMAL take #-}
482take :: Monad m => Int -> Unfold m a b -> Unfold m a b
483take n (Unfold step1 inject1) = Unfold step inject
488 {-# INLINE_LATE step #-}
489 step (st, i) | i < n = do
492 Yield x s -> Yield x (s, i + 1)
493 Skip s -> Skip (s, i)
495 step (_, _) = return Stop
497{-# INLINE_NORMAL takeWhileM #-}
498takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
499takeWhileM f (Unfold step1 inject1) = Unfold step inject1
501 {-# INLINE_LATE step #-}
507 return $ if b then Yield x s else Stop
508 Skip s -> return $ Skip s
511{-# INLINE takeWhile #-}
512takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
513takeWhile f = takeWhileM (return . f)
515{-# INLINE_NORMAL filterM #-}
516filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
517filterM f (Unfold step1 inject1) = Unfold step inject1
519 {-# INLINE_LATE step #-}
525 return $ if b then Yield x s else Skip s
526 Skip s -> return $ Skip s
530filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
531filter f = filterM (return . f)
533-------------------------------------------------------------------------------
535-------------------------------------------------------------------------------
537-- | Can be used to enumerate unbounded integrals. This does not check for
538-- overflow or underflow for bounded integrals.
539{-# INLINE_NORMAL enumerateFromStepIntegral #-}
540enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
541enumerateFromStepIntegral = Unfold step inject
543 inject (from, stride) = from `seq` stride `seq` return (from, stride)
544 {-# INLINE_LATE step #-}
545 step !(x, stride) = return $ Yield x $! (x + stride, stride)
547-- We are assuming that "to" is constrained by the type to be within
549{-# INLINE enumerateFromToIntegral #-}
550enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
551enumerateFromToIntegral to =
552 takeWhile (<= to) $ supplySecond enumerateFromStepIntegral 1
554{-# INLINE enumerateFromIntegral #-}
555enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
556enumerateFromIntegral = enumerateFromToIntegral maxBound
558-------------------------------------------------------------------------------
560-------------------------------------------------------------------------------
562{-# INLINE_NORMAL zipWithM #-}
564 => (a -> b -> m c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
565zipWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
572 return (s1, s2, Nothing)
574 {-# INLINE_LATE step #-}
575 step (s1, s2, Nothing) = do
579 Yield x s -> Skip (s, s2, Just x)
580 Skip s -> Skip (s, s2, Nothing)
583 step (s1, s2, Just x) = do
588 return $ Yield z (s1, s, Nothing)
589 Skip s -> return $ Skip (s1, s, Just x)
592-- | Divide the input into two unfolds and then zip the outputs to a single
597-- $ S.concatUnfold (UF.zipWith (,) UF.identity (UF.singleton sqrt))
598-- $ S.map (\x -> (x,x))
599-- $ S.fromList [1..10]
604{-# INLINE zipWith #-}
606 => (a -> b -> c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
607zipWith f = zipWithM (\a b -> return (f a b))
609-- | Distribute the input to two unfolds and then zip the outputs to a single
613-- S.mapM_ print $ S.concatUnfold (UF.teeZipWith (,) UF.identity (UF.singleton sqrt)) $ S.fromList [1..10]
618{-# INLINE_NORMAL teeZipWith #-}
620 => (a -> b -> c) -> Unfold m x a -> Unfold m x b -> Unfold m x c
621teeZipWith f unf1 unf2 = lmap (\x -> (x,x)) $ zipWith f unf1 unf2
623-------------------------------------------------------------------------------
625-------------------------------------------------------------------------------
627{-# ANN type ConcatState Fuse #-}
628data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
630-- | Apply the second unfold to each output element of the first unfold and
631-- flatten the output in a single stream.
635{-# INLINE_NORMAL concat #-}
636concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
637concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
641 return $ ConcatOuter s
643 {-# INLINE_LATE step #-}
644 step (ConcatOuter st) = do
649 return $ Skip (ConcatInner s innerSt)
650 Skip s -> return $ Skip (ConcatOuter s)
653 step (ConcatInner ost ist) = do
656 Yield x s -> Yield x (ConcatInner ost s)
657 Skip s -> Skip (ConcatInner ost s)
658 Stop -> Skip (ConcatOuter ost)
660data OuterProductState s1 s2 sy x y =
661 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
663-- | Create an outer product (vector product or cartesian product) of the
664-- output streams of two unfolds.
666{-# INLINE_NORMAL outerProduct #-}
667outerProduct :: Monad m
668 => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d)
669outerProduct (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
673 return $ OuterProductOuter s1 y
675 {-# INLINE_LATE step #-}
676 step (OuterProductOuter st1 sy) = do
681 return $ Skip (OuterProductInner s sy s2 x)
682 Skip s -> return $ Skip (OuterProductOuter s sy)
685 step (OuterProductInner ost sy ist x) = do
688 Yield y s -> Yield (x, y) (OuterProductInner ost sy s x)
689 Skip s -> Skip (OuterProductInner ost sy s x)
690 Stop -> Skip (OuterProductOuter ost sy)
692-- XXX This can be used to implement a Monad instance for "Unfold m ()".
694data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
696-- | Map an unfold generating action to each element of an unfold and
697-- flattern the results into a single stream.
699{-# INLINE_NORMAL concatMapM #-}
701 => (b -> m (Unfold m () c)) -> Unfold m a b -> Unfold m a c
702concatMapM f (Unfold step1 inject1) = Unfold step inject
706 return $ ConcatMapOuter s
708 {-# INLINE_LATE step #-}
709 step (ConcatMapOuter st) = do
713 Unfold step2 inject2 <- f x
714 innerSt <- inject2 ()
715 return $ Skip (ConcatMapInner s (Stream (\_ ss -> step2 ss)
717 Skip s -> return $ Skip (ConcatMapOuter s)
720 step (ConcatMapInner ost (UnStream istep ist)) = do
721 r <- istep defState ist
723 Yield x s -> Yield x (ConcatMapInner ost (Stream istep s))
724 Skip s -> Skip (ConcatMapInner ost (Stream istep s))
725 Stop -> Skip (ConcatMapOuter ost)
727------------------------------------------------------------------------------
729------------------------------------------------------------------------------
731-- | The most general bracketing and exception combinator. All other
732-- combinators can be expressed in terms of this combinator. This can also be
733-- used for cases which are not covered by the standard combinators.
737{-# INLINE_NORMAL gbracket #-}
740 => (a -> m c) -- ^ before
741 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
742 -> (c -> m d) -- ^ after, on normal stop
743 -> Unfold m (c, e) b -- ^ on exception
744 -> Unfold m c b -- ^ unfold to run
746gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
754 return $ Right (s, r)
756 {-# INLINE_LATE step #-}
757 step (Right (st, v)) = do
758 res <- exc $ step1 st
761 Yield x s -> return $ Yield x (Right (s, v))
762 Skip s -> return $ Skip (Right (s, v))
763 Stop -> aft v >> return Stop
766 return $ Skip (Left r)
770 Yield x s -> return $ Yield x (Left s)
771 Skip s -> return $ Skip (Left s)
774-- | The most general bracketing and exception combinator. All other
775-- combinators can be expressed in terms of this combinator. This can also be
776-- used for cases which are not covered by the standard combinators.
780{-# INLINE_NORMAL gbracketIO #-}
782 :: (MonadIO m, MonadBaseControl IO m)
783 => (a -> m c) -- ^ before
784 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
785 -> (c -> m d) -- ^ after, on normal stop, or GC
786 -> Unfold m (c, e) b -- ^ on exception
787 -> Unfold m c b -- ^ unfold to run
789gbracketIO bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
795 -- Mask asynchronous exceptions to make the execution of 'bef' and
796 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
797 (r, ref) <- liftBaseOp_ mask_ $ do
799 ref <- D.newFinalizedIORef (aft r)
802 return $ Right (s, r, ref)
804 {-# INLINE_LATE step #-}
805 step (Right (st, v, ref)) = do
806 res <- exc $ step1 st
809 Yield x s -> return $ Yield x (Right (s, v, ref))
810 Skip s -> return $ Skip (Right (s, v, ref))
812 D.runIORefFinalizer ref
815 D.clearIORefFinalizer ref
817 return $ Skip (Left r)
821 Yield x s -> return $ Yield x (Left s)
822 Skip s -> return $ Skip (Left s)
825-- The custom implementation of "before" is slightly faster (5-7%) than
826-- "_before". This is just to document and make sure that we can always use
827-- gbracket to implement before. The same applies to other combinators as well.
829{-# INLINE_NORMAL _before #-}
830_before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
831_before action unf = gbracket (\x -> action x >> return x) (fmap Right)
832 (\_ -> return ()) undefined unf
834-- | Run a side effect before the unfold yields its first element.
837{-# INLINE_NORMAL before #-}
838before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
839before action (Unfold step1 inject1) = Unfold step inject
848 {-# INLINE_LATE step #-}
852 Yield x s -> return $ Yield x s
853 Skip s -> return $ Skip s
856{-# INLINE_NORMAL _after #-}
857_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
858_after aft = gbracket return (fmap Right) aft undefined
860-- | Run a side effect whenever the unfold stops normally.
862-- Prefer afterIO over this as the @after@ action in this combinator is not
863-- executed if the unfold is partially evaluated lazily and then garbage
867{-# INLINE_NORMAL after #-}
868after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
869after action (Unfold step1 inject1) = Unfold step inject
877 {-# INLINE_LATE step #-}
881 Yield x s -> return $ Yield x (s, v)
882 Skip s -> return $ Skip (s, v)
883 Stop -> action v >> return Stop
885-- | Run a side effect whenever the unfold stops normally
886-- or is garbage collected after a partial lazy evaluation.
889{-# INLINE_NORMAL afterIO #-}
890afterIO :: (MonadIO m, MonadBaseControl IO m)
891 => (a -> m c) -> Unfold m a b -> Unfold m a b
892afterIO action (Unfold step1 inject1) = Unfold step inject
898 ref <- D.newFinalizedIORef (action x)
901 {-# INLINE_LATE step #-}
905 Yield x s -> return $ Yield x (s, ref)
906 Skip s -> return $ Skip (s, ref)
908 D.runIORefFinalizer ref
911{-# INLINE_NORMAL _onException #-}
912_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
913_onException action unf =
914 gbracket return MC.try
916 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
918-- | Run a side effect whenever the unfold aborts due to an exception.
921{-# INLINE_NORMAL onException #-}
922onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
923onException action (Unfold step1 inject1) = Unfold step inject
931 {-# INLINE_LATE step #-}
933 res <- step1 st `MC.onException` action v
935 Yield x s -> return $ Yield x (s, v)
936 Skip s -> return $ Skip (s, v)
939{-# INLINE_NORMAL _finally #-}
940_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
942 gbracket return MC.try action
943 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
945-- | Run a side effect whenever the unfold stops normally or aborts due to an
948-- Prefer finallyIO over this as the @after@ action in this combinator is not
949-- executed if the unfold is partially evaluated lazily and then garbage
953{-# INLINE_NORMAL finally #-}
954finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
955finally 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)
969 Stop -> action v >> return Stop
971-- | Run a side effect whenever the unfold stops normally, aborts due to an
972-- exception or if it is garbage collected after a partial lazy evaluation.
975{-# INLINE_NORMAL finallyIO #-}
976finallyIO :: (MonadAsync m, MonadCatch m)
977 => (a -> m c) -> Unfold m a b -> Unfold m a b
978finallyIO action (Unfold step1 inject1) = Unfold step inject
984 ref <- D.newFinalizedIORef (action x)
987 {-# INLINE_LATE step #-}
989 res <- step1 st `MC.onException` D.runIORefFinalizer ref
991 Yield x s -> return $ Yield x (s, ref)
992 Skip s -> return $ Skip (s, ref)
994 D.runIORefFinalizer ref
997{-# INLINE_NORMAL _bracket #-}
998_bracket :: MonadCatch m
999 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1000_bracket bef aft unf =
1001 gbracket bef MC.try aft (nilM (\(a, (e :: MC.SomeException)) -> aft a >>
1004-- | @bracket before after between@ runs the @before@ action and then unfolds
1005-- its output using the @between@ unfold. When the @between@ unfold is done or
1006-- if an exception occurs then the @after@ action is run with the output of
1007-- @before@ as argument.
1009-- Prefer bracketIO over this as the @after@ action in this combinator is not
1010-- executed if the unfold is partially evaluated lazily and then garbage
1014{-# INLINE_NORMAL bracket #-}
1015bracket :: MonadCatch m
1016 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1017bracket bef aft (Unfold step1 inject1) = Unfold step inject
1026 {-# INLINE_LATE step #-}
1028 res <- step1 st `MC.onException` aft v
1030 Yield x s -> return $ Yield x (s, v)
1031 Skip s -> return $ Skip (s, v)
1032 Stop -> aft v >> return Stop
1034-- | @bracket before after between@ runs the @before@ action and then unfolds
1035-- its output using the @between@ unfold. When the @between@ unfold is done or
1036-- if an exception occurs then the @after@ action is run with the output of
1037-- @before@ as argument. The after action is also executed if the unfold is
1038-- paritally evaluated and then garbage collected.
1041{-# INLINE_NORMAL bracketIO #-}
1042bracketIO :: (MonadAsync m, MonadCatch m)
1043 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1044bracketIO bef aft (Unfold step1 inject1) = Unfold step inject
1049 -- Mask asynchronous exceptions to make the execution of 'bef' and
1050 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1051 (r, ref) <- liftBaseOp_ mask_ $ do
1053 ref <- D.newFinalizedIORef (aft r)
1058 {-# INLINE_LATE step #-}
1060 res <- step1 st `MC.onException` D.runIORefFinalizer ref
1062 Yield x s -> return $ Yield x (s, ref)
1063 Skip s -> return $ Skip (s, ref)
1065 D.runIORefFinalizer ref
1068-- | When unfolding if an exception occurs, unfold the exception using the
1069-- exception unfold supplied as the first argument to 'handle'.
1072{-# INLINE_NORMAL handle #-}
1073handle :: (MonadCatch m, Exception e)
1074 => Unfold m e b -> Unfold m a b -> Unfold m a b
1076 gbracket return MC.try (\_ -> return ()) (discardFirst exc) unf
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