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 Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
143#if __GLASGOW_HASKELL__ < 800
144import Streamly.Internal.Data.Stream.StreamD.Type (pattern Stream)
146import Streamly.Internal.Data.Unfold.Types (Unfold(..))
147import Streamly.Internal.Data.Fold.Types (Fold(..))
148import Streamly.Internal.Data.SVar (defState, MonadAsync)
149import Control.Monad.Catch (MonadCatch)
151import qualified Prelude
152import qualified Control.Monad.Catch as MC
153import qualified Data.Tuple as Tuple
154import qualified Streamly.Internal.Data.Stream.StreamK as K
155import qualified Streamly.Internal.Data.Stream.StreamD as D
157-------------------------------------------------------------------------------
159-------------------------------------------------------------------------------
161-- | Map a function on the input argument of the 'Unfold'.
164-- lmap f = concat (singleton f)
168{-# INLINE_NORMAL lmap #-}
169lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
170lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
172-- | Map an action on the input argument of the 'Unfold'.
175-- lmapM f = concat (singletonM f)
179{-# INLINE_NORMAL lmapM #-}
180lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
181lmapM f (Unfold ustep uinject) = Unfold ustep (\x -> f x >>= uinject)
183-- XXX change the signature to the following?
184-- supply :: a -> Unfold m a b -> Unfold m Void b
186-- | Supply the seed to an unfold closing the input end of the unfold.
190{-# INLINE_NORMAL supply #-}
191supply :: Unfold m a b -> a -> Unfold m Void b
192supply unf a = lmap (Prelude.const a) unf
194-- XXX change the signature to the following?
195-- supplyFirst :: a -> Unfold m (a, b) c -> Unfold m b c
197-- | Supply the first component of the tuple to an unfold that accepts a tuple
198-- as a seed resulting in a fold that accepts the second component of the tuple
203{-# INLINE_NORMAL supplyFirst #-}
204supplyFirst :: Unfold m (a, b) c -> a -> Unfold m b c
205supplyFirst unf a = lmap (a, ) unf
207-- XXX change the signature to the following?
208-- supplySecond :: b -> Unfold m (a, b) c -> Unfold m a c
210-- | Supply the second component of the tuple to an unfold that accepts a tuple
211-- as a seed resulting in a fold that accepts the first component of the tuple
216{-# INLINE_NORMAL supplySecond #-}
217supplySecond :: Unfold m (a, b) c -> b -> Unfold m a c
218supplySecond unf b = lmap (, b) unf
220-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
221-- using the argument of the original fold as the second element of tuple and
222-- discarding the first element of the tuple.
226{-# INLINE_NORMAL discardFirst #-}
227discardFirst :: Unfold m a b -> Unfold m (c, a) b
228discardFirst = lmap snd
230-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
231-- using the argument of the original fold as the first element of tuple and
232-- discarding the second element of the tuple.
236{-# INLINE_NORMAL discardSecond #-}
237discardSecond :: Unfold m a b -> Unfold m (a, c) b
238discardSecond = lmap fst
240-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
241-- that accepts a tuple with elements swapped.
245{-# INLINE_NORMAL swap #-}
246swap :: Unfold m (a, c) b -> Unfold m (c, a) b
247swap = lmap Tuple.swap
249-------------------------------------------------------------------------------
251-------------------------------------------------------------------------------
253-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
254-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
255-- application of the fold on the unfolded stream.
259{-# INLINE_NORMAL fold #-}
260fold :: Monad m => Unfold m a b -> Fold m b c -> a -> m c
261fold (Unfold ustep inject) (Fold fstep initial extract) a =
262 initial >>= \x -> inject a >>= go SPEC x
265 {-# INLINE_LATE go #-}
266 go !_ acc st = acc `seq` do
272 Skip s -> go SPEC acc s
275{-# INLINE_NORMAL map #-}
276map :: Monad m => (b -> c) -> Unfold m a b -> Unfold m a c
277map f (Unfold ustep uinject) = Unfold step uinject
279 {-# INLINE_LATE step #-}
283 Yield x s -> Yield (f x) s
287{-# INLINE_NORMAL mapM #-}
288mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
289mapM f (Unfold ustep uinject) = Unfold step uinject
291 {-# INLINE_LATE step #-}
295 Yield x s -> f x >>= \a -> return $ Yield a s
296 Skip s -> return $ Skip s
297 Stop -> return $ Stop
299{-# INLINE_NORMAL mapMWithInput #-}
300mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
301mapMWithInput f (Unfold ustep uinject) = Unfold step inject
307 {-# INLINE_LATE step #-}
311 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
312 Skip s -> return $ Skip (inp, s)
313 Stop -> return $ Stop
315-------------------------------------------------------------------------------
316-- Convert streams into unfolds
317-------------------------------------------------------------------------------
319{-# INLINE_LATE streamStep #-}
320streamStep :: Monad m => Stream m a -> m (Step (Stream m a) a)
321streamStep (Stream step1 state) = do
322 r <- step1 defState state
324 Yield x s -> Yield x (Stream step1 s)
325 Skip s -> Skip (Stream step1 s)
328-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
329-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
332fromStream :: (K.IsStream t, Monad m) => t m b -> Unfold m Void b
333fromStream str = Unfold streamStep (\_ -> return $ D.toStreamD str)
335-- | Convert a single argument stream generator function into an
336-- 'Unfold'. Note that a stream converted to an 'Unfold' may not be as
337-- efficient as an 'Unfold' in some situations.
340fromStream1 :: (K.IsStream t, Monad m) => (a -> t m b) -> Unfold m a b
341fromStream1 f = Unfold streamStep (return . D.toStreamD . f)
343-- | Convert a two argument stream generator function into an 'Unfold'. Note
344-- that a stream converted to an 'Unfold' may not be as efficient as an
345-- 'Unfold' in some situations.
348fromStream2 :: (K.IsStream t, Monad m)
349 => (a -> b -> t m c) -> Unfold m (a, b) c
350fromStream2 f = Unfold streamStep (\(a, b) -> return $ D.toStreamD $ f a b)
352-------------------------------------------------------------------------------
354-------------------------------------------------------------------------------
356-- | Lift a monadic function into an unfold generating a nil stream with a side
360nilM :: Monad m => (a -> m c) -> Unfold m a b
361nilM f = Unfold step return
363 {-# INLINE_LATE step #-}
364 step x = f x >> return Stop
366-- | Prepend a monadic single element generator function to an 'Unfold'.
369{-# INLINE_NORMAL consM #-}
370consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
371consM action unf = Unfold step inject
375 inject = return . Left
377 {-# INLINE_LATE step #-}
379 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
380 step (Right (UnStream step1 st)) = do
381 res <- step1 defState st
383 Yield x s -> return $ Yield x (Right (Stream step1 s))
384 Skip s -> return $ Skip (Right (Stream step1 s))
387-- | Lift a monadic effect into an unfold generating a singleton stream.
390effect :: Monad m => m b -> Unfold m Void b
391effect eff = Unfold step inject
393 inject _ = return True
394 {-# INLINE_LATE step #-}
395 step True = eff >>= \r -> return $ Yield r False
396 step False = return Stop
398-- XXX change it to yieldM or change yieldM in Prelude to singletonM
400-- | Lift a monadic function into an unfold generating a singleton stream.
402{-# INLINE singletonM #-}
403singletonM :: Monad m => (a -> m b) -> Unfold m a b
404singletonM f = Unfold step inject
406 inject x = return $ Just x
407 {-# INLINE_LATE step #-}
408 step (Just x) = f x >>= \r -> return $ Yield r Nothing
409 step Nothing = return Stop
411-- | Lift a pure function into an unfold generating a singleton stream.
413{-# INLINE singleton #-}
414singleton :: Monad m => (a -> b) -> Unfold m a b
415singleton f = singletonM $ return . f
417-- | Identity unfold. Generates a singleton stream with the seed as the only
418-- element in the stream.
420-- > identity = singletonM return
422{-# INLINE identity #-}
423identity :: Monad m => Unfold m a a
424identity = singletonM return
426const :: Monad m => m b -> Unfold m a b
427const m = Unfold step inject
430 step () = m >>= \r -> return $ Yield r ()
432-- | Generates a stream replicating the seed @n@ times.
434{-# INLINE replicateM #-}
435replicateM :: Monad m => Int -> Unfold m a a
436replicateM n = Unfold step inject
438 inject x = return (x, n)
439 {-# INLINE_LATE step #-}
440 step (x, i) = return $
443 else Yield x (x, (i - 1))
445-- | Generates an infinite stream repeating the seed.
447{-# INLINE repeatM #-}
448repeatM :: Monad m => Unfold m a a
449repeatM = Unfold step return
451 {-# INLINE_LATE step #-}
452 step x = return $ Yield x x
454-- | Convert a list of pure values to a 'Stream'
455{-# INLINE_LATE fromList #-}
456fromList :: Monad m => Unfold m [a] a
457fromList = Unfold step inject
460 {-# INLINE_LATE step #-}
461 step (x:xs) = return $ Yield x xs
462 step [] = return Stop
464-- | Convert a list of monadic values to a 'Stream'
465{-# INLINE_LATE fromListM #-}
466fromListM :: Monad m => Unfold m [m a] a
467fromListM = Unfold step inject
470 {-# INLINE_LATE step #-}
471 step (x:xs) = x >>= \r -> return $ Yield r xs
472 step [] = return Stop
474-------------------------------------------------------------------------------
476-------------------------------------------------------------------------------
478{-# INLINE_NORMAL take #-}
479take :: Monad m => Int -> Unfold m a b -> Unfold m a b
480take n (Unfold step1 inject1) = Unfold step inject
485 {-# INLINE_LATE step #-}
486 step (st, i) | i < n = do
489 Yield x s -> Yield x (s, i + 1)
490 Skip s -> Skip (s, i)
492 step (_, _) = return Stop
494{-# INLINE_NORMAL takeWhileM #-}
495takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
496takeWhileM f (Unfold step1 inject1) = Unfold step inject1
498 {-# INLINE_LATE step #-}
504 return $ if b then Yield x s else Stop
505 Skip s -> return $ Skip s
508{-# INLINE takeWhile #-}
509takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
510takeWhile f = takeWhileM (return . f)
512{-# INLINE_NORMAL filterM #-}
513filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
514filterM f (Unfold step1 inject1) = Unfold step inject1
516 {-# INLINE_LATE step #-}
522 return $ if b then Yield x s else Skip s
523 Skip s -> return $ Skip s
527filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
528filter f = filterM (return . f)
530-------------------------------------------------------------------------------
532-------------------------------------------------------------------------------
534-- | Can be used to enumerate unbounded integrals. This does not check for
535-- overflow or underflow for bounded integrals.
536{-# INLINE_NORMAL enumerateFromStepIntegral #-}
537enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
538enumerateFromStepIntegral = Unfold step inject
540 inject (from, stride) = from `seq` stride `seq` return (from, stride)
541 {-# INLINE_LATE step #-}
542 step !(x, stride) = return $ Yield x $! (x + stride, stride)
544-- We are assuming that "to" is constrained by the type to be within
546{-# INLINE enumerateFromToIntegral #-}
547enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
548enumerateFromToIntegral to =
549 takeWhile (<= to) $ supplySecond enumerateFromStepIntegral 1
551{-# INLINE enumerateFromIntegral #-}
552enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
553enumerateFromIntegral = enumerateFromToIntegral maxBound
555-------------------------------------------------------------------------------
557-------------------------------------------------------------------------------
559{-# INLINE_NORMAL zipWithM #-}
561 => (a -> b -> m c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
562zipWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
569 return (s1, s2, Nothing)
571 {-# INLINE_LATE step #-}
572 step (s1, s2, Nothing) = do
576 Yield x s -> Skip (s, s2, Just x)
577 Skip s -> Skip (s, s2, Nothing)
580 step (s1, s2, Just x) = do
585 return $ Yield z (s1, s, Nothing)
586 Skip s -> return $ Skip (s1, s, Just x)
589-- | Divide the input into two unfolds and then zip the outputs to a single
594-- $ S.concatUnfold (UF.zipWith (,) UF.identity (UF.singleton sqrt))
595-- $ S.map (\x -> (x,x))
596-- $ S.fromList [1..10]
601{-# INLINE zipWith #-}
603 => (a -> b -> c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
604zipWith f = zipWithM (\a b -> return (f a b))
606-- | Distribute the input to two unfolds and then zip the outputs to a single
610-- S.mapM_ print $ S.concatUnfold (UF.teeZipWith (,) UF.identity (UF.singleton sqrt)) $ S.fromList [1..10]
615{-# INLINE_NORMAL teeZipWith #-}
617 => (a -> b -> c) -> Unfold m x a -> Unfold m x b -> Unfold m x c
618teeZipWith f unf1 unf2 = lmap (\x -> (x,x)) $ zipWith f unf1 unf2
620-------------------------------------------------------------------------------
622-------------------------------------------------------------------------------
624data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
626-- | Apply the second unfold to each output element of the first unfold and
627-- flatten the output in a single stream.
631{-# INLINE_NORMAL concat #-}
632concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
633concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
637 return $ ConcatOuter s
639 {-# INLINE_LATE step #-}
640 step (ConcatOuter st) = do
645 return $ Skip (ConcatInner s innerSt)
646 Skip s -> return $ Skip (ConcatOuter s)
649 step (ConcatInner ost ist) = do
652 Yield x s -> Yield x (ConcatInner ost s)
653 Skip s -> Skip (ConcatInner ost s)
654 Stop -> Skip (ConcatOuter ost)
656data OuterProductState s1 s2 sy x y =
657 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
659-- | Create an outer product (vector product or cartesian product) of the
660-- output streams of two unfolds.
662{-# INLINE_NORMAL outerProduct #-}
663outerProduct :: Monad m
664 => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d)
665outerProduct (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
669 return $ OuterProductOuter s1 y
671 {-# INLINE_LATE step #-}
672 step (OuterProductOuter st1 sy) = do
677 return $ Skip (OuterProductInner s sy s2 x)
678 Skip s -> return $ Skip (OuterProductOuter s sy)
681 step (OuterProductInner ost sy ist x) = do
684 Yield y s -> Yield (x, y) (OuterProductInner ost sy s x)
685 Skip s -> Skip (OuterProductInner ost sy s x)
686 Stop -> Skip (OuterProductOuter ost sy)
688-- XXX This can be used to implement a Monad instance for "Unfold m ()".
690data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
692-- | Map an unfold generating action to each element of an unfold and
693-- flattern the results into a single stream.
695{-# INLINE_NORMAL concatMapM #-}
697 => (b -> m (Unfold m () c)) -> Unfold m a b -> Unfold m a c
698concatMapM f (Unfold step1 inject1) = Unfold step inject
702 return $ ConcatMapOuter s
704 {-# INLINE_LATE step #-}
705 step (ConcatMapOuter st) = do
709 Unfold step2 inject2 <- f x
710 innerSt <- inject2 ()
711 return $ Skip (ConcatMapInner s (Stream (\_ ss -> step2 ss)
713 Skip s -> return $ Skip (ConcatMapOuter s)
716 step (ConcatMapInner ost (UnStream istep ist)) = do
717 r <- istep defState ist
719 Yield x s -> Yield x (ConcatMapInner ost (Stream istep s))
720 Skip s -> Skip (ConcatMapInner ost (Stream istep s))
721 Stop -> Skip (ConcatMapOuter ost)
723------------------------------------------------------------------------------
725------------------------------------------------------------------------------
727-- | The most general bracketing and exception combinator. All other
728-- combinators can be expressed in terms of this combinator. This can also be
729-- used for cases which are not covered by the standard combinators.
733{-# INLINE_NORMAL gbracket #-}
736 => (a -> m c) -- ^ before
737 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
738 -> (c -> m d) -- ^ after, on normal stop
739 -> Unfold m (c, e) b -- ^ on exception
740 -> Unfold m c b -- ^ unfold to run
742gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
750 return $ Right (s, r)
752 {-# INLINE_LATE step #-}
753 step (Right (st, v)) = do
754 res <- exc $ step1 st
757 Yield x s -> return $ Yield x (Right (s, v))
758 Skip s -> return $ Skip (Right (s, v))
759 Stop -> aft v >> return Stop
762 return $ Skip (Left r)
766 Yield x s -> return $ Yield x (Left s)
767 Skip s -> return $ Skip (Left s)
770-- | The most general bracketing and exception combinator. All other
771-- combinators can be expressed in terms of this combinator. This can also be
772-- used for cases which are not covered by the standard combinators.
776{-# INLINE_NORMAL gbracketIO #-}
778 :: (MonadIO m, MonadBaseControl IO m)
779 => (a -> m c) -- ^ before
780 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
781 -> (c -> m d) -- ^ after, on normal stop, or GC
782 -> Unfold m (c, e) b -- ^ on exception
783 -> Unfold m c b -- ^ unfold to run
785gbracketIO bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
791 -- Mask asynchronous exceptions to make the execution of 'bef' and
792 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
793 (r, ref) <- liftBaseOp_ mask_ $ do
795 ref <- D.newFinalizedIORef (aft r)
798 return $ Right (s, r, ref)
800 {-# INLINE_LATE step #-}
801 step (Right (st, v, ref)) = do
802 res <- exc $ step1 st
805 Yield x s -> return $ Yield x (Right (s, v, ref))
806 Skip s -> return $ Skip (Right (s, v, ref))
808 D.runIORefFinalizer ref
811 D.clearIORefFinalizer ref
813 return $ Skip (Left r)
817 Yield x s -> return $ Yield x (Left s)
818 Skip s -> return $ Skip (Left s)
821-- The custom implementation of "before" is slightly faster (5-7%) than
822-- "_before". This is just to document and make sure that we can always use
823-- gbracket to implement before. The same applies to other combinators as well.
825{-# INLINE_NORMAL _before #-}
826_before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
827_before action unf = gbracket (\x -> action x >> return x) (fmap Right)
828 (\_ -> return ()) undefined unf
830-- | Run a side effect before the unfold yields its first element.
833{-# INLINE_NORMAL before #-}
834before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
835before action (Unfold step1 inject1) = Unfold step inject
844 {-# INLINE_LATE step #-}
848 Yield x s -> return $ Yield x s
849 Skip s -> return $ Skip s
852{-# INLINE_NORMAL _after #-}
853_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
854_after aft = gbracket return (fmap Right) aft undefined
856-- | Run a side effect whenever the unfold stops normally.
858-- Prefer afterIO over this as the @after@ action in this combinator is not
859-- executed if the unfold is partially evaluated lazily and then garbage
863{-# INLINE_NORMAL after #-}
864after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
865after action (Unfold step1 inject1) = Unfold step inject
873 {-# INLINE_LATE step #-}
877 Yield x s -> return $ Yield x (s, v)
878 Skip s -> return $ Skip (s, v)
879 Stop -> action v >> return Stop
881-- | Run a side effect whenever the unfold stops normally
882-- or is garbage collected after a partial lazy evaluation.
885{-# INLINE_NORMAL afterIO #-}
886afterIO :: (MonadIO m, MonadBaseControl IO m)
887 => (a -> m c) -> Unfold m a b -> Unfold m a b
888afterIO action (Unfold step1 inject1) = Unfold step inject
894 ref <- D.newFinalizedIORef (action x)
897 {-# INLINE_LATE step #-}
901 Yield x s -> return $ Yield x (s, ref)
902 Skip s -> return $ Skip (s, ref)
904 D.runIORefFinalizer ref
907{-# INLINE_NORMAL _onException #-}
908_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
909_onException action unf =
910 gbracket return MC.try
912 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
914-- | Run a side effect whenever the unfold aborts due to an exception.
917{-# INLINE_NORMAL onException #-}
918onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
919onException action (Unfold step1 inject1) = Unfold step inject
927 {-# INLINE_LATE step #-}
929 res <- step1 st `MC.onException` action v
931 Yield x s -> return $ Yield x (s, v)
932 Skip s -> return $ Skip (s, v)
935{-# INLINE_NORMAL _finally #-}
936_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
938 gbracket return MC.try action
939 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
941-- | Run a side effect whenever the unfold stops normally or aborts due to an
944-- Prefer finallyIO over this as the @after@ action in this combinator is not
945-- executed if the unfold is partially evaluated lazily and then garbage
949{-# INLINE_NORMAL finally #-}
950finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
951finally action (Unfold step1 inject1) = Unfold step inject
959 {-# INLINE_LATE step #-}
961 res <- step1 st `MC.onException` action v
963 Yield x s -> return $ Yield x (s, v)
964 Skip s -> return $ Skip (s, v)
965 Stop -> action v >> return Stop
967-- | Run a side effect whenever the unfold stops normally, aborts due to an
968-- exception or if it is garbage collected after a partial lazy evaluation.
971{-# INLINE_NORMAL finallyIO #-}
972finallyIO :: (MonadAsync m, MonadCatch m)
973 => (a -> m c) -> Unfold m a b -> Unfold m a b
974finallyIO action (Unfold step1 inject1) = Unfold step inject
980 ref <- D.newFinalizedIORef (action x)
983 {-# INLINE_LATE step #-}
985 res <- step1 st `MC.onException` D.runIORefFinalizer ref
987 Yield x s -> return $ Yield x (s, ref)
988 Skip s -> return $ Skip (s, ref)
990 D.runIORefFinalizer ref
993{-# INLINE_NORMAL _bracket #-}
994_bracket :: MonadCatch m
995 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
996_bracket bef aft unf =
997 gbracket bef MC.try aft (nilM (\(a, (e :: MC.SomeException)) -> aft a >>
1000-- | @bracket before after between@ runs the @before@ action and then unfolds
1001-- its output using the @between@ unfold. When the @between@ unfold is done or
1002-- if an exception occurs then the @after@ action is run with the output of
1003-- @before@ as argument.
1005-- Prefer bracketIO over this as the @after@ action in this combinator is not
1006-- executed if the unfold is partially evaluated lazily and then garbage
1010{-# INLINE_NORMAL bracket #-}
1011bracket :: MonadCatch m
1012 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1013bracket bef aft (Unfold step1 inject1) = Unfold step inject
1022 {-# INLINE_LATE step #-}
1024 res <- step1 st `MC.onException` aft v
1026 Yield x s -> return $ Yield x (s, v)
1027 Skip s -> return $ Skip (s, v)
1028 Stop -> aft v >> return Stop
1030-- | @bracket before after between@ runs the @before@ action and then unfolds
1031-- its output using the @between@ unfold. When the @between@ unfold is done or
1032-- if an exception occurs then the @after@ action is run with the output of
1033-- @before@ as argument. The after action is also executed if the unfold is
1034-- paritally evaluated and then garbage collected.
1037{-# INLINE_NORMAL bracketIO #-}
1038bracketIO :: (MonadAsync m, MonadCatch m)
1039 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1040bracketIO bef aft (Unfold step1 inject1) = Unfold step inject
1045 -- Mask asynchronous exceptions to make the execution of 'bef' and
1046 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1047 (r, ref) <- liftBaseOp_ mask_ $ do
1049 ref <- D.newFinalizedIORef (aft r)
1054 {-# INLINE_LATE step #-}
1056 res <- step1 st `MC.onException` D.runIORefFinalizer ref
1058 Yield x s -> return $ Yield x (s, ref)
1059 Skip s -> return $ Skip (s, ref)
1061 D.runIORefFinalizer ref
1064-- | When unfolding if an exception occurs, unfold the exception using the
1065-- exception unfold supplied as the first argument to 'handle'.
1068{-# INLINE_NORMAL handle #-}
1069handle :: (MonadCatch m, Exception e)
1070 => Unfold m e b -> Unfold m a b -> Unfold m a b
1072 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