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
92 , enumerateFromStepIntegral
93 , enumerateFromToIntegral
94 , enumerateFromIntegral
128import Control.Exception (Exception)
129import Control.Monad.IO.Class (MonadIO(..))
130import Control.Monad.Trans.Control (MonadBaseControl)
131import Data.Void (Void)
132import GHC.Types (SPEC(..))
133import Prelude hiding (concat, map, mapM, takeWhile, take, filter, const)
135import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
136#if __GLASGOW_HASKELL__ < 800
137import Streamly.Internal.Data.Stream.StreamD.Type (pattern Stream)
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{-# INLINE_NORMAL lmap #-}
158lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
159lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f)
161-- | Map an action on the input argument of the 'Unfold'.
164{-# INLINE_NORMAL lmapM #-}
165lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
166lmapM f (Unfold ustep uinject) = Unfold ustep (\x -> f x >>= uinject)
168-- | Supply the seed to an unfold closing the input end of the unfold.
172{-# INLINE_NORMAL supply #-}
173supply :: Unfold m a b -> a -> Unfold m Void b
174supply unf a = lmap (Prelude.const a) unf
176-- | Supply the first component of the tuple to an unfold that accepts a tuple
177-- as a seed resulting in a fold that accepts the second component of the tuple
182{-# INLINE_NORMAL supplyFirst #-}
183supplyFirst :: Unfold m (a, b) c -> a -> Unfold m b c
184supplyFirst unf a = lmap (a, ) unf
186-- | Supply the second component of the tuple to an unfold that accepts a tuple
187-- as a seed resulting in a fold that accepts the first component of the tuple
192{-# INLINE_NORMAL supplySecond #-}
193supplySecond :: Unfold m (a, b) c -> b -> Unfold m a c
194supplySecond unf b = lmap (, b) unf
196-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
197-- using the argument of the original fold as the second element of tuple and
198-- discarding the first element of the tuple.
202{-# INLINE_NORMAL discardFirst #-}
203discardFirst :: Unfold m a b -> Unfold m (c, a) b
204discardFirst = lmap snd
206-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
207-- using the argument of the original fold as the first element of tuple and
208-- discarding the second element of the tuple.
212{-# INLINE_NORMAL discardSecond #-}
213discardSecond :: Unfold m a b -> Unfold m (a, c) b
214discardSecond = lmap fst
216-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
217-- that accepts a tuple with elements swapped.
221{-# INLINE_NORMAL swap #-}
222swap :: Unfold m (a, c) b -> Unfold m (c, a) b
223swap = lmap Tuple.swap
225-------------------------------------------------------------------------------
227-------------------------------------------------------------------------------
229-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
230-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
231-- application of the fold on the unfolded stream.
235{-# INLINE_NORMAL fold #-}
236fold :: Monad m => Unfold m a b -> Fold m b c -> a -> m c
237fold (Unfold ustep inject) (Fold fstep initial extract) a =
238 initial >>= \x -> inject a >>= go SPEC x
241 {-# INLINE_LATE go #-}
242 go !_ acc st = acc `seq` do
248 Skip s -> go SPEC acc s
251{-# INLINE_NORMAL map #-}
252map :: Monad m => (b -> c) -> Unfold m a b -> Unfold m a c
253map f (Unfold ustep uinject) = Unfold step uinject
255 {-# INLINE_LATE step #-}
259 Yield x s -> Yield (f x) s
263{-# INLINE_NORMAL mapM #-}
264mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
265mapM f (Unfold ustep uinject) = Unfold step uinject
267 {-# INLINE_LATE step #-}
271 Yield x s -> f x >>= \a -> return $ Yield a s
272 Skip s -> return $ Skip s
273 Stop -> return $ Stop
275{-# INLINE_NORMAL mapMWithInput #-}
276mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
277mapMWithInput f (Unfold ustep uinject) = Unfold step inject
283 {-# INLINE_LATE step #-}
287 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
288 Skip s -> return $ Skip (inp, s)
289 Stop -> return $ Stop
291-------------------------------------------------------------------------------
292-- Convert streams into unfolds
293-------------------------------------------------------------------------------
295{-# INLINE_LATE streamStep #-}
296streamStep :: Monad m => Stream m a -> m (Step (Stream m a) a)
297streamStep (Stream step1 state) = do
298 r <- step1 defState state
300 Yield x s -> Yield x (Stream step1 s)
301 Skip s -> Skip (Stream step1 s)
304-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
305-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
308fromStream :: (K.IsStream t, Monad m) => t m b -> Unfold m Void b
309fromStream str = Unfold streamStep (\_ -> return $ D.toStreamD str)
311-- | Convert a single argument stream generator function into an
312-- 'Unfold'. Note that a stream converted to an 'Unfold' may not be as
313-- efficient as an 'Unfold' in some situations.
316fromStream1 :: (K.IsStream t, Monad m) => (a -> t m b) -> Unfold m a b
317fromStream1 f = Unfold streamStep (return . D.toStreamD . f)
319-- | Convert a two argument stream generator function into an 'Unfold'. Note
320-- that a stream converted to an 'Unfold' may not be as efficient as an
321-- 'Unfold' in some situations.
324fromStream2 :: (K.IsStream t, Monad m)
325 => (a -> b -> t m c) -> Unfold m (a, b) c
326fromStream2 f = Unfold streamStep (\(a, b) -> return $ D.toStreamD $ f a b)
328-------------------------------------------------------------------------------
330-------------------------------------------------------------------------------
332-- | Lift a monadic function into an unfold generating a nil stream with a side
336nilM :: Monad m => (a -> m c) -> Unfold m a b
337nilM f = Unfold step return
339 {-# INLINE_LATE step #-}
340 step x = f x >> return Stop
342-- | Prepend a monadic single element generator function to an 'Unfold'.
345{-# INLINE_NORMAL consM #-}
346consM :: Monad m => (a -> m b) -> Unfold m a b -> Unfold m a b
347consM action unf = Unfold step inject
351 inject = return . Left
353 {-# INLINE_LATE step #-}
355 action a >>= \r -> return $ Yield r (Right (D.unfold unf a))
356 step (Right (UnStream step1 st)) = do
357 res <- step1 defState st
359 Yield x s -> return $ Yield x (Right (Stream step1 s))
360 Skip s -> return $ Skip (Right (Stream step1 s))
363-- | Lift a monadic effect into an unfold generating a singleton stream.
366effect :: Monad m => m b -> Unfold m Void b
367effect eff = Unfold step inject
369 inject _ = return True
370 {-# INLINE_LATE step #-}
371 step True = eff >>= \r -> return $ Yield r False
372 step False = return Stop
374-- | Lift a monadic function into an unfold generating a singleton stream.
376{-# INLINE singleton #-}
377singleton :: Monad m => (a -> m b) -> Unfold m a b
378singleton f = Unfold step inject
380 inject x = return $ Just x
381 {-# INLINE_LATE step #-}
382 step (Just x) = f x >>= \r -> return $ Yield r Nothing
383 step Nothing = return Stop
385-- | Identity unfold. Generates a singleton stream with the seed as the only
386-- element in the stream.
388-- > identity = singleton return
390{-# INLINE identity #-}
391identity :: Monad m => Unfold m a a
392identity = singleton return
394const :: Monad m => m b -> Unfold m a b
395const m = Unfold step inject
398 step () = m >>= \r -> return $ Yield r ()
400-- | Generates a stream replicating the seed @n@ times.
402{-# INLINE replicateM #-}
403replicateM :: Monad m => Int -> Unfold m a a
404replicateM n = Unfold step inject
406 inject x = return (x, n)
407 {-# INLINE_LATE step #-}
408 step (x, i) = return $
411 else Yield x (x, (i - 1))
413-- | Generates an infinite stream repeating the seed.
415{-# INLINE repeatM #-}
416repeatM :: Monad m => Unfold m a a
417repeatM = Unfold step return
419 {-# INLINE_LATE step #-}
420 step x = return $ Yield x x
422-- | Convert a list of pure values to a 'Stream'
423{-# INLINE_LATE fromList #-}
424fromList :: Monad m => Unfold m [a] a
425fromList = Unfold step inject
428 {-# INLINE_LATE step #-}
429 step (x:xs) = return $ Yield x xs
430 step [] = return Stop
432-- | Convert a list of monadic values to a 'Stream'
433{-# INLINE_LATE fromListM #-}
434fromListM :: Monad m => Unfold m [m a] a
435fromListM = Unfold step inject
438 {-# INLINE_LATE step #-}
439 step (x:xs) = x >>= \r -> return $ Yield r xs
440 step [] = return Stop
442-------------------------------------------------------------------------------
444-------------------------------------------------------------------------------
446{-# INLINE_NORMAL take #-}
447take :: Monad m => Int -> Unfold m a b -> Unfold m a b
448take n (Unfold step1 inject1) = Unfold step inject
453 {-# INLINE_LATE step #-}
454 step (st, i) | i < n = do
457 Yield x s -> Yield x (s, i + 1)
458 Skip s -> Skip (s, i)
460 step (_, _) = return Stop
462{-# INLINE_NORMAL takeWhileM #-}
463takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
464takeWhileM f (Unfold step1 inject1) = Unfold step inject1
466 {-# INLINE_LATE step #-}
472 return $ if b then Yield x s else Stop
473 Skip s -> return $ Skip s
476{-# INLINE takeWhile #-}
477takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
478takeWhile f = takeWhileM (return . f)
480{-# INLINE_NORMAL filterM #-}
481filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
482filterM f (Unfold step1 inject1) = Unfold step inject1
484 {-# INLINE_LATE step #-}
490 return $ if b then Yield x s else Skip s
491 Skip s -> return $ Skip s
495filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
496filter f = filterM (return . f)
498-------------------------------------------------------------------------------
500-------------------------------------------------------------------------------
502-- | Can be used to enumerate unbounded integrals. This does not check for
503-- overflow or underflow for bounded integrals.
504{-# INLINE_NORMAL enumerateFromStepIntegral #-}
505enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
506enumerateFromStepIntegral = Unfold step inject
508 inject (from, stride) = from `seq` stride `seq` return (from, stride)
509 {-# INLINE_LATE step #-}
510 step !(x, stride) = return $ Yield x $! (x + stride, stride)
512-- We are assuming that "to" is constrained by the type to be within
514{-# INLINE enumerateFromToIntegral #-}
515enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
516enumerateFromToIntegral to =
517 takeWhile (<= to) $ supplySecond enumerateFromStepIntegral 1
519{-# INLINE enumerateFromIntegral #-}
520enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
521enumerateFromIntegral = enumerateFromToIntegral maxBound
523-------------------------------------------------------------------------------
525-------------------------------------------------------------------------------
527data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
529{-# INLINE_NORMAL concat #-}
530concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
531concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
535 return $ ConcatOuter s
537 {-# INLINE_LATE step #-}
538 step (ConcatOuter st) = do
543 return $ Skip (ConcatInner s innerSt)
544 Skip s -> return $ Skip (ConcatOuter s)
547 step (ConcatInner ost ist) = do
550 Yield x s -> Yield x (ConcatInner ost s)
551 Skip s -> Skip (ConcatInner ost s)
552 Stop -> Skip (ConcatOuter ost)
554data OuterProductState s1 s2 sy x y =
555 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
557{-# INLINE_NORMAL outerProduct #-}
558outerProduct :: Monad m
559 => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d)
560outerProduct (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
564 return $ OuterProductOuter s1 y
566 {-# INLINE_LATE step #-}
567 step (OuterProductOuter st1 sy) = do
572 return $ Skip (OuterProductInner s sy s2 x)
573 Skip s -> return $ Skip (OuterProductOuter s sy)
576 step (OuterProductInner ost sy ist x) = do
579 Yield y s -> Yield (x, y) (OuterProductInner ost sy s x)
580 Skip s -> Skip (OuterProductInner ost sy s x)
581 Stop -> Skip (OuterProductOuter ost sy)
583-- XXX This can be used to implement a Monad instance for "Unfold m ()".
585data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
587{-# INLINE_NORMAL concatMapM #-}
589 => (b -> m (Unfold m () c)) -> Unfold m a b -> Unfold m a c
590concatMapM f (Unfold step1 inject1) = Unfold step inject
594 return $ ConcatMapOuter s
596 {-# INLINE_LATE step #-}
597 step (ConcatMapOuter st) = do
601 Unfold step2 inject2 <- f x
602 innerSt <- inject2 ()
603 return $ Skip (ConcatMapInner s (Stream (\_ ss -> step2 ss)
605 Skip s -> return $ Skip (ConcatMapOuter s)
608 step (ConcatMapInner ost (UnStream istep ist)) = do
609 r <- istep defState ist
611 Yield x s -> Yield x (ConcatMapInner ost (Stream istep s))
612 Skip s -> Skip (ConcatMapInner ost (Stream istep s))
613 Stop -> Skip (ConcatMapOuter ost)
615------------------------------------------------------------------------------
617------------------------------------------------------------------------------
619-- | The most general bracketing and exception combinator. All other
620-- combinators can be expressed in terms of this combinator. This can also be
621-- used for cases which are not covered by the standard combinators.
625{-# INLINE_NORMAL gbracket #-}
628 => (a -> m c) -- ^ before
629 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
630 -> (c -> m d) -- ^ after, on normal stop
631 -> Unfold m (c, e) b -- ^ on exception
632 -> Unfold m c b -- ^ unfold to run
634gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
642 return $ Right (s, r)
644 {-# INLINE_LATE step #-}
645 step (Right (st, v)) = do
646 res <- exc $ step1 st
649 Yield x s -> return $ Yield x (Right (s, v))
650 Skip s -> return $ Skip (Right (s, v))
651 Stop -> aft v >> return Stop
654 return $ Skip (Left r)
658 Yield x s -> return $ Yield x (Left s)
659 Skip s -> return $ Skip (Left s)
662-- | The most general bracketing and exception combinator. All other
663-- combinators can be expressed in terms of this combinator. This can also be
664-- used for cases which are not covered by the standard combinators.
668{-# INLINE_NORMAL gbracketIO #-}
670 :: (MonadIO m, MonadBaseControl IO m)
671 => (a -> m c) -- ^ before
672 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
673 -> (c -> m d) -- ^ after, on normal stop, or GC
674 -> Unfold m (c, e) b -- ^ on exception
675 -> Unfold m c b -- ^ unfold to run
677gbracketIO bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
684 ref <- D.newFinalizedIORef (aft r)
686 return $ Right (s, r, ref)
688 {-# INLINE_LATE step #-}
689 step (Right (st, v, ref)) = do
690 res <- exc $ step1 st
693 Yield x s -> return $ Yield x (Right (s, v, ref))
694 Skip s -> return $ Skip (Right (s, v, ref))
696 D.runIORefFinalizer ref
699 D.clearIORefFinalizer ref
701 return $ Skip (Left r)
705 Yield x s -> return $ Yield x (Left s)
706 Skip s -> return $ Skip (Left s)
709-- The custom implementation of "before" is slightly faster (5-7%) than
710-- "_before". This is just to document and make sure that we can always use
711-- gbracket to implement before. The same applies to other combinators as well.
713{-# INLINE_NORMAL _before #-}
714_before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
715_before action unf = gbracket (\x -> action x >> return x) (fmap Right)
716 (\_ -> return ()) undefined unf
718-- | Run a side effect before the unfold yields its first element.
721{-# INLINE_NORMAL before #-}
722before :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
723before action (Unfold step1 inject1) = Unfold step inject
732 {-# INLINE_LATE step #-}
736 Yield x s -> return $ Yield x s
737 Skip s -> return $ Skip s
740{-# INLINE_NORMAL _after #-}
741_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
742_after aft = gbracket return (fmap Right) aft undefined
744-- | Run a side effect whenever the unfold stops normally.
746-- Prefer afterIO over this as the @after@ action in this combinator is not
747-- executed if the unfold is partially evaluated lazily and then garbage
751{-# INLINE_NORMAL after #-}
752after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
753after action (Unfold step1 inject1) = Unfold step inject
761 {-# INLINE_LATE step #-}
765 Yield x s -> return $ Yield x (s, v)
766 Skip s -> return $ Skip (s, v)
767 Stop -> action v >> return Stop
769-- | Run a side effect whenever the unfold stops normally
770-- or is garbage collected after a partial lazy evaluation.
773{-# INLINE_NORMAL afterIO #-}
774afterIO :: (MonadIO m, MonadBaseControl IO m)
775 => (a -> m c) -> Unfold m a b -> Unfold m a b
776afterIO action (Unfold step1 inject1) = Unfold step inject
782 ref <- D.newFinalizedIORef (action x)
785 {-# INLINE_LATE step #-}
789 Yield x s -> return $ Yield x (s, ref)
790 Skip s -> return $ Skip (s, ref)
792 D.runIORefFinalizer ref
795{-# INLINE_NORMAL _onException #-}
796_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
797_onException action unf =
798 gbracket return MC.try
800 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
802-- | Run a side effect whenever the unfold aborts due to an exception.
805{-# INLINE_NORMAL onException #-}
806onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
807onException action (Unfold step1 inject1) = Unfold step inject
815 {-# INLINE_LATE step #-}
817 res <- step1 st `MC.onException` action v
819 Yield x s -> return $ Yield x (s, v)
820 Skip s -> return $ Skip (s, v)
823{-# INLINE_NORMAL _finally #-}
824_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
826 gbracket return MC.try action
827 (nilM (\(a, (e :: MC.SomeException)) -> action a >> MC.throwM e)) unf
829-- | Run a side effect whenever the unfold stops normally or aborts due to an
832-- Prefer finallyIO over this as the @after@ action in this combinator is not
833-- executed if the unfold is partially evaluated lazily and then garbage
837{-# INLINE_NORMAL finally #-}
838finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
839finally action (Unfold step1 inject1) = Unfold step inject
847 {-# INLINE_LATE step #-}
849 res <- step1 st `MC.onException` action v
851 Yield x s -> return $ Yield x (s, v)
852 Skip s -> return $ Skip (s, v)
853 Stop -> action v >> return Stop
855-- | Run a side effect whenever the unfold stops normally, aborts due to an
856-- exception or if it is garbage collected after a partial lazy evaluation.
859{-# INLINE_NORMAL finallyIO #-}
860finallyIO :: (MonadAsync m, MonadCatch m)
861 => (a -> m c) -> Unfold m a b -> Unfold m a b
862finallyIO action (Unfold step1 inject1) = Unfold step inject
868 ref <- D.newFinalizedIORef (action x)
871 {-# INLINE_LATE step #-}
873 res <- step1 st `MC.onException` D.runIORefFinalizer ref
875 Yield x s -> return $ Yield x (s, ref)
876 Skip s -> return $ Skip (s, ref)
878 D.runIORefFinalizer ref
881{-# INLINE_NORMAL _bracket #-}
882_bracket :: MonadCatch m
883 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
884_bracket bef aft unf =
885 gbracket bef MC.try aft (nilM (\(a, (e :: MC.SomeException)) -> aft a >>
888-- | @bracket before after between@ runs the @before@ action and then unfolds
889-- its output using the @between@ unfold. When the @between@ unfold is done or
890-- if an exception occurs then the @after@ action is run with the output of
891-- @before@ as argument.
893-- Prefer bracketIO over this as the @after@ action in this combinator is not
894-- executed if the unfold is partially evaluated lazily and then garbage
898{-# INLINE_NORMAL bracket #-}
899bracket :: MonadCatch m
900 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
901bracket bef aft (Unfold step1 inject1) = Unfold step inject
910 {-# INLINE_LATE step #-}
912 res <- step1 st `MC.onException` aft v
914 Yield x s -> return $ Yield x (s, v)
915 Skip s -> return $ Skip (s, v)
916 Stop -> aft v >> return Stop
918-- | @bracket before after between@ runs the @before@ action and then unfolds
919-- its output using the @between@ unfold. When the @between@ unfold is done or
920-- if an exception occurs then the @after@ action is run with the output of
921-- @before@ as argument. The after action is also executed if the unfold is
922-- paritally evaluated and then garbage collected.
925{-# INLINE_NORMAL bracketIO #-}
926bracketIO :: (MonadAsync m, MonadCatch m)
927 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
928bracketIO bef aft (Unfold step1 inject1) = Unfold step inject
935 ref <- D.newFinalizedIORef (aft r)
938 {-# INLINE_LATE step #-}
940 res <- step1 st `MC.onException` D.runIORefFinalizer ref
942 Yield x s -> return $ Yield x (s, ref)
943 Skip s -> return $ Skip (s, ref)
945 D.runIORefFinalizer ref
948-- | When unfolding if an exception occurs, unfold the exception using the
949-- exception unfold supplied as the first argument to 'handle'.
952{-# INLINE_NORMAL handle #-}
953handle :: (MonadCatch m, Exception e)
954 => Unfold m e b -> Unfold m a b -> Unfold m a b
956 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