on f8d80cc help sign in
1154 lines 36 kB history raw
1#include "inline.hs"
3-- |
4-- Module : Streamly.Internal.Data.Unfold
5-- Copyright : (c) 2019 Composewell Technologies
6-- License : BSD3
7-- Maintainer : streamly@composewell.com
8-- Stability : experimental
9-- Portability : GHC
10--
11-- To run the examples in this module:
12--
13-- >>> import qualified Streamly.Data.Fold as Fold
14-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
15--
16-- = Unfolds and Streams
17--
18-- An 'Unfold' type is the same as the direct style 'Stream' type except that
19-- it uses an inject function to determine the initial state of the stream
20-- based on an input. A stream is a special case of Unfold when the static
21-- input is unit or Void.
22--
23-- This allows an important optimization to occur in several cases, making the
24-- 'Unfold' a more efficient abstraction. Consider the 'concatMap' and
25-- 'unfoldMany' operations, the latter is more efficient. 'concatMap'
26-- generates a new stream object from each element in the stream by applying
27-- the supplied function to the element, the stream object includes the "step"
28-- function as well as the initial "state" of the stream. Since the stream is
29-- generated dynamically the compiler does not know the step function or the
30-- state type statically at compile time, therefore, it cannot inline it. On
31-- the other hand in case of 'unfoldMany' the compiler has visibility into
32-- the unfold's state generation function, therefore, the compiler knows all
33-- the types statically and it can inline the inject as well as the step
34-- functions, generating efficient code. Essentially, the stream is not opaque
35-- to the consumer in case of unfolds, the consumer knows how to generate the
36-- stream from a seed using a known "inject" and "step" functions.
37--
38-- A Stream is like a data object whereas unfold is like a function. Being
39-- function like, an Unfold is an instance of 'Category' and 'Arrow' type
40-- classes.
41--
42-- = Unfolds and Folds
43--
44-- Streams forcing a closed control flow loop can be categorized under
45-- two types, unfolds and folds, both of these are duals of each other.
46--
47-- Unfold streams are really generators of a sequence of elements, we can also
48-- call them pull style streams. These are lazy producers of streams. On each
49-- evaluation the producer generates the next element. A consumer can
50-- therefore pull elements from the stream whenever it wants to. A stream
51-- consumer can multiplex pull streams by pulling elements from the chosen
52-- streams, therefore, pull streams allow merging or multiplexing. On the
53-- other hand, with this representation we cannot split or demultiplex a
54-- stream. So really these are stream sources that can be generated from a
55-- seed and can be merged or zipped into a single stream.
56--
57-- The dual of Unfolds are Folds. Folds can also be called as push style
58-- streams or reducers. These are strict consumers of streams. We keep pushing
59-- elements to a fold and we can extract the result at any point. A driver can
60-- choose which fold to push to and can also push the same element to multiple
61-- folds. Therefore, folds allow splitting or demultiplexing a stream. On the
62-- other hand, we cannot merge streams using this representation. So really
63-- these are stream consumers that reduce the stream to a single value, these
64-- consumers can be composed such that a stream can be split over multiple
65-- consumers.
66--
67-- Performance:
68--
69-- Composing a tree or graph of computations with unfolds can be much more
70-- efficient compared to composing with the Monad instance. The reason is that
71-- unfolds allow the compiler to statically know the state and optimize it
72-- using stream fusion whereas it is not possible with the monad bind because
73-- the state is determined dynamically.
75-- Open control flow style streams can also have two representations. StreamK
76-- is a producer style representation. We can also have a consumer style
77-- representation. We can use that for composable folds in StreamK
78-- representation.
79--
81-- = Performance Notes
82--
83-- 'Unfold' representation is more efficient than using streams when combining
84-- streams. 'Unfold' type allows multiple unfold actions to be composed into a
85-- single unfold function in an efficient manner by enabling the compiler to
86-- perform stream fusion optimization.
87-- @Unfold m a b@ can be considered roughly equivalent to an action @a -> t m
88-- b@ (where @t@ is a stream type). Instead of using an 'Unfold' one could just
89-- use a function of the shape @a -> t m b@. However, working with stream types
90-- like t'Streamly.SerialT' does not allow the compiler to perform stream fusion
91-- optimization when merging, appending or concatenating multiple streams.
92-- Even though stream based combinator have excellent performance, they are
93-- much less efficient when compared to combinators using 'Unfold'. For
94-- example, the 'Streamly.Prelude.concatMap' combinator which uses @a -> t m b@
95-- (where @t@ is a stream type) to generate streams is much less efficient
96-- compared to 'Streamly.Prelude.unfoldMany'.
97--
98-- On the other hand, transformation operations on stream types are as
99-- efficient as transformations on 'Unfold'.
100--
101-- We should note that in some cases working with stream types may be more
102-- convenient compared to working with the 'Unfold' type. However, if extra
103-- performance boost is important then 'Unfold' based composition should be
104-- preferred compared to stream based composition when merging or concatenating
105-- streams.
107module Streamly.Internal.Data.Unfold
108 (
109 -- * Unfold Type
110 Step(..)
111 , Unfold
113 -- * Unfolds
114 -- One to one correspondence with
115 -- "Streamly.Internal.Data.Stream.IsStream.Generate"
116 -- ** Basic Constructors
117 , mkUnfoldM
118 , mkUnfoldrM
119 , unfoldrM
120 , unfoldr
121 , functionM
122 , function
123 , identity
124 , nilM
125 , consM
127 -- ** From Values
128 , fromEffect
129 , fromPure
131 -- ** Generators
132 -- | Generate a monadic stream from a seed.
133 , repeatM
134 , replicateM
135 , fromIndicesM
136 , iterateM
138 -- ** Enumerations
139 , Enumerable (..)
141 -- ** Enumerate Num
142 , enumerateFromNum
143 , enumerateFromThenNum
144 , enumerateFromStepNum
146 -- ** Enumerating 'Bounded 'Integral' Types
147 , enumerateFromIntegralBounded
148 , enumerateFromThenIntegralBounded
149 , enumerateFromToIntegralBounded
150 , enumerateFromThenToIntegralBounded
152 -- ** Enumerating 'Unounded Integral' Types
153 , enumerateFromIntegral
154 , enumerateFromThenIntegral
155 , enumerateFromToIntegral
156 , enumerateFromThenToIntegral
158 -- ** Enumerating 'Small Integral' Types
159 , enumerateFromSmallBounded
160 , enumerateFromThenSmallBounded
161 , enumerateFromToSmall
162 , enumerateFromThenToSmall
164 -- ** Enumerating 'Fractional' Types
165 , enumerateFromFractional
166 , enumerateFromThenFractional
167 , enumerateFromToFractional
168 , enumerateFromThenToFractional
170 -- ** From Containers
171 , fromList
172 , fromListM
174 , fromStream
175 , fromStreamK
176 , fromStreamD
178 -- * Combinators
179 -- ** Mapping on Input
180 , lmap
181 , lmapM
182 , supply
183 , supplyFirst
184 , supplySecond
185 , discardFirst
186 , discardSecond
187 , swap
188 -- coapply
189 -- comonad
191 -- * Folding
192 , fold
194 -- ** Mapping on Output
195 , map
196 , mapM
197 , mapMWithInput
198 , scanlM'
199 , scan
200 , foldMany
201 -- pipe
203 -- ** Either Wrapped Input
204 , either
206 -- ** Filtering
207 , takeWhileM
208 , takeWhile
209 , take
210 , filter
211 , filterM
212 , drop
213 , dropWhile
214 , dropWhileM
216 -- ** Zipping
217 , zipWithM
218 , zipWith
220 -- ** Cross product
221 , crossWithM
222 , crossWith
223 , cross
224 , apply
226 -- ** Nesting
227 , ConcatState (..)
228 , many
229 , concatMapM
230 , bind
232 -- ** Resource Management
233 , gbracket_
234 , gbracket
235 , before
236 , after
237 , after_
238 , finally
239 , finally_
240 , bracket
241 , bracket_
243 -- ** Exceptions
244 , onException
245 , handle
246 )
247where
249import Control.Exception (Exception, mask_)
250import Control.Monad.Catch (MonadCatch)
251import Control.Monad.IO.Class (MonadIO(..))
252import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
253import Data.Functor (($>))
254import GHC.Types (SPEC(..))
255import Streamly.Internal.Control.Concurrent (MonadAsync)
256import Streamly.Internal.Data.Fold.Type (Fold(..))
257import Streamly.Internal.Data.IOFinalizer
258 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
259import Streamly.Internal.Data.Stream.IsStream.Type (IsStream)
260import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
261import Streamly.Internal.Data.SVar.Type (defState)
263import qualified Control.Monad.Catch as MC
264import qualified Data.Tuple as Tuple
265import qualified Streamly.Internal.Data.Fold.Type as FL
266import qualified Streamly.Internal.Data.Stream.IsStream.Type as IsStream
267import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
268import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
270import Streamly.Internal.Data.Unfold.Enumeration
271import Streamly.Internal.Data.Unfold.Type
272import Prelude
273 hiding (map, mapM, takeWhile, take, filter, const, zipWith
274 , drop, dropWhile, either)
276-- $setup
277-- >>> import qualified Streamly.Data.Fold as Fold
278-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
279-- >>> import qualified Streamly.Prelude as Stream
282-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
283-- using the argument of the original fold as the second element of tuple and
284-- discarding the first element of the tuple.
285--
286-- @
287-- discardFirst = Unfold.lmap snd
288-- @
289--
290-- /Pre-release/
291--
292{-# INLINE_NORMAL discardFirst #-}
293discardFirst :: Unfold m a b -> Unfold m (c, a) b
294discardFirst = lmap snd
296-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
297-- using the argument of the original fold as the first element of tuple and
298-- discarding the second element of the tuple.
299--
300-- @
301-- discardSecond = Unfold.lmap fst
302-- @
303--
304-- /Pre-release/
305--
306{-# INLINE_NORMAL discardSecond #-}
307discardSecond :: Unfold m a b -> Unfold m (a, c) b
308discardSecond = lmap fst
310-- | Convert an 'Unfold' that accepts a tuple as an argument into an unfold
311-- that accepts a tuple with elements swapped.
312--
313-- @
314-- swap = Unfold.lmap Tuple.swap
315-- @
316--
317-- /Pre-release/
318--
319{-# INLINE_NORMAL swap #-}
320swap :: Unfold m (a, c) b -> Unfold m (c, a) b
321swap = lmap Tuple.swap
323-------------------------------------------------------------------------------
324-- Output operations
325-------------------------------------------------------------------------------
327-- XXX Do we need this combinator or the stream based idiom is enough?
329-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
330-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
331-- application of the fold on the unfolded stream.
332--
333-- >>> Unfold.fold Fold.sum Unfold.fromList [1..100]
334-- 5050
335--
336-- >>> fold f u = Stream.fold f . Stream.unfold u
337--
338-- /Pre-release/
339--
340{-# INLINE_NORMAL fold #-}
341fold :: Monad m => Fold m b c -> Unfold m a b -> a -> m c
342fold (Fold fstep initial extract) (Unfold ustep inject) a = do
343 res <- initial
344 case res of
345 FL.Partial x -> inject a >>= go SPEC x
346 FL.Done b -> return b
348 where
350 {-# INLINE_LATE go #-}
351 go !_ !fs st = do
352 r <- ustep st
353 case r of
354 Yield x s -> do
355 res <- fstep fs x
356 case res of
357 FL.Partial fs1 -> go SPEC fs1 s
358 FL.Done c -> return c
359 Skip s -> go SPEC fs s
360 Stop -> extract fs
362-- | Apply a fold multiple times on the output of an unfold.
363--
364-- /Unimplemented/
365foldMany :: -- Monad m =>
366 Fold m b c -> Unfold m a b -> Unfold m a c
367foldMany = undefined
369-- | Apply a monadic function to each element of the stream and replace it
370-- with the output of the resulting action.
371--
372-- /Since: 0.8.0/
373--
374{-# INLINE_NORMAL mapM #-}
375mapM :: Monad m => (b -> m c) -> Unfold m a b -> Unfold m a c
376mapM f (Unfold ustep uinject) = Unfold step uinject
377 where
378 {-# INLINE_LATE step #-}
379 step st = do
380 r <- ustep st
381 case r of
382 Yield x s -> f x >>= \a -> return $ Yield a s
383 Skip s -> return $ Skip s
384 Stop -> return Stop
386{-# INLINE_NORMAL mapMWithInput #-}
387mapMWithInput :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c
388mapMWithInput f (Unfold ustep uinject) = Unfold step inject
389 where
390 inject a = do
391 r <- uinject a
392 return (a, r)
394 {-# INLINE_LATE step #-}
395 step (inp, st) = do
396 r <- ustep st
397 case r of
398 Yield x s -> f inp x >>= \a -> return $ Yield a (inp, s)
399 Skip s -> return $ Skip (inp, s)
400 Stop -> return Stop
402-------------------------------------------------------------------------------
403-- Either
404-------------------------------------------------------------------------------
406-- | Make an unfold operate on values wrapped in an @Either a a@ type. 'Right
407-- a' translates to 'Right b' and 'Left a' translates to 'Left b'.
408--
409-- /Internal/
410{-# INLINE_NORMAL either #-}
411either :: Applicative m => Unfold m a b -> Unfold m (Either a a) (Either b b)
412either (Unfold step1 inject1) = Unfold step inject
414 where
416 inject (Left a) = (, Left) <$> inject1 a
417 inject (Right a) = (, Right) <$> inject1 a
419 {-# INLINE_LATE step #-}
420 step (st, f) = do
421 (\case
422 Yield x s -> Yield (f x) (s, f)
423 Skip s -> Skip (s, f)
424 Stop -> Stop) <$> step1 st
426-- | Scan the output of an 'Unfold' to change it in a stateful manner.
427--
428-- /Pre-release/
429{-# INLINE_NORMAL scan #-}
430scan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c
431scan (Fold stepF initial extract) (Unfold stepU injectU) =
432 Unfold step inject
434 where
436 inject a = do
437 r <- initial
438 case r of
439 FL.Partial fs -> Just . (fs,) <$> injectU a
440 FL.Done _ -> return Nothing
442 {-# INLINE_LATE step #-}
443 step (Just (fs, us)) = do
444 ru <- stepU us
445 case ru of
446 Yield x s -> do
447 rf <- stepF fs x
448 case rf of
449 FL.Done v -> return $ Yield v Nothing
450 FL.Partial fs1 -> do
451 v <- extract fs1
452 return $ Yield v (Just (fs1, s))
453 Skip s -> return $ Skip (Just (fs, s))
454 Stop -> return Stop
456 step Nothing = return Stop
458-- | Scan the output of an 'Unfold' to change it in a stateful manner.
459--
460-- /Unimplemented/
461{-# INLINE_NORMAL scanlM' #-}
462scanlM' :: Monad m => (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b
463scanlM' f z = scan (FL.foldlM' f z)
465-------------------------------------------------------------------------------
466-- Convert streams into unfolds
467-------------------------------------------------------------------------------
469{-# INLINE_NORMAL fromStreamD #-}
470fromStreamD :: Applicative m => Unfold m (Stream m a) a
471fromStreamD = Unfold step pure
473 where
475 {-# INLINE_LATE step #-}
476 step (UnStream step1 state1) =
477 (\case
478 Yield x s -> Yield x (Stream step1 s)
479 Skip s -> Skip (Stream step1 s)
480 Stop -> Stop) <$> step1 defState state1
482{-# INLINE_NORMAL fromStreamK #-}
483fromStreamK :: Applicative m => Unfold m (K.Stream m a) a
484fromStreamK = Unfold step pure
486 where
488 {-# INLINE_LATE step #-}
489 step stream = do
490 (\case
491 Just (x, xs) -> Yield x xs
492 Nothing -> Stop) <$> K.uncons stream
494-- XXX Using Unfold.fromStreamD seems to be faster (using cross product test
495-- case) than using fromStream even if it is implemented using fromStreamD.
496-- Check if StreamK to StreamD rewrite rules are working correctly when
497-- implementing fromStream using fromStreamD.
498--
499-- | Convert a stream into an 'Unfold'. Note that a stream converted to an
500-- 'Unfold' may not be as efficient as an 'Unfold' in some situations.
501--
502-- /Since: 0.8.0/
503--
504{-# INLINE_NORMAL fromStream #-}
505fromStream :: (IsStream t, Monad m) => Unfold m (t m a) a
506fromStream = lmap IsStream.toStream fromStreamK
508-------------------------------------------------------------------------------
509-- Unfolds
510-------------------------------------------------------------------------------
512-- | Lift a monadic function into an unfold generating a nil stream with a side
513-- effect.
514--
515{-# INLINE nilM #-}
516nilM :: Applicative m => (a -> m c) -> Unfold m a b
517nilM f = Unfold step pure
519 where
521 {-# INLINE_LATE step #-}
522 step x = f x $> Stop
524-- | Prepend a monadic single element generator function to an 'Unfold'. The
525-- same seed is used in the action as well as the unfold.
526--
527-- /Pre-release/
528{-# INLINE_NORMAL consM #-}
529consM :: Applicative m => (a -> m b) -> Unfold m a b -> Unfold m a b
530consM action unf = Unfold step inject
532 where
534 inject = pure . Left
536 {-# INLINE_LATE step #-}
537 step (Left a) = (`Yield` Right (D.unfold unf a)) <$> action a
538 step (Right (UnStream step1 st)) = do
539 (\case
540 Yield x s -> Yield x (Right (Stream step1 s))
541 Skip s -> Skip (Right (Stream step1 s))
542 Stop -> Stop) <$> step1 defState st
544-- XXX Check if "unfold (fromList [1..10])" fuses, if it doesn't we can use
545-- rewrite rules to rewrite list enumerations to unfold enumerations.
546--
547-- | Convert a list of pure values to a 'Stream'
548--
549-- /Since: 0.8.0/
550--
551{-# INLINE_LATE fromList #-}
552fromList :: Monad m => Unfold m [a] a
553fromList = Unfold step pure
555 where
557 {-# INLINE_LATE step #-}
558 step (x:xs) = pure $ Yield x xs
559 step [] = pure Stop
561-- | Convert a list of monadic values to a 'Stream'
562--
563-- /Since: 0.8.0/
564--
565{-# INLINE_LATE fromListM #-}
566fromListM :: Monad m => Unfold m [m a] a
567fromListM = Unfold step pure
569 where
571 {-# INLINE_LATE step #-}
572 step (x:xs) = (`Yield` xs) <$> x
573 step [] = pure Stop
575------------------------------------------------------------------------------
576-- Specialized Generation
577------------------------------------------------------------------------------
579-- | Generates a stream replicating the seed @n@ times.
580--
581-- /Since: 0.8.0/
582--
583{-# INLINE replicateM #-}
584replicateM :: Monad m => Int -> Unfold m (m a) a
585replicateM n = Unfold step inject
587 where
589 inject action = pure (action, n)
591 {-# INLINE_LATE step #-}
592 step (action, i) =
593 if i <= 0
594 then pure Stop
595 else (\x -> Yield x (action, i - 1)) <$> action
597-- | Generates an infinite stream repeating the seed.
598--
599-- /Since: 0.8.0/
600--
601{-# INLINE repeatM #-}
602repeatM :: Monad m => Unfold m (m a) a
603repeatM = Unfold step pure
605 where
607 {-# INLINE_LATE step #-}
608 step action = (`Yield` action) <$> action
610-- | Generates an infinite stream starting with the given seed and applying the
611-- given function repeatedly.
612--
613-- /Since: 0.8.0/
614--
615{-# INLINE iterateM #-}
616iterateM :: Monad m => (a -> m a) -> Unfold m (m a) a
617iterateM f = Unfold step id
619 where
621 {-# INLINE_LATE step #-}
622 step x = Yield x <$> f x
624-- | @fromIndicesM gen@ generates an infinite stream of values using @gen@
625-- starting from the seed.
626--
627-- @
628-- fromIndicesM f = Unfold.mapM f $ Unfold.enumerateFrom 0
629-- @
630--
631-- /Pre-release/
632--
633{-# INLINE_NORMAL fromIndicesM #-}
634fromIndicesM :: Applicative m => (Int -> m a) -> Unfold m Int a
635fromIndicesM gen = Unfold step pure
637 where
639 {-# INLINE_LATE step #-}
640 step i = (`Yield` (i + 1)) <$> gen i
642-------------------------------------------------------------------------------
643-- Filtering
644-------------------------------------------------------------------------------
646-- |
647-- >>> u = Unfold.take 2 Unfold.fromList
648-- >>> Unfold.fold Fold.toList u [1..100]
649-- [1,2]
650--
651-- /Since: 0.8.0/
652--
653{-# INLINE_NORMAL take #-}
654take :: Monad m => Int -> Unfold m a b -> Unfold m a b
655take n (Unfold step1 inject1) = Unfold step inject
657 where
659 inject x = (, 0) <$> inject1 x
661 {-# INLINE_LATE step #-}
662 step (st, i) | i < n = do
663 (\case
664 Yield x s -> Yield x (s, i + 1)
665 Skip s -> Skip (s, i)
666 Stop -> Stop) <$> step1 st
667 step (_, _) = pure Stop
669-- | Same as 'filter' but with a monadic predicate.
670--
671-- /Since: 0.8.0/
672--
673{-# INLINE_NORMAL filterM #-}
674filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
675filterM f (Unfold step1 inject1) = Unfold step inject1
676 where
677 {-# INLINE_LATE step #-}
678 step st = do
679 r <- step1 st
680 case r of
681 Yield x s -> do
682 b <- f x
683 return $ if b then Yield x s else Skip s
684 Skip s -> return $ Skip s
685 Stop -> return Stop
687-- | Include only those elements that pass a predicate.
688--
689-- /Since: 0.8.0/
690--
691{-# INLINE filter #-}
692filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
693filter f = filterM (return . f)
695-- | @drop n unf@ drops @n@ elements from the stream generated by @unf@.
696--
697-- /Since: 0.8.0/
698--
699{-# INLINE_NORMAL drop #-}
700drop :: Monad m => Int -> Unfold m a b -> Unfold m a b
701drop n (Unfold step inject) = Unfold step' inject'
703 where
705 inject' a = (, n) <$> inject a
707 {-# INLINE_LATE step' #-}
708 step' (st, i)
709 | i > 0 = do
710 (\case
711 Yield _ s -> Skip (s, i - 1)
712 Skip s -> Skip (s, i)
713 Stop -> Stop) <$> step st
714 | otherwise = do
715 (\case
716 Yield x s -> Yield x (s, 0)
717 Skip s -> Skip (s, 0)
718 Stop -> Stop) <$> step st
720-- | @dropWhileM f unf@ drops elements from the stream generated by @unf@ while
721-- the condition holds true. The condition function @f@ is /monadic/ in nature.
722--
723-- /Since: 0.8.0/
724--
725{-# INLINE_NORMAL dropWhileM #-}
726dropWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
727dropWhileM f (Unfold step inject) = Unfold step' inject'
729 where
731 inject' a = do
732 b <- inject a
733 return $ Left b
735 {-# INLINE_LATE step' #-}
736 step' (Left st) = do
737 r <- step st
738 case r of
739 Yield x s -> do
740 b <- f x
741 return
742 $ if b
743 then Skip (Left s)
744 else Yield x (Right s)
745 Skip s -> return $ Skip (Left s)
746 Stop -> return Stop
747 step' (Right st) = do
748 r <- step st
749 return
750 $ case r of
751 Yield x s -> Yield x (Right s)
752 Skip s -> Skip (Right s)
753 Stop -> Stop
755-- | Similar to 'dropWhileM' but with a pure condition function.
756--
757-- /Since: 0.8.0/
758--
759{-# INLINE dropWhile #-}
760dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
761dropWhile f = dropWhileM (return . f)
763------------------------------------------------------------------------------
764-- Exceptions
765------------------------------------------------------------------------------
767-- | Like 'gbracket' but with following differences:
768--
769-- * alloc action @a -> m c@ runs with async exceptions enabled
770-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
771-- after partial evaluation.
772-- * does not require a 'MonadAsync' constraint.
773--
774-- /Inhibits stream fusion/
775--
776-- /Pre-release/
777--
778{-# INLINE_NORMAL gbracket_ #-}
779gbracket_
780 :: Monad m
781 => (a -> m c) -- ^ before
782 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
783 -> (c -> m d) -- ^ after, on normal stop
784 -> Unfold m (c, e) b -- ^ on exception
785 -> Unfold m c b -- ^ unfold to run
786 -> Unfold m a b
787gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
788 Unfold step inject
790 where
792 inject x = do
793 r <- bef x
794 s <- inject1 r
795 return $ Right (s, r)
797 {-# INLINE_LATE step #-}
798 step (Right (st, v)) = do
799 res <- exc $ step1 st
800 case res of
801 Right r -> case r of
802 Yield x s -> return $ Yield x (Right (s, v))
803 Skip s -> return $ Skip (Right (s, v))
804 Stop -> aft v >> return Stop
805 -- XXX Do not handle async exceptions, just rethrow them.
806 Left e -> do
807 r <- einject (v, e)
808 return $ Skip (Left r)
809 step (Left st) = do
810 res <- estep st
811 return $ case res of
812 Yield x s -> Yield x (Left s)
813 Skip s -> Skip (Left s)
814 Stop -> Stop
816-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
817-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
818-- output @c@ as input to @Unfold m c b@ to generate an output stream. When
819-- unfolding use the supplied @try@ operation @forall s. m s -> m (Either e s)@
820-- to catch synchronous exceptions. If an exception occurs run the exception
821-- handling unfold @Unfold m (c, e) b@.
822--
823-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due
824-- to a sync or async exception or if it gets garbage collected after a partial
825-- lazy evaluation. See 'bracket' for the semantics of the cleanup action.
826--
827-- 'gbracket' can express all other exception handling combinators.
828--
829-- /Inhibits stream fusion/
830--
831-- /Pre-release/
832{-# INLINE_NORMAL gbracket #-}
833gbracket
834 :: (MonadIO m, MonadBaseControl IO m)
835 => (a -> m c) -- ^ before
836 -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)
837 -> (c -> m d) -- ^ after, on normal stop, or GC
838 -> Unfold m (c, e) b -- ^ on exception
839 -> Unfold m c b -- ^ unfold to run
840 -> Unfold m a b
841gbracket bef exc aft (Unfold estep einject) (Unfold step1 inject1) =
842 Unfold step inject
844 where
846 inject x = do
847 -- Mask asynchronous exceptions to make the execution of 'bef' and
848 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
849 (r, ref) <- liftBaseOp_ mask_ $ do
850 r <- bef x
851 ref <- newIOFinalizer (aft r)
852 return (r, ref)
853 s <- inject1 r
854 return $ Right (s, r, ref)
856 {-# INLINE_LATE step #-}
857 step (Right (st, v, ref)) = do
858 res <- exc $ step1 st
859 case res of
860 Right r -> case r of
861 Yield x s -> return $ Yield x (Right (s, v, ref))
862 Skip s -> return $ Skip (Right (s, v, ref))
863 Stop -> do
864 runIOFinalizer ref
865 return Stop
866 -- XXX Do not handle async exceptions, just rethrow them.
867 Left e -> do
868 -- Clearing of finalizer and running of exception handler must
869 -- be atomic wrt async exceptions. Otherwise if we have cleared
870 -- the finalizer and have not run the exception handler then we
871 -- may leak the resource.
872 r <- clearingIOFinalizer ref (einject (v, e))
873 return $ Skip (Left r)
874 step (Left st) = do
875 res <- estep st
876 return $ case res of
877 Yield x s -> Yield x (Left s)
878 Skip s -> Skip (Left s)
879 Stop -> Stop
881-- | Run a side effect @a -> m c@ on the input @a@ before unfolding it using
882-- @Unfold m a b@.
883--
884-- > before f = lmapM (\a -> f a >> return a)
885--
886-- /Pre-release/
887{-# INLINE_NORMAL before #-}
888before :: (a -> m c) -> Unfold m a b -> Unfold m a b
889before action (Unfold step inject) = Unfold step (action >> inject)
891-- The custom implementation of "after_" is slightly faster (5-7%) than
892-- "_after". This is just to document and make sure that we can always use
893-- gbracket to implement after_ The same applies to other combinators as well.
894--
895{-# INLINE_NORMAL _after #-}
896_after :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
897_after aft = gbracket_ return (fmap Right) aft undefined
899-- | Like 'after' with following differences:
900--
901-- * action @a -> m c@ won't run if the stream is garbage collected
902-- after partial evaluation.
903-- * Monad @m@ does not require any other constraints.
904--
905-- /Pre-release/
906{-# INLINE_NORMAL after_ #-}
907after_ :: Monad m => (a -> m c) -> Unfold m a b -> Unfold m a b
908after_ action (Unfold step1 inject1) = Unfold step inject
910 where
912 inject x = do
913 s <- inject1 x
914 return (s, x)
916 {-# INLINE_LATE step #-}
917 step (st, v) = do
918 res <- step1 st
919 case res of
920 Yield x s -> return $ Yield x (s, v)
921 Skip s -> return $ Skip (s, v)
922 Stop -> action v >> return Stop
924-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
925-- the unfold stops normally, or if it is garbage collected after a partial
926-- lazy evaluation.
927--
928-- The semantics of the action @a -> m c@ are similar to the cleanup action
929-- semantics in 'bracket'.
930--
931-- /See also 'after_'/
932--
933-- /Pre-release/
934{-# INLINE_NORMAL after #-}
935after :: (MonadIO m, MonadBaseControl IO m)
936 => (a -> m c) -> Unfold m a b -> Unfold m a b
937after action (Unfold step1 inject1) = Unfold step inject
939 where
941 inject x = do
942 s <- inject1 x
943 ref <- newIOFinalizer (action x)
944 return (s, ref)
946 {-# INLINE_LATE step #-}
947 step (st, ref) = do
948 res <- step1 st
949 case res of
950 Yield x s -> return $ Yield x (s, ref)
951 Skip s -> return $ Skip (s, ref)
952 Stop -> do
953 runIOFinalizer ref
954 return Stop
956{-# INLINE_NORMAL _onException #-}
957_onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
958_onException action =
959 gbracket_ return MC.try
960 (\_ -> return ())
961 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
963-- | Unfold the input @a@ using @Unfold m a b@, run the action @a -> m c@ on
964-- @a@ if the unfold aborts due to an exception.
965--
966-- /Inhibits stream fusion/
967--
968-- /Pre-release/
969{-# INLINE_NORMAL onException #-}
970onException :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
971onException action (Unfold step1 inject1) = Unfold step inject
973 where
975 inject x = do
976 s <- inject1 x
977 return (s, x)
979 {-# INLINE_LATE step #-}
980 step (st, v) = do
981 res <- step1 st `MC.onException` action v
982 return $ case res of
983 Yield x s -> Yield x (s, v)
984 Skip s -> Skip (s, v)
985 Stop -> Stop
987{-# INLINE_NORMAL _finally #-}
988_finally :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
989_finally action =
990 gbracket_ return MC.try action
991 (nilM (\(a, e :: MC.SomeException) -> action a >> MC.throwM e))
993-- | Like 'finally' with following differences:
994--
995-- * action @a -> m c@ won't run if the stream is garbage collected
996-- after partial evaluation.
997-- * does not require a 'MonadAsync' constraint.
998--
999-- /Inhibits stream fusion/
1001-- /Pre-release/
1002{-# INLINE_NORMAL finally_ #-}
1003finally_ :: MonadCatch m => (a -> m c) -> Unfold m a b -> Unfold m a b
1004finally_ action (Unfold step1 inject1) = Unfold step inject
1006 where
1008 inject x = do
1009 s <- inject1 x
1010 return (s, x)
1012 {-# INLINE_LATE step #-}
1013 step (st, v) = do
1014 res <- step1 st `MC.onException` action v
1015 case res of
1016 Yield x s -> return $ Yield x (s, v)
1017 Skip s -> return $ Skip (s, v)
1018 Stop -> action v >> return Stop
1020-- | Unfold the input @a@ using @Unfold m a b@, run an action on @a@ whenever
1021-- the unfold stops normally, aborts due to an exception or if it is garbage
1022-- collected after a partial lazy evaluation.
1024-- The semantics of the action @a -> m c@ are similar to the cleanup action
1025-- semantics in 'bracket'.
1027-- @
1028-- finally release = bracket return release
1029-- @
1031-- /See also 'finally_'/
1033-- /Inhibits stream fusion/
1035-- /Pre-release/
1036{-# INLINE_NORMAL finally #-}
1037finally :: (MonadAsync m, MonadCatch m)
1038 => (a -> m c) -> Unfold m a b -> Unfold m a b
1039finally action (Unfold step1 inject1) = Unfold step inject
1041 where
1043 inject x = do
1044 s <- inject1 x
1045 ref <- newIOFinalizer (action x)
1046 return (s, ref)
1048 {-# INLINE_LATE step #-}
1049 step (st, ref) = do
1050 res <- step1 st `MC.onException` runIOFinalizer ref
1051 case res of
1052 Yield x s -> return $ Yield x (s, ref)
1053 Skip s -> return $ Skip (s, ref)
1054 Stop -> do
1055 runIOFinalizer ref
1056 return Stop
1058{-# INLINE_NORMAL _bracket #-}
1059_bracket :: MonadCatch m
1060 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1061_bracket bef aft =
1062 gbracket_ bef MC.try aft (nilM (\(a, e :: MC.SomeException) -> aft a >>
1063 MC.throwM e))
1065-- | Like 'bracket' but with following differences:
1067-- * alloc action @a -> m c@ runs with async exceptions enabled
1068-- * cleanup action @c -> m d@ won't run if the stream is garbage collected
1069-- after partial evaluation.
1070-- * does not require a 'MonadAsync' constraint.
1072-- /Inhibits stream fusion/
1074-- /Pre-release/
1075{-# INLINE_NORMAL bracket_ #-}
1076bracket_ :: MonadCatch m
1077 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1078bracket_ bef aft (Unfold step1 inject1) = Unfold step inject
1080 where
1082 inject x = do
1083 r <- bef x
1084 s <- inject1 r
1085 return (s, r)
1087 {-# INLINE_LATE step #-}
1088 step (st, v) = do
1089 res <- step1 st `MC.onException` aft v
1090 case res of
1091 Yield x s -> return $ Yield x (s, v)
1092 Skip s -> return $ Skip (s, v)
1093 Stop -> aft v >> return Stop
1095-- | Run the alloc action @a -> m c@ with async exceptions disabled but keeping
1096-- blocking operations interruptible (see 'Control.Exception.mask'). Use the
1097-- output @c@ as input to @Unfold m c b@ to generate an output stream.
1099-- @c@ is usually a resource under the state of monad @m@, e.g. a file
1100-- handle, that requires a cleanup after use. The cleanup action @c -> m d@,
1101-- runs whenever the stream ends normally, due to a sync or async exception or
1102-- if it gets garbage collected after a partial lazy evaluation.
1104-- 'bracket' only guarantees that the cleanup action runs, and it runs with
1105-- async exceptions enabled. The action must ensure that it can successfully
1106-- cleanup the resource in the face of sync or async exceptions.
1108-- When the stream ends normally or on a sync exception, cleanup action runs
1109-- immediately in the current thread context, whereas in other cases it runs in
1110-- the GC context, therefore, cleanup may be delayed until the GC gets to run.
1112-- /See also: 'bracket_', 'gbracket'/
1114-- /Inhibits stream fusion/
1116-- /Pre-release/
1117{-# INLINE_NORMAL bracket #-}
1118bracket :: (MonadAsync m, MonadCatch m)
1119 => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b
1120bracket bef aft (Unfold step1 inject1) = Unfold step inject
1122 where
1124 inject x = do
1125 -- Mask asynchronous exceptions to make the execution of 'bef' and
1126 -- the registration of 'aft' atomic. See comment in 'D.gbracketIO'.
1127 (r, ref) <- liftBaseOp_ mask_ $ do
1128 r <- bef x
1129 ref <- newIOFinalizer (aft r)
1130 return (r, ref)
1131 s <- inject1 r
1132 return (s, ref)
1134 {-# INLINE_LATE step #-}
1135 step (st, ref) = do
1136 res <- step1 st `MC.onException` runIOFinalizer ref
1137 case res of
1138 Yield x s -> return $ Yield x (s, ref)
1139 Skip s -> return $ Skip (s, ref)
1140 Stop -> do
1141 runIOFinalizer ref
1142 return Stop
1144-- | When unfolding @Unfold m a b@ if an exception @e@ occurs, unfold @e@ using
1145-- @Unfold m e b@.
1147-- /Inhibits stream fusion/
1149-- /Pre-release/
1150{-# INLINE_NORMAL handle #-}
1151handle :: (MonadCatch m, Exception e)
1152 => Unfold m e b -> Unfold m a b -> Unfold m a b
1153handle exc =
1154 gbracket_ return MC.try (\_ -> return ()) (discardFirst exc)