on master help sign in
Add modular scanning functions for transformations (#1809)
935f69eHarendra Kumar
benchmark/Streamly/Benchmark/Data/Fold.hs+10−10
@@ -93,16 +93,16 @@ sequence_ value =
93filter :: Monad m => Int -> Stream m Int -> m ()
93filter :: Monad m => Int -> Stream m Int -> m ()
94filter _ = Stream.fold (FL.filter even FL.drain)
94filter _ = Stream.fold (FL.filter even FL.drain)
95
95
96−{-# INLINE foldFilter #-}
96+{-# INLINE scanMaybe #-}
97−foldFilter :: Monad m => Int -> Stream m Int -> m ()
97+scanMaybe :: Monad m => Int -> Stream m Int -> m ()
98−foldFilter _ = Stream.fold (FL.foldFilter (FL.satisfy even) FL.drain)
98+scanMaybe _ = Stream.fold (FL.scanMaybe (FL.filtering even) FL.drain)
99
99
100−{-# INLINE foldFilter2 #-}
100+{-# INLINE scanMaybe2 #-}
101−foldFilter2 :: Monad m => Int -> Stream m Int -> m ()
101+scanMaybe2 :: Monad m => Int -> Stream m Int -> m ()
102−foldFilter2 _ =
102+scanMaybe2 _ =
103 Stream.fold
103 Stream.fold
104− $ FL.foldFilter (FL.satisfy even)
104+ $ FL.scanMaybe (FL.filtering even)
105− $ FL.foldFilter (FL.satisfy odd) FL.drain
105+ $ FL.scanMaybe (FL.filtering odd) FL.drain
106
106
107-------------------------------------------------------------------------------
107-------------------------------------------------------------------------------
108-- Splitting by serial application
108-- Splitting by serial application
@@ -395,8 +395,8 @@ o_1_space_serial_composition value =
395 [ bgroup
395 [ bgroup
396 "composition"
396 "composition"
397 [ benchIOSink value "filter even" $ filter value
397 [ benchIOSink value "filter even" $ filter value
398− , benchIOSink value "foldFilter even" $ foldFilter value
398+ , benchIOSink value "scanMaybe even" $ scanMaybe value
399− , benchIOSink value "foldFilter even, odd" $ foldFilter2 value
399+ , benchIOSink value "scanMaybe even, odd" $ scanMaybe2 value
400 , benchIOSink value "foldBreak (recursive)" foldBreak
400 , benchIOSink value "foldBreak (recursive)" foldBreak
401 , benchIOSink value "serialWith (all, any)" $ splitAllAny value
401 , benchIOSink value "serialWith (all, any)" $ splitAllAny value
402 , benchIOSink value "serial_ (all, any)" $ serial_ value
402 , benchIOSink value "serial_ (all, any)" $ serial_ value
core/src/Streamly/Data/Stream.hs+14−1
@@ -101,7 +101,8 @@ module Streamly.Data.Stream
101 -- , consM2 -- fused version
101 -- , consM2 -- fused version
102
102
103 -- ** Unfolding
103 -- ** Unfolding
104− -- | Generalized way of generating a stream efficiently.
104+ -- | 'unfoldrM' is the most general way of generating a stream efficiently.
105+ -- All other generation operations can be expressed using it.
105 , unfoldr
106 , unfoldr
106 , unfoldrM
107 , unfoldrM
107
108
@@ -226,6 +227,8 @@ module Streamly.Data.Stream
226 , uncons
227 , uncons
227
228
228 -- ** Folding
229 -- ** Folding
230+ -- XXX Need to have a general parse operation here which can be used to
231+ -- express all others.
229 , fold -- XXX rename to run? We can have a Stream.run and Fold.run.
232 , fold -- XXX rename to run? We can have a Stream.run and Fold.run.
230 -- XXX fold1 can be achieved using Monoids or Refolds.
233 -- XXX fold1 can be achieved using Monoids or Refolds.
231 , foldBreak
234 , foldBreak
@@ -292,6 +295,8 @@ module Streamly.Data.Stream
292 -- | Remove elements from the stream.
295 -- | Remove elements from the stream.
293
296
294 -- ** Stateless Filters
297 -- ** Stateless Filters
298+ -- | 'mapMaybeM' is the most general stateless filtering operation. All
299+ -- other filtering operations can be expressed using it.
295 -- EXPLANATION:
300 -- EXPLANATION:
296 -- In imperative terms a filter over a stream corresponds to a loop with a
301 -- In imperative terms a filter over a stream corresponds to a loop with a
297 -- @continue@ clause for the cases when the predicate fails.
302 -- @continue@ clause for the cases when the predicate fails.
@@ -302,6 +307,11 @@ module Streamly.Data.Stream
302 , filterM
307 , filterM
303
308
304 -- ** Stateful Filters
309 -- ** Stateful Filters
310+ -- | 'scanMaybe' is the most general stateful filtering operation. The
311+ -- filtering folds (folds returning a 'Maybe' type) in
312+ -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
313+ -- perform stateful filtering operations in general.
314+ , scanMaybe
305 , take
315 , take
306 , takeWhile
316 , takeWhile
307 , takeWhileM
317 , takeWhileM
@@ -389,6 +399,7 @@ module Streamly.Data.Stream
389
399
390 -- * Buffered Operations
400 -- * Buffered Operations
391 -- | Operations that require buffering of the stream.
401 -- | Operations that require buffering of the stream.
402+ -- Reverse is essentially a left fold followed by an unfold.
392 , reverse
403 , reverse
393
404
394 -- * Multi-Stream folds
405 -- * Multi-Stream folds
@@ -414,6 +425,8 @@ module Streamly.Data.Stream
414 , handle
425 , handle
415
426
416 -- * Resource Management
427 -- * Resource Management
428+ -- | 'bracket' is the most general resource management operation, all other
429+ -- operations can be expressed using it.
417 , before
430 , before
418 , after
431 , after
419 , finally
432 , finally
core/src/Streamly/Internal/Data/Fold.hs+293−88
@@ -36,6 +36,11 @@ module Streamly.Internal.Data.Fold
36 , foldr
36 , foldr
37 , foldrM
37 , foldrM
38
38
39+ -- * Mappers
40+ -- | Monadic functions useful with mapM/lmapM on folds or streams.
41+ , tracing
42+ , trace
43+
39 -- * Folds
44 -- * Folds
40 -- ** Identity
45 -- ** Identity
41 , fromPure
46 , fromPure
@@ -51,7 +56,6 @@ module Streamly.Internal.Data.Fold
51 -- *** Reducers
56 -- *** Reducers
52 , drain
57 , drain
53 , drainBy
58 , drainBy
54− , last
55 , the
59 , the
56 , length
60 , length
57 , genericLength
61 , genericLength
@@ -63,7 +67,6 @@ module Streamly.Internal.Data.Fold
63 , rollingHashWithSalt
67 , rollingHashWithSalt
64 , rollingHashFirstN
68 , rollingHashFirstN
65 -- , rollingHashLastN
69 -- , rollingHashLastN
66− , rollingMapM
67
70
68 -- *** Saturating Reducers
71 -- *** Saturating Reducers
69 -- | 'product' terminates if it becomes 0. Other folds can theoretically
72 -- | 'product' terminates if it becomes 0. Other folds can theoretically
@@ -86,15 +89,41 @@ module Streamly.Internal.Data.Fold
86 , toStream
89 , toStream
87 , toStreamRev
90 , toStreamRev
88 , toMap
91 , toMap
92+ , topBy
89 , top
93 , top
90 , bottom
94 , bottom
91
95
96+ -- *** Scanners
97+ -- | Stateful transformation of the elements. Useful in combination with
98+ -- the 'scanMaybe' combinator. For scanners the result of the fold is
99+ -- usually a transformation of the current element rather than an
100+ -- aggregation of all elements till now.
101+ , last -- XXX prev
102+ -- , nthLast -- using Ring array
103+ , indexingWith
104+ , indexing
105+ , indexingRev
106+ , rollingMapM
107+
108+ -- *** Filters
109+ -- | Useful in combination with the 'scanMaybe' combinator.
110+ , filtering
111+ , deleteBy
112+ , nub
113+ , nubInt
114+ , uniqBy
115+ , uniq
116+ , repeated
117+ , findIndices
118+ , elemIndices
119+
92 -- ** Terminating Folds
120 -- ** Terminating Folds
93 -- Element folds. Terminate after inspecting one element. All these can be
121 -- Element folds. Terminate after inspecting one element. All these can be
94 -- implemented in terms of the 'maybe' fold.
122 -- implemented in terms of the 'maybe' fold.
95 , head
123 , head
96 , one
124 , one
97 , null
125 , null
126+ , satisfy
98 , maybe
127 , maybe
99
128
100 -- Sequence folds. Terminate after inspecting a sequence of elements.
129 -- Sequence folds. Terminate after inspecting a sequence of elements.
@@ -115,6 +144,18 @@ module Streamly.Internal.Data.Fold
115 , and
144 , and
116 , or
145 , or
117
146
147+ -- ** Trimmers
148+ -- | Useful in combination with the 'scanMaybe' combinator.
149+ , taking
150+ , dropping
151+ , takingEndByM
152+ , takingEndBy
153+ , takingEndByM_
154+ , takingEndBy_
155+ , droppingWhileM
156+ , droppingWhile
157+ , prune
158+
118 -- * Combinators
159 -- * Combinators
119 -- ** Utilities
160 -- ** Utilities
120 , with
161 , with
@@ -131,42 +172,35 @@ module Streamly.Internal.Data.Fold
131 , lmap
172 , lmap
132 --, lsequence
173 --, lsequence
133 , lmapM
174 , lmapM
175+
176+ -- ** Sliding Window
177+ , slide2
178+
179+ -- ** Scanning Input
134 , scan
180 , scan
135 , scanMany
181 , scanMany
136 , postscan
182 , postscan
137 , indexed
183 , indexed
138− , trace
139
184
140 -- ** Zipping Input
185 -- ** Zipping Input
141 , zipWithM
186 , zipWithM
142 , zip
187 , zip
143
188
144− -- ** Filtering
189+ -- ** Filtering Input
190+ , catMaybes
191+ , mapMaybeM
192+ , mapMaybe
193+ , scanMaybe
145 , filter
194 , filter
146 , filterM
195 , filterM
147− , foldFilter
148− , satisfy
149 , sampleFromthen
196 , sampleFromthen
150− -- , ldeleteBy
151− -- , luniq
152− , nub
153− , nubInt
154−
155− -- ** Mapping Filters
156− , catMaybes
157− , mapMaybe
158− -- , mapMaybeM
159
197
160 -- Either streams
198 -- Either streams
161 , lefts
199 , lefts
162 , rights
200 , rights
163 , both
201 , both
164
202
165− -- ** Scanning Filters
166− , findIndices
167 {-
203 {-
168− , elemIndices
169−
170 -- ** Insertion
204 -- ** Insertion
171 -- | Insertion adds more elements to the stream.
205 -- | Insertion adds more elements to the stream.
172
206
@@ -304,10 +338,10 @@ import Data.Functor.Identity (Identity(..))
304import Data.Int (Int64)
338import Data.Int (Int64)
305import Data.IORef (newIORef, readIORef, writeIORef)
339import Data.IORef (newIORef, readIORef, writeIORef)
306import Data.Map.Strict (Map)
340import Data.Map.Strict (Map)
307−import Data.Maybe (isJust, fromJust)
308import Data.Word (Word32)
341import Data.Word (Word32)
309import Foreign.Storable (peek, sizeOf)
342import Foreign.Storable (peek, sizeOf)
310import Streamly.Internal.Data.IsMap (IsMap(..))
343import Streamly.Internal.Data.IsMap (IsMap(..))
344+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
311import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
345import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
312import Streamly.Internal.Data.Unboxed (Unboxed)
346import Streamly.Internal.Data.Unboxed (Unboxed)
313import Streamly.Internal.Data.Unfold.Type (Unfold(..))
347import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -339,17 +373,18 @@ import Streamly.Internal.Data.Fold.Type
339-- >>> :m
373-- >>> :m
340-- >>> :set -package streamly
374-- >>> :set -package streamly
341-- >>> import Prelude hiding (break, map, span, splitAt)
375-- >>> import Prelude hiding (break, map, span, splitAt)
342−-- >>> import qualified Streamly.Data.Stream as Stream
376+-- >>> import qualified Streamly.Data.Array.Unboxed as Array
343-- >>> import qualified Streamly.Data.Fold as Fold
377-- >>> import qualified Streamly.Data.Fold as Fold
378+-- >>> import qualified Streamly.Data.Stream as Stream
379+-- >>> import qualified Streamly.Internal.Data.Array.Unboxed.Mut.Type as MA
344-- >>> import qualified Streamly.Internal.Data.Fold as Fold
380-- >>> import qualified Streamly.Internal.Data.Fold as Fold
345-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
381-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
346-- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW
382-- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW
347−-- >>> import qualified Streamly.Data.Array.Unboxed as Array
348-- >>> import qualified Streamly.Internal.Data.Parser as Parser
383-- >>> import qualified Streamly.Internal.Data.Parser as Parser
349-- >>> import qualified Streamly.Internal.Data.Stream.Type as Stream
384-- >>> import qualified Streamly.Internal.Data.Stream.Type as Stream
385+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
350-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
386-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
351-- >>> import Data.IORef (newIORef, readIORef, writeIORef)
387-- >>> import Data.IORef (newIORef, readIORef, writeIORef)
352−-- >>> import qualified Streamly.Internal.Data.Array.Unboxed.Mut.Type as MA
353
388
354------------------------------------------------------------------------------
389------------------------------------------------------------------------------
355-- hoist
390-- hoist
@@ -390,10 +425,20 @@ sequence = rmapM id
390mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
425mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
391mapM = rmapM
426mapM = rmapM
392
427
428+-- |
429+-- >>> mapMaybeM f = Fold.lmapM f . Fold.catMaybes
430+--
431+{-# INLINE mapMaybeM #-}
432+mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Fold m b r -> Fold m a r
433+mapMaybeM f = lmapM f . catMaybes
434+
393-- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of
435-- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of
394-- the fold, filters out 'Nothing' elements, and return the values extracted
436-- the fold, filters out 'Nothing' elements, and return the values extracted
395-- from 'Just'.
437-- from 'Just'.
396--
438--
439+-- >>> mapMaybe f = Fold.lmap f . Fold.catMaybes
440+-- >>> mapMaybe f = Fold.mapMaybeM (return . f)
441+--
397-- >>> f x = if even x then Just x else Nothing
442-- >>> f x = if even x then Just x else Nothing
398-- >>> fld = Fold.mapMaybe f Fold.toList
443-- >>> fld = Fold.mapMaybe f Fold.toList
399-- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)
444-- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)
@@ -401,13 +446,25 @@ mapM = rmapM
401--
446--
402-- @since 0.8.0
447-- @since 0.8.0
403{-# INLINE mapMaybe #-}
448{-# INLINE mapMaybe #-}
404−mapMaybe :: (Monad m) => (a -> Maybe b) -> Fold m b r -> Fold m a r
449+mapMaybe :: Monad m => (a -> Maybe b) -> Fold m b r -> Fold m a r
405−mapMaybe f = lmap f . filter isJust . lmap fromJust
450+mapMaybe f = lmap f . catMaybes
406
451
407------------------------------------------------------------------------------
452------------------------------------------------------------------------------
408-- Transformations on fold inputs
453-- Transformations on fold inputs
409------------------------------------------------------------------------------
454------------------------------------------------------------------------------
410
455
456+-- | Apply a monadic function on the input and return the input.
457+--
458+-- >>> Stream.fold (Fold.lmapM (Fold.tracing print) Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)
459+-- 1
460+-- 2
461+--
462+-- /Pre-release/
463+--
464+{-# INLINE tracing #-}
465+tracing :: Monad m => (a -> m b) -> (a -> m a)
466+tracing f x = void (f x) >> return x
467+
411-- | Apply a monadic function to each element flowing through and discard the
468-- | Apply a monadic function to each element flowing through and discard the
412-- results.
469-- results.
413--
470--
@@ -415,10 +472,12 @@ mapMaybe f = lmap f . filter isJust . lmap fromJust
415-- 1
472-- 1
416-- 2
473-- 2
417--
474--
475+-- >>> trace f = Fold.lmapM (Fold.tracing f)
476+--
418-- /Pre-release/
477-- /Pre-release/
419{-# INLINE trace #-}
478{-# INLINE trace #-}
420trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r
479trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r
421−trace f = lmapM (\x -> void (f x) >> return x)
480+trace f = lmapM (tracing f)
422
481
423-- rename to lpipe?
482-- rename to lpipe?
424--
483--
@@ -516,82 +575,110 @@ scan = scanWith False
516scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
575scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
517scanMany = scanWith True
576scanMany = scanWith True
518
577
519−-- | Postscan the input of a 'Fold' to change it in a stateful manner using
578+------------------------------------------------------------------------------
520−-- another 'Fold'.
579+-- Filters
521−-- /Pre-release/
580+------------------------------------------------------------------------------
522−{-# INLINE postscan #-}
581+
523−postscan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
582+-- | A filtering scan that deletes the first occurrence of the element in the
524−postscan (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
583+-- stream that satisfies the given equality predicate.
525− Fold step initial extract
584+--
585+-- >>> input = Stream.unfold Unfold.fromList [1,3,3,5]
586+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.deleteBy (==) 3) input
587+-- [1,3,5]
588+--
589+{-# INLINE_NORMAL deleteBy #-}
590+deleteBy :: Monad m => (a -> a -> Bool) -> a -> Fold m a (Maybe a)
591+deleteBy eq x0 = fmap extract $ foldl' step (Tuple' False Nothing)
526
592
527 where
593 where
528
594
529− {-# INLINE runStep #-}
595+ step (Tuple' False _) x =
530− runStep actionL sR = do
596+ if eq x x0
531− rL <- actionL
597+ then Tuple' True Nothing
532− case rL of
598+ else Tuple' False (Just x)
533− Done bL -> do
599+ step (Tuple' True _) x = Tuple' True (Just x)
534− rR <- stepR sR bL
535− case rR of
536− Partial sR1 -> Done <$> extractR sR1
537− Done bR -> return $ Done bR
538− Partial sL -> do
539− !b <- extractL sL
540− rR <- stepR sR b
541− return
542− $ case rR of
543− Partial sR1 -> Partial (sL, sR1)
544− Done bR -> Done bR
545
600
546− initial = do
601+ extract (Tuple' _ x) = x
547− r <- initialR
548− rL <- initialL
549− case r of
550− Partial sR ->
551− case rL of
552− Done _ -> Done <$> extractR sR
553− Partial sL -> return $ Partial (sL, sR)
554− Done b -> return $ Done b
555
602
556− step (sL, sR) x = runStep (stepL sL x) sR
603+-- | Provide a sliding window of length 2 elements.
604+--
605+-- See "Streamly.Internal.Data.Fold.Window".
606+--
607+{-# INLINE slide2 #-}
608+slide2 :: Monad m => Fold m (a, Maybe a) b -> Fold m a b
609+slide2 (Fold step1 initial1 extract1) = Fold step initial extract
557
610
558− extract = extractR . snd
611+ where
559
612
560−------------------------------------------------------------------------------
613+ initial =
561−-- Filters
614+ first (Tuple' Nothing) <$> initial1
562−------------------------------------------------------------------------------
615+
616+ step (Tuple' prev s) cur =
617+ first (Tuple' (Just cur)) <$> step1 s (cur, prev)
618+
619+ extract (Tuple' _ s) = extract1 s
563
620
564−-- | Convert a predicate into a filtering fold.
621+-- | Drop repeated elements that are adjacent to each other using the supplied
622+-- comparison function.
565--
623--
566−-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
624+-- To strip duplicate path separators:
625+--
626+-- >>> input = Stream.unfold Unfold.fromList "//a//b"
627+-- >>> f x y = x == '/' && y == '/'
567−-- >>> Stream.fold f $ Stream.fromList [1..10]
628+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.uniqBy f) input
568−-- 40
629+-- "/a/b"
630+--
631+-- Space: @O(1)@
632+--
633+-- See also: 'nubBy'.
569--
634--
570-- /Pre-release/
635-- /Pre-release/
636+--
571−{-# INLINE satisfy #-}
637+{-# INLINE uniqBy #-}
572−satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
638+uniqBy :: Monad m => (a -> a -> Bool) -> Fold m a (Maybe a)
573−satisfy f = Fold step (return $ Partial ()) (const (return Nothing))
639+uniqBy eq = rollingMap f
574
640
575 where
641 where
576
642
577− step () a = return $ Done $ if f a then Just a else Nothing
643+ f pre curr =
644+ case pre of
645+ Nothing -> Just curr
646+ Just x -> if x `eq` curr then Nothing else Just curr
578
647
579−-- | Use a 'Maybe' returning fold as a filtering scan.
648+-- | Drop repeated elements that are adjacent to each other.
580--
649--
581−-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
650+-- >>> uniq = Fold.uniqBy (==)
582−-- >>> Stream.fold f $ Stream.fromList [1..10]
583−-- 40
584--
651--
585−-- The above snippet is equivalent to:
652+{-# INLINE uniq #-}
653+uniq :: (Monad m, Eq a) => Fold m a (Maybe a)
654+uniq = uniqBy (==)
655+
656+-- | Strip all leading and trailing occurrences of an element passing a
657+-- predicate and make all other consecutive occurrences uniq.
586--
658--
587−-- >>> f = Fold.filter (> 5) Fold.sum
659+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
588−-- >>> Stream.fold f $ Stream.fromList [1..10]
589−-- 40
590--
660--
591−-- /Pre-release/
661+-- @
662+-- > Stream.prune isSpace (Stream.fromList " hello world! ")
663+-- "hello world!"
664+--
665+-- @
666+--
667+-- Space: @O(1)@
668+--
669+-- /Unimplemented/
592−{-# INLINE foldFilter #-}
670+{-# INLINE prune #-}
593−foldFilter :: Monad m => Fold m a (Maybe b) -> Fold m b c -> Fold m a c
671+prune ::
594−foldFilter f1 f2 = many f1 (catMaybes f2)
672+ -- (Monad m, Eq a) =>
673+ (a -> Bool) -> Fold m a (Maybe a)
674+prune = error "Not implemented yet!"
675+
676+-- | Emit only repeated elements, once.
677+--
678+-- /Unimplemented/
679+repeated :: -- (Monad m, Eq a) =>
680+ Fold m a (Maybe a)
681+repeated = error "Not implemented yet!"
595
682
596-- | Used as a scan. Returns 'Just' for the first occurrence of an element,
683-- | Used as a scan. Returns 'Just' for the first occurrence of an element,
597-- returns 'Nothing' for any other occurrences.
684-- returns 'Nothing' for any other occurrences.
@@ -960,6 +1047,9 @@ rollingHash = rollingHashWithSalt defaultSalt
960rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64
1047rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64
961rollingHashFirstN n = take n rollingHash
1048rollingHashFirstN n = take n rollingHash
962
1049
1050+-- XXX Compare this with the implementation in Fold.Window, preferrably use the
1051+-- latter if performance is good.
1052+
963-- | Apply a function on every two successive elements of a stream. The first
1053-- | Apply a function on every two successive elements of a stream. The first
964-- argument of the map function is the previous element and the second argument
1054-- argument of the map function is the previous element and the second argument
965-- is the current element. When processing the very first element in the
1055-- is the current element. When processing the very first element in the
@@ -983,6 +1073,10 @@ rollingMapM f = Fold step initial extract
983
1073
984 extract = return . snd
1074 extract = return . snd
985
1075
1076+{-# INLINE rollingMap #-}
1077+rollingMap :: Monad m => (Maybe a -> a -> b) -> Fold m a b
1078+rollingMap f = rollingMapM (\x y -> return $ f x y)
1079+
986------------------------------------------------------------------------------
1080------------------------------------------------------------------------------
987-- Monoidal left folds
1081-- Monoidal left folds
988------------------------------------------------------------------------------
1082------------------------------------------------------------------------------
@@ -1122,6 +1216,17 @@ index = genericIndex
1122maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)
1216maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)
1123maybe f = foldt' (const (Done . f)) (Partial Nothing) id
1217maybe f = foldt' (const (Done . f)) (Partial Nothing) id
1124
1218
1219+-- | Test if the next element satisfies the supplied predicate.
1220+--
1221+-- /Pre-release/
1222+{-# INLINE satisfy #-}
1223+satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1224+satisfy f = Fold step (return $ Partial ()) (const (return Nothing))
1225+
1226+ where
1227+
1228+ step () a = return $ Done $ if f a then Just a else Nothing
1229+
1125-- Naming notes:
1230-- Naming notes:
1126--
1231--
1127-- "head" and "next" are two alternative names for the same API. head sounds
1232-- "head" and "next" are two alternative names for the same API. head sounds
@@ -1227,6 +1332,7 @@ findIndex predicate = foldt' step (Partial 0) (const Nothing)
1227{-# INLINE findIndices #-}
1332{-# INLINE findIndices #-}
1228findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
1333findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
1229findIndices predicate =
1334findIndices predicate =
1335+ -- XXX implement by combining indexing and filtering scans
1230 fmap (either (const Nothing) Just) $ foldl' step (Left (-1))
1336 fmap (either (const Nothing) Just) $ foldl' step (Left (-1))
1231
1337
1232 where
1338 where
@@ -1236,6 +1342,15 @@ findIndices predicate =
1236 then Right (either id id i + 1)
1342 then Right (either id id i + 1)
1237 else Left (either id id i + 1)
1343 else Left (either id id i + 1)
1238
1344
1345+-- | Find all the indices where the value of the element in the stream is equal
1346+-- to the given value.
1347+--
1348+-- >>> elemIndices a = Fold.findIndices (== a)
1349+--
1350+{-# INLINE elemIndices #-}
1351+elemIndices :: (Monad m, Eq a) => a -> Fold m a (Maybe Int)
1352+elemIndices a = findIndices (== a)
1353+
1239-- | Returns the first index where a given value is found in the stream.
1354-- | Returns the first index where a given value is found in the stream.
1240--
1355--
1241-- > elemIndex a = Fold.findIndex (== a)
1356-- > elemIndex a = Fold.findIndex (== a)
@@ -1393,6 +1508,73 @@ splitAt n fld = serialWith (,) (take n fld)
1393-- Binary APIs
1508-- Binary APIs
1394------------------------------------------------------------------------------
1509------------------------------------------------------------------------------
1395
1510
1511+{-# INLINE takingEndByM #-}
1512+takingEndByM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1513+takingEndByM p = Fold step initial (return . toMaybe)
1514+
1515+ where
1516+
1517+ initial = return $ Partial Nothing'
1518+
1519+ step _ a = do
1520+ r <- p a
1521+ return
1522+ $ if r
1523+ then Done $ Just a
1524+ else Partial $ Just' a
1525+
1526+-- |
1527+--
1528+-- >>> takingEndBy p = Fold.takingEndByM (return . p)
1529+--
1530+{-# INLINE takingEndBy #-}
1531+takingEndBy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1532+takingEndBy p = takingEndByM (return . p)
1533+
1534+{-# INLINE takingEndByM_ #-}
1535+takingEndByM_ :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1536+takingEndByM_ p = Fold step initial (return . toMaybe)
1537+
1538+ where
1539+
1540+ initial = return $ Partial Nothing'
1541+
1542+ step _ a = do
1543+ r <- p a
1544+ return
1545+ $ if r
1546+ then Done Nothing
1547+ else Partial $ Just' a
1548+
1549+-- |
1550+--
1551+-- >>> takingEndBy_ p = Fold.takingEndByM_ (return . p)
1552+--
1553+{-# INLINE takingEndBy_ #-}
1554+takingEndBy_ :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1555+takingEndBy_ p = takingEndByM_ (return . p)
1556+
1557+{-# INLINE droppingWhileM #-}
1558+droppingWhileM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1559+droppingWhileM p = Fold step initial (return . toMaybe)
1560+
1561+ where
1562+
1563+ initial = return $ Partial Nothing'
1564+
1565+ step Nothing' a = do
1566+ r <- p a
1567+ return
1568+ $ Partial
1569+ $ if r
1570+ then Nothing'
1571+ else Just' a
1572+ step _ a = return $ Partial $ Just' a
1573+
1574+{-# INLINE droppingWhile #-}
1575+droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1576+droppingWhile p = droppingWhileM (return . p)
1577+
1396-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1578-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1397-- S.splitOn in favor of the fold.
1579-- S.splitOn in favor of the fold.
1398--
1580--
@@ -1414,6 +1596,7 @@ splitAt n fld = serialWith (,) (take n fld)
1414-- @since 0.8.0
1596-- @since 0.8.0
1415{-# INLINE takeEndBy_ #-}
1597{-# INLINE takeEndBy_ #-}
1416takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1598takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1599+-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)
1417takeEndBy_ predicate (Fold fstep finitial fextract) =
1600takeEndBy_ predicate (Fold fstep finitial fextract) =
1418 Fold step finitial fextract
1601 Fold step finitial fextract
1419
1602
@@ -1441,6 +1624,7 @@ takeEndBy_ predicate (Fold fstep finitial fextract) =
1441-- @since 0.8.0
1624-- @since 0.8.0
1442{-# INLINE takeEndBy #-}
1625{-# INLINE takeEndBy #-}
1443takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1626takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1627+-- takeEndBy predicate = scanMaybe (takingEndBy predicate)
1444takeEndBy predicate (Fold fstep finitial fextract) =
1628takeEndBy predicate (Fold fstep finitial fextract) =
1445 Fold step finitial fextract
1629 Fold step finitial fextract
1446
1630
@@ -2469,11 +2653,30 @@ zip = zipWithM (curry return)
2469
2653
2470-- | Pair each element of a fold input with its index, starting from index 0.
2654-- | Pair each element of a fold input with its index, starting from index 0.
2471--
2655--
2472−-- /Unimplemented/
2656+{-# INLINE indexingWith #-}
2657+indexingWith :: Monad m => Int -> (Int -> Int) -> Fold m a (Maybe (Int, a))
2658+indexingWith i f = fmap toMaybe $ foldl' step initial
2659+
2660+ where
2661+
2662+ initial = Nothing'
2663+
2664+ step Nothing' a = Just' (i, a)
2665+ step (Just' (n, _)) a = Just' (f n, a)
2666+
2667+{-# INLINE indexing #-}
2668+indexing :: Monad m => Fold m a (Maybe (Int, a))
2669+indexing = indexingWith 0 (+ 1)
2670+
2671+{-# INLINE indexingRev #-}
2672+indexingRev :: Monad m => Int -> Fold m a (Maybe (Int, a))
2673+indexingRev n = indexingWith n (subtract 1)
2674+
2675+-- | Pair each element of a fold input with its index, starting from index 0.
2676+--
2473{-# INLINE indexed #-}
2677{-# INLINE indexed #-}
2474−indexed :: -- forall m a b. Monad m =>
2475− Fold m (Int, a) b -> Fold m a b
2678+indexed :: Monad m => Fold m (Int, a) b -> Fold m a b
2476−indexed = undefined -- zip (Stream.enumerateFrom 0 :: Stream m Int)
2679+indexed = scanMaybe indexing
2477
2680
2478-- | Change the predicate function of a Fold from @a -> b@ to accept an
2681-- | Change the predicate function of a Fold from @a -> b@ to accept an
2479-- additional state input @(s, a) -> b@. Convenient to filter with an
2682-- additional state input @(s, a) -> b@. Convenient to filter with an
@@ -2493,10 +2696,12 @@ with ::
2493 -> (((s, a) -> c) -> Fold m a b -> Fold m a b)
2696 -> (((s, a) -> c) -> Fold m a b -> Fold m a b)
2494with f comb g = f . comb g . lmap snd
2697with f comb g = f . comb g . lmap snd
2495
2698
2699+-- XXX Implement as a filter
2700+-- sampleFromthen :: Monad m => Int -> Int -> Fold m a (Maybe a)
2701+
2496-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
2702-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
2497-- then every element at strides of @stride@.
2703-- then every element at strides of @stride@.
2498--
2704--
2499−-- /Unimplemented/
2500{-# INLINE sampleFromthen #-}
2705{-# INLINE sampleFromthen #-}
2501sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b
2706sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b
2502sampleFromthen offset size =
2707sampleFromthen offset size =
core/src/Streamly/Internal/Data/Fold/Type.hs+151−17
@@ -12,9 +12,22 @@
12-- We can classify stream consumers in the following categories in order of
12-- We can classify stream consumers in the following categories in order of
13-- increasing complexity and power:
13-- increasing complexity and power:
14--
14--
15+-- * Accumulators: Tee/Zip is simple, cannot be appended, good for scanning.
16+-- * Terminating folds: Tee/Zip varies based on termination, can be appended,
17+-- good for scanning, nesting (many) is easy.
18+-- * Non-failing (backtracking only) parsers: cannot be used as scans because
19+-- of backtracking, nesting is complicated because of backtracking, appending
20+-- is efficient because of no Alternative, Alternative does not make sense
21+-- because it cannot fail.
22+-- * Parsers: Alternative on failure, appending is not as efficient because of
23+-- buffering for Alternative.
24+--
25+-- First two are represented by the 'Fold' type and the last two by the
26+-- 'Parser' type.
27+--
15−-- == Folds that never terminate
28+-- == Folds that never terminate (Accumulators)
16−--
29+--
17−-- An Accumulator is the simplest type of fold, it never fails and never
30+-- An @Accumulator@ is the simplest type of fold, it never fails and never
18-- terminates. It can always accept more inputs (never terminates) and the
31-- terminates. It can always accept more inputs (never terminates) and the
19-- accumulator is always valid. For example 'Streamly.Internal.Data.Fold.sum'.
32-- accumulator is always valid. For example 'Streamly.Internal.Data.Fold.sum'.
20-- Traditional Haskell left folds like 'foldl' are accumulators.
33-- Traditional Haskell left folds like 'foldl' are accumulators.
@@ -40,7 +53,7 @@
40--
53--
41-- == Folds that terminate after one or more input
54-- == Folds that terminate after one or more input
42--
55--
43−-- Terminating folds are accumulators that can terminate, like accumulators
56+-- @Terminating folds@ are accumulators that can terminate, like accumulators
44-- they do not fail. Once a fold terminates it no longer accepts any more
57-- they do not fail. Once a fold terminates it no longer accepts any more
45-- inputs. Terminating folds can be appended, the next fold can be
58-- inputs. Terminating folds can be appended, the next fold can be
46-- applied after the first one terminates. Because they cannot fail, they do
59-- applied after the first one terminates. Because they cannot fail, they do
@@ -130,7 +143,9 @@
130-- wordBy
143-- wordBy
131-- @
144-- @
132--
145--
133−-- However, it creates several complications.
146+-- However, it creates several complications. The most important one is that we
147+-- cannot use such folds for scanning. We cannot backtrack after producing an
148+-- output in a scan.
134--
149--
135-- === Nested backtracking
150-- === Nested backtracking
136--
151--
@@ -349,17 +364,22 @@ module Streamly.Internal.Data.Fold.Type
349 -- ** Mapping Input
364 -- ** Mapping Input
350 , lmap
365 , lmap
351 , lmapM
366 , lmapM
367+ , postscan
352
368
353 -- ** Filtering
369 -- ** Filtering
370+ , catMaybes
371+ , scanMaybe
354 , filter
372 , filter
373+ , filtering
355 , filterM
374 , filterM
356− , catMaybes
357 , lefts
375 , lefts
358 , rights
376 , rights
359 , both
377 , both
360
378
361 -- ** Trimming
379 -- ** Trimming
362 , take
380 , take
381+ , taking
382+ , dropping
363
383
364 -- ** Sequential application
384 -- ** Sequential application
365 , serialWith -- rename to "append"
385 , serialWith -- rename to "append"
@@ -395,10 +415,10 @@ module Streamly.Internal.Data.Fold.Type
395where
415where
396
416
397#include "inline.hs"
417#include "inline.hs"
418+
398import Control.Monad ((>=>))
419import Control.Monad ((>=>))
399import Data.Bifunctor (Bifunctor(..))
420import Data.Bifunctor (Bifunctor(..))
400import Data.Either (fromLeft, fromRight, isLeft, isRight)
421import Data.Either (fromLeft, fromRight, isLeft, isRight)
401−import Data.Maybe (isJust, fromJust)
402import Fusion.Plugin.Types (Fuse(..))
422import Fusion.Plugin.Types (Fuse(..))
403import Streamly.Internal.Data.Fold.Step (Step(..), mapMStep, chainStepM)
423import Streamly.Internal.Data.Fold.Step (Step(..), mapMStep, chainStepM)
404import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
424import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
@@ -413,6 +433,7 @@ import Prelude hiding (concatMap, filter, foldr, map, take)
413-- >>> :m
433-- >>> :m
414-- >>> :set -XFlexibleContexts
434-- >>> :set -XFlexibleContexts
415-- >>> :set -package streamly
435-- >>> :set -package streamly
436+-- >>> import Data.Maybe (fromJust, isJust)
416-- >>> import Streamly.Data.Fold (Fold)
437-- >>> import Streamly.Data.Fold (Fold)
417-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
438-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
418-- >>> import qualified Data.Foldable as Foldable
439-- >>> import qualified Data.Foldable as Foldable
@@ -602,7 +623,7 @@ foldt' step initial extract =
602-- | Make a terminating fold with an effectful step function and initial state,
623-- | Make a terminating fold with an effectful step function and initial state,
603-- and a state extraction function.
624-- and a state extraction function.
604--
625--
605−-- > mkFoldM = Fold
626+-- >>> foldtM' = Fold.Fold
606--
627--
607-- We can just use 'Fold' but it is provided for completeness.
628-- We can just use 'Fold' but it is provided for completeness.
608--
629--
@@ -1110,26 +1131,113 @@ lmapM f (Fold step begin done) = Fold step' begin done
1110 where
1131 where
1111 step' x a = f a >>= step x
1132 step' x a = f a >>= step x
1112
1133
1134+-- | Postscan the input of a 'Fold' to change it in a stateful manner using
1135+-- another 'Fold'.
1136+--
1137+-- @postscan scanner collector@
1138+--
1139+-- /Pre-release/
1140+{-# INLINE postscan #-}
1141+postscan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
1142+postscan (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
1143+ Fold step initial extract
1144+
1145+ where
1146+
1147+ {-# INLINE runStep #-}
1148+ runStep actionL sR = do
1149+ rL <- actionL
1150+ case rL of
1151+ Done bL -> do
1152+ rR <- stepR sR bL
1153+ case rR of
1154+ Partial sR1 -> Done <$> extractR sR1
1155+ Done bR -> return $ Done bR
1156+ Partial sL -> do
1157+ !b <- extractL sL
1158+ rR <- stepR sR b
1159+ return
1160+ $ case rR of
1161+ Partial sR1 -> Partial (sL, sR1)
1162+ Done bR -> Done bR
1163+
1164+ initial = do
1165+ r <- initialR
1166+ rL <- initialL
1167+ case r of
1168+ Partial sR ->
1169+ case rL of
1170+ Done _ -> Done <$> extractR sR
1171+ Partial sL -> return $ Partial (sL, sR)
1172+ Done b -> return $ Done b
1173+
1174+ step (sL, sR) x = runStep (stepL sL x) sR
1175+
1176+ extract = extractR . snd
1177+
1113------------------------------------------------------------------------------
1178------------------------------------------------------------------------------
1114-- Filtering
1179-- Filtering
1115------------------------------------------------------------------------------
1180------------------------------------------------------------------------------
1116
1181
1182+-- | Modify a fold to receive a 'Maybe' input, the 'Just' values are unwrapped
1183+-- and sent to the original fold, 'Nothing' values are discarded.
1184+--
1185+-- >>> catMaybes = Fold.mapMaybe id
1186+-- >>> catMaybes = Fold.filter isJust . Fold.lmap fromJust
1187+--
1188+-- @since 0.8.0
1189+{-# INLINE_NORMAL catMaybes #-}
1190+catMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b
1191+catMaybes (Fold step initial extract) = Fold step1 initial extract
1192+
1193+ where
1194+
1195+ step1 s a =
1196+ case a of
1197+ Nothing -> return $ Partial s
1198+ Just x -> step s x
1199+
1200+-- | Use a 'Maybe' returning fold as a filtering scan.
1201+--
1202+-- >>> scanMaybe p f = Fold.postscan p (Fold.catMaybes f)
1203+--
1204+-- /Pre-release/
1205+{-# INLINE scanMaybe #-}
1206+scanMaybe :: Monad m => Fold m a (Maybe b) -> Fold m b c -> Fold m a c
1207+scanMaybe f1 f2 = postscan f1 (catMaybes f2)
1208+
1209+-- | A scanning fold for filtering elements based on a predicate.
1210+--
1211+{-# INLINE filtering #-}
1212+filtering :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1213+filtering f = foldl' step Nothing
1214+
1215+ where
1216+
1217+ step _ a = if f a then Just a else Nothing
1218+
1117-- | Include only those elements that pass a predicate.
1219-- | Include only those elements that pass a predicate.
1118--
1220--
1119-- >>> Stream.fold (Fold.filter (> 5) Fold.sum) $ Stream.fromList [1..10]
1221-- >>> Stream.fold (Fold.filter (> 5) Fold.sum) $ Stream.fromList [1..10]
1120-- 40
1222-- 40
1121--
1223--
1224+-- >>> filter p = Fold.scanMaybe (Fold.filtering p)
1122−-- > filter f = Fold.filterM (return . f)
1225+-- >>> filter p = Fold.filterM (return . p)
1226+-- >>> filter p = Fold.mapMaybe (\x -> if p x then Just x else Nothing)
1123--
1227--
1124-- @since 0.8.0
1228-- @since 0.8.0
1125{-# INLINE filter #-}
1229{-# INLINE filter #-}
1126filter :: Monad m => (a -> Bool) -> Fold m a r -> Fold m a r
1230filter :: Monad m => (a -> Bool) -> Fold m a r -> Fold m a r
1231+-- filter p = scanMaybe (filtering p)
1127filter f (Fold step begin done) = Fold step' begin done
1232filter f (Fold step begin done) = Fold step' begin done
1128 where
1233 where
1129 step' x a = if f a then step x a else return $ Partial x
1234 step' x a = if f a then step x a else return $ Partial x
1130
1235
1131-- | Like 'filter' but with a monadic predicate.
1236-- | Like 'filter' but with a monadic predicate.
1132--
1237--
1238+-- >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing
1239+-- >>> filterM p = Fold.mapMaybeM (f p)
1240+--
1133-- @since 0.8.0
1241-- @since 0.8.0
1134{-# INLINE filterM #-}
1242{-# INLINE filterM #-}
1135filterM :: Monad m => (a -> m Bool) -> Fold m a r -> Fold m a r
1243filterM :: Monad m => (a -> m Bool) -> Fold m a r -> Fold m a r
@@ -1139,14 +1247,6 @@ filterM f (Fold step begin done) = Fold step' begin done
1139 use <- f a
1247 use <- f a
1140 if use then step x a else return $ Partial x
1248 if use then step x a else return $ Partial x
1141
1249
1142−-- | Modify a fold to receive a 'Maybe' input, the 'Just' values are unwrapped
1143−-- and sent to the original fold, 'Nothing' values are discarded.
1144−--
1145−-- @since 0.8.0
1146−{-# INLINE_NORMAL catMaybes #-}
1147−catMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b
1148−catMaybes = filter isJust . lmap fromJust
1149−
1150------------------------------------------------------------------------------
1250------------------------------------------------------------------------------
1151-- Either streams
1251-- Either streams
1152------------------------------------------------------------------------------
1252------------------------------------------------------------------------------
@@ -1184,6 +1284,39 @@ both = lmap (either id id)
1184{-# ANN type Tuple'Fused Fuse #-}
1284{-# ANN type Tuple'Fused Fuse #-}
1185data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
1285data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
1186
1286
1287+{-# INLINE taking #-}
1288+taking :: Monad m => Int -> Fold m a (Maybe a)
1289+taking n = foldt' step initial extract
1290+
1291+ where
1292+
1293+ initial =
1294+ if n <= 0
1295+ then Done Nothing
1296+ else Partial (Tuple'Fused n Nothing)
1297+
1298+ step (Tuple'Fused i _) a =
1299+ if i > 1
1300+ then Partial (Tuple'Fused (i - 1) (Just a))
1301+ else Done (Just a)
1302+
1303+ extract (Tuple'Fused _ r) = r
1304+
1305+{-# INLINE dropping #-}
1306+dropping :: Monad m => Int -> Fold m a (Maybe a)
1307+dropping n = foldt' step initial extract
1308+
1309+ where
1310+
1311+ initial = Partial (Tuple'Fused n Nothing)
1312+
1313+ step (Tuple'Fused i _) a =
1314+ if i > 0
1315+ then Partial (Tuple'Fused (i - 1) Nothing)
1316+ else Partial (Tuple'Fused i (Just a))
1317+
1318+ extract (Tuple'Fused _ r) = r
1319+
1187-- | Take at most @n@ input elements and fold them using the supplied fold. A
1320-- | Take at most @n@ input elements and fold them using the supplied fold. A
1188-- negative count is treated as 0.
1321-- negative count is treated as 0.
1189--
1322--
@@ -1193,6 +1326,7 @@ data Tuple'Fused a b = Tuple'Fused !a !b deriving Show
1193-- @since 0.8.0
1326-- @since 0.8.0
1194{-# INLINE take #-}
1327{-# INLINE take #-}
1195take :: Monad m => Int -> Fold m a b -> Fold m a b
1328take :: Monad m => Int -> Fold m a b -> Fold m a b
1329+-- take n = scanMaybe (taking n)
1196take n (Fold fstep finitial fextract) = Fold step initial extract
1330take n (Fold fstep finitial fextract) = Fold step initial extract
1197
1331
1198 where
1332 where
core/src/Streamly/Internal/Data/Fold/Window.hs+37
@@ -18,6 +18,9 @@
18-- For more advanced statistical measures see the @streamly-statistics@
18-- For more advanced statistical measures see the @streamly-statistics@
19-- package.
19-- package.
20
20
21+-- XXX A window fold can be driven either using the Ring.slidingWindow
22+-- combinator or by zipping nthLast fold and last fold.
23+
21module Streamly.Internal.Data.Fold.Window
24module Streamly.Internal.Data.Fold.Window
22 (
25 (
23 -- * Incremental Folds
26 -- * Incremental Folds
@@ -36,6 +39,9 @@ module Streamly.Internal.Data.Fold.Window
36 lmap
39 lmap
37 , cumulative
40 , cumulative
38
41
42+ , rollingMap
43+ , rollingMapM
44+
39 -- ** Sums
45 -- ** Sums
40 , length
46 , length
41 , sum
47 , sum
@@ -98,6 +104,37 @@ lmap f = Fold.lmap (bimap f (f <$>))
98cumulative :: Fold m (a, Maybe a) b -> Fold m a b
104cumulative :: Fold m (a, Maybe a) b -> Fold m a b
99cumulative = Fold.lmap (, Nothing)
105cumulative = Fold.lmap (, Nothing)
100
106
107+-- XXX Exchange the first two arguments of rollingMap or exchange the order in
108+-- the fold input tuple.
109+
110+-- | Apply an effectful function on the latest and the oldest element of the
111+-- window.
112+{-# INLINE rollingMapM #-}
113+rollingMapM :: Monad m =>
114+ (Maybe a -> a -> m (Maybe b)) -> Fold m (a, Maybe a) (Maybe b)
115+rollingMapM f = Fold.foldlM' f1 initial
116+
117+ where
118+
119+ initial = return Nothing
120+
121+ f1 _ (a, ma) = f ma a
122+
123+-- | Apply a pure function on the latest and the oldest element of the window.
124+--
125+-- >>> rollingMap f = FoldW.rollingMapM (\x y -> return $ f x y)
126+--
127+{-# INLINE rollingMap #-}
128+rollingMap :: Monad m =>
129+ (Maybe a -> a -> Maybe b) -> Fold m (a, Maybe a) (Maybe b)
130+rollingMap f = Fold.foldl' f1 initial
131+
132+ where
133+
134+ initial = Nothing
135+
136+ f1 _ (a, ma) = f ma a
137+
101-------------------------------------------------------------------------------
138-------------------------------------------------------------------------------
102-- Sum
139-- Sum
103-------------------------------------------------------------------------------
140-------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Bottom.hs+31
@@ -28,6 +28,9 @@ module Streamly.Internal.Data.Stream.Bottom
28 , smapM
28 , smapM
29 -- $smapM_Notes
29 -- $smapM_Notes
30 , postscan
30 , postscan
31+ , catMaybes
32+ , scanMaybe
33+
31 , take
34 , take
32 , takeWhile
35 , takeWhile
33 , takeEndBy
36 , takeEndBy
@@ -79,6 +82,7 @@ import Streamly.Internal.Data.Stream.Type
79-- >>> import Control.Monad (join)
82-- >>> import Control.Monad (join)
80-- >>> import Control.Monad.Trans.Class (lift)
83-- >>> import Control.Monad.Trans.Class (lift)
81-- >>> import Data.Function (fix, (&))
84-- >>> import Data.Function (fix, (&))
85+-- >>> import Data.Maybe (fromJust, isJust)
82-- >>> import Data.Semigroup (cycle1)
86-- >>> import Data.Semigroup (cycle1)
83-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
87-- >>> import Prelude hiding (take, takeWhile, drop, reverse)
84-- >>> import System.IO.Unsafe (unsafePerformIO)
88-- >>> import System.IO.Unsafe (unsafePerformIO)
@@ -305,6 +309,28 @@ smapM step initial stream =
305 (fmap (,undefined) initial)
309 (fmap (,undefined) initial)
306 in fmap snd $ postscan f stream
310 in fmap snd $ postscan f stream
307
311
312+-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
313+--
314+-- >>> catMaybes = Stream.mapMaybe id
315+-- >>> catMaybes = fmap fromJust . Stream.filter isJust
316+--
317+-- /Pre-release/
318+--
319+{-# INLINE catMaybes #-}
320+catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
321+-- catMaybes = fmap fromJust . filter isJust
322+catMaybes = fromStreamD . D.catMaybes . toStreamD
323+
324+-- | Use a filtering fold on a stream.
325+--
326+-- >>> scanMaybe f = Stream.catMaybes . Stream.postscan f
327+--
328+-- /Pre-release/
329+--
330+{-# INLINE scanMaybe #-}
331+scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
332+scanMaybe p = catMaybes . postscan p
333+
308------------------------------------------------------------------------------
334------------------------------------------------------------------------------
309-- Transformation - Trimming
335-- Transformation - Trimming
310------------------------------------------------------------------------------
336------------------------------------------------------------------------------
@@ -313,22 +339,26 @@ smapM step initial stream =
313--
339--
314{-# INLINE take #-}
340{-# INLINE take #-}
315take :: Monad m => Int -> Stream m a -> Stream m a
341take :: Monad m => Int -> Stream m a -> Stream m a
342+-- take n = scanMaybe (Fold.taking n)
316take n m = fromStreamD $ D.take n $ toStreamD m
343take n m = fromStreamD $ D.take n $ toStreamD m
317
344
318-- | End the stream as soon as the predicate fails on an element.
345-- | End the stream as soon as the predicate fails on an element.
319--
346--
320{-# INLINE takeWhile #-}
347{-# INLINE takeWhile #-}
321takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
348takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
349+-- takeWhile p = scanMaybe (Fold.takingEndBy_ (not . p))
322takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
350takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m
323
351
324{-# INLINE takeEndBy #-}
352{-# INLINE takeEndBy #-}
325takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
353takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
354+-- takeEndBy p = scanMaybe (Fold.takingEndBy p)
326takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
355takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m
327
356
328-- | Discard first 'n' elements from the stream and take the rest.
357-- | Discard first 'n' elements from the stream and take the rest.
329--
358--
330{-# INLINE drop #-}
359{-# INLINE drop #-}
331drop :: Monad m => Int -> Stream m a -> Stream m a
360drop :: Monad m => Int -> Stream m a -> Stream m a
361+-- drop n = scanMaybe (Fold.dropping n)
332drop n m = fromStreamD $ D.drop n $ toStreamD m
362drop n m = fromStreamD $ D.drop n $ toStreamD m
333
363
334------------------------------------------------------------------------------
364------------------------------------------------------------------------------
@@ -342,6 +372,7 @@ drop n m = fromStreamD $ D.drop n $ toStreamD m
342--
372--
343{-# INLINE findIndices #-}
373{-# INLINE findIndices #-}
344findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
374findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int
375+-- findIndices p = scanMaybe (Fold.findIndices p)
345findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
376findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
346
377
347------------------------------------------------------------------------------
378------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+26−16
@@ -68,9 +68,9 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
68
68
69 -- * Filtering
69 -- * Filtering
70 -- | Produce a subset of the stream.
70 -- | Produce a subset of the stream.
71+ , scanMaybe
71 , filter
72 , filter
72 , filterM
73 , filterM
73− , foldFilter
74 , deleteBy
74 , deleteBy
75 , uniq
75 , uniq
76 , nub
76 , nub
@@ -120,6 +120,7 @@ module Streamly.Internal.Data.Stream.StreamD.Transform
120 -- * Maybe Streams
120 -- * Maybe Streams
121 , mapMaybe
121 , mapMaybe
122 , mapMaybeM
122 , mapMaybeM
123+ , catMaybes
123 )
124 )
124where
125where
125
126
@@ -705,19 +706,6 @@ scanl1' f = scanl1M' (\x y -> return (f x y))
705-- Filtering
706-- Filtering
706-------------------------------------------------------------------------------
707-------------------------------------------------------------------------------
707
708
708−-- XXX nested foldMany does not fuse, therefore, this may not be very useful as
709−-- a filter unless that is fixed.
710−--
711−-- | Use a filtering fold on a stream.
712−--
713−-- > Stream.sum $ Stream.foldFilter (Fold.satisfy (> 5)) $ Stream.fromList [1..10]
714−-- 40
715−--
716−-- /Pre-release/
717−{-# INLINE foldFilter #-}
718−foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
719−foldFilter f = catMaybes . foldMany f
720−
721-- Adapted from the vector package
709-- Adapted from the vector package
722{-# INLINE_NORMAL filterM #-}
710{-# INLINE_NORMAL filterM #-}
723filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
711filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
@@ -1259,5 +1247,27 @@ mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b
1259mapMaybeM f = fmap fromJust . filter isJust . mapM f
1247mapMaybeM f = fmap fromJust . filter isJust . mapM f
1260
1248
1261{-# INLINE catMaybes #-}
1249{-# INLINE catMaybes #-}
1262−catMaybes :: (Monad m) => Stream m (Maybe a) -> Stream m a
1250+catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
1263−catMaybes = fmap fromJust . filter isJust
1251+-- catMaybes = fmap fromJust . filter isJust
1252+catMaybes (Stream step state) = Stream step1 state
1253+
1254+ where
1255+
1256+ {-# INLINE_LATE step1 #-}
1257+ step1 gst st = do
1258+ r <- step (adaptState gst) st
1259+ case r of
1260+ Yield x s -> do
1261+ return
1262+ $ case x of
1263+ Just a -> Yield a s
1264+ Nothing -> Skip s
1265+ Skip s -> return $ Skip s
1266+ Stop -> return Stop
1267+
1268+-- | Use a filtering fold on a stream.
1269+--
1270+-- /Pre-release/
1271+{-# INLINE scanMaybe #-}
1272+scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
1273+scanMaybe f = catMaybes . postscanOnce f
core/src/Streamly/Internal/Data/Stream/Transform.hs+60−59
@@ -38,14 +38,21 @@ module Streamly.Internal.Data.Stream.Transform
38 -- the elements. We can use a concatMap and scan for filtering but these
38 -- the elements. We can use a concatMap and scan for filtering but these
39 -- combinators are more efficient and convenient.
39 -- combinators are more efficient and convenient.
40
40
41+ -- mapMaybeM is a general filtering combinator as we can map the stream to
42+ -- Just/Nothing using any stateful fold and then use this to filter out.
43+ , mapMaybeM
44+ , mapMaybe
45+ , catMaybes
46+ , scanMaybe
47+
41 , with
48 , with
42 , deleteBy
49 , deleteBy
43 , filter
50 , filter
44 , filterM
51 , filterM
45− , foldFilter
52+
53+ -- Stateful/scanning filters
46 , uniq
54 , uniq
47 , uniqBy
55 , uniqBy
48− , nubBy
49 , prune
56 , prune
50 , repeated
57 , repeated
51
58
@@ -59,6 +66,22 @@ module Streamly.Internal.Data.Stream.Transform
59 , dropWhile
66 , dropWhile
60 , dropWhileM
67 , dropWhileM
61
68
69+ -- * Position Indexing
70+ , indexed
71+ , indexedR
72+
73+ -- * Searching
74+ , findIndices -- XXX indicesBy
75+ , elemIndices -- XXX indicesOf
76+
77+ -- * Rolling map
78+ -- | Map using the previous element.
79+ , rollingMapM
80+ , rollingMap
81+ , rollingMap2
82+
83+ -- Merge
84+
62 -- * Inserting Elements
85 -- * Inserting Elements
63 -- | Produce a superset of the stream. This is the opposite of
86 -- | Produce a superset of the stream. This is the opposite of
64 -- filtering/sampling. We can always use concatMap and scan for inserting
87 -- filtering/sampling. We can always use concatMap and scan for inserting
@@ -89,31 +112,13 @@ module Streamly.Internal.Data.Stream.Transform
89 -- , intersperseByBefore
112 -- , intersperseByBefore
90 -- , intersperseByAfter
113 -- , intersperseByAfter
91
114
115+ -- Fold and Unfold, Buffering
116+
92 -- * Reordering
117 -- * Reordering
93 , reverse
118 , reverse
94 , reverse'
119 , reverse'
95 , reassembleBy
120 , reassembleBy
96
121
97− -- * Position Indexing
98− , indexed
99− , indexedR
100−
101− -- * Searching
102− , findIndices -- XXX indicesBy
103− , elemIndices -- XXX indicesOf
104−
105− -- * Rolling map
106− -- | Map using the previous element.
107− , rollingMapM
108− , rollingMap
109− , rollingMap2
110−
111− -- * Maybe Streams
112− -- Move these to Streamly.Data.Maybe.Stream?
113− , catMaybes -- XXX justs (like lefts/rights)
114− , mapMaybe
115− , mapMaybeM
116−
117 -- * Either Streams
122 -- * Either Streams
118 -- Move these to Streamly.Data.Either.Stream?
123 -- Move these to Streamly.Data.Either.Stream?
119 , lefts
124 , lefts
@@ -135,6 +140,7 @@ import Streamly.Internal.Data.Fold.Type (Fold)
135import Streamly.Internal.Data.Pipe (Pipe)
140import Streamly.Internal.Data.Pipe (Pipe)
136
141
137import qualified Streamly.Internal.Data.Fold as FL
142import qualified Streamly.Internal.Data.Fold as FL
143+-- import qualified Streamly.Internal.Data.Fold.Window as Window
138import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
144import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
139import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
145import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
140import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
146import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
@@ -153,12 +159,14 @@ import Prelude hiding
153-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
159-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
154-- >>> import Control.Monad.Trans (lift)
160-- >>> import Control.Monad.Trans (lift)
155-- >>> import Control.Monad.Trans.Identity (runIdentityT)
161-- >>> import Control.Monad.Trans.Identity (runIdentityT)
162+-- >>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
156-- >>> import Data.Function ((&))
163-- >>> import Data.Function ((&))
157-- >>> import Data.Maybe (fromJust, isJust)
164-- >>> import Data.Maybe (fromJust, isJust)
158-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
165-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
159-- >>> import Streamly.Internal.Data.Stream as Stream
166-- >>> import Streamly.Internal.Data.Stream as Stream
160-- >>> import qualified Streamly.Data.Fold as Fold
167-- >>> import qualified Streamly.Data.Fold as Fold
161-- >>> import qualified Streamly.Internal.Data.Fold as Fold
168-- >>> import qualified Streamly.Internal.Data.Fold as Fold
169+-- >>> import qualified Streamly.Internal.Data.Fold.Window as Window
162-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
170-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
163-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
171-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
164--
172--
@@ -432,28 +440,24 @@ with f comb g = fmap snd . comb g . f
432
440
433-- | Include only those elements that pass a predicate.
441-- | Include only those elements that pass a predicate.
434--
442--
443+-- >>> filter p = Stream.filterM (return . p)
444+-- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)
445+-- >>> filter p = Stream.scanMaybe (Fold.filtering p)
446+--
435{-# INLINE filter #-}
447{-# INLINE filter #-}
436filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
448filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
449+-- filter p = scanMaybe (FL.filtering p)
437filter p m = fromStreamD $ D.filter p $ toStreamD m
450filter p m = fromStreamD $ D.filter p $ toStreamD m
438
451
439-- | Same as 'filter' but with a monadic predicate.
452-- | Same as 'filter' but with a monadic predicate.
440--
453--
454+-- >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing
455+-- >>> filterM p = Stream.mapMaybeM (f p)
456+--
441{-# INLINE filterM #-}
457{-# INLINE filterM #-}
442filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
458filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
443filterM p m = fromStreamD $ D.filterM p $ toStreamD m
459filterM p m = fromStreamD $ D.filterM p $ toStreamD m
444
460
445−-- | Use a filtering fold on a stream.
446−--
447−-- >>> input = Stream.fromList [1..10]
448−-- >>> Stream.fold Fold.sum $ Stream.foldFilter (Fold.satisfy (> 5)) input
449−-- 40
450−--
451−-- /Pre-release/
452−--
453−{-# INLINE foldFilter #-}
454−foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
455−foldFilter p = fromStreamD . D.foldFilter p . toStreamD
456−
457-- | Drop repeated elements that are adjacent to each other using the supplied
461-- | Drop repeated elements that are adjacent to each other using the supplied
458-- comparison function.
462-- comparison function.
459--
463--
@@ -475,6 +479,7 @@ foldFilter p = fromStreamD . D.foldFilter p . toStreamD
475{-# INLINE uniqBy #-}
479{-# INLINE uniqBy #-}
476uniqBy :: Monad m =>
480uniqBy :: Monad m =>
477 (a -> a -> Bool) -> Stream m a -> Stream m a
481 (a -> a -> Bool) -> Stream m a -> Stream m a
482+-- uniqBy eq = scanMaybe (FL.uniqBy eq)
478uniqBy eq = catMaybes . rollingMap f
483uniqBy eq = catMaybes . rollingMap f
479
484
480 where
485 where
@@ -486,8 +491,11 @@ uniqBy eq = catMaybes . rollingMap f
486
491
487-- | Drop repeated elements that are adjacent to each other.
492-- | Drop repeated elements that are adjacent to each other.
488--
493--
494+-- >>> uniq = Stream.uniqBy (==)
495+--
489{-# INLINE uniq #-}
496{-# INLINE uniq #-}
490uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
497uniq :: (Eq a, Monad m) => Stream m a -> Stream m a
498+-- uniq = scanMaybe FL.uniq
491uniq = fromStreamD . D.uniq . toStreamD
499uniq = fromStreamD . D.uniq . toStreamD
492
500
493-- | Strip all leading and trailing occurrences of an element passing a
501-- | Strip all leading and trailing occurrences of an element passing a
@@ -524,21 +532,6 @@ repeated :: -- (Monad m, Eq a) =>
524 Stream m a -> Stream m a
532 Stream m a -> Stream m a
525repeated = undefined
533repeated = undefined
526
534
527−-- We can have more efficient implementations for nubOrd and nubInt by using
528−-- Set and IntSet to find/remove duplication. For Hashable we can use a
529−-- hashmap. Use rewrite rules to specialize to more efficient impls.
530−
531−-- | Drop repeated elements anywhere in the stream.
532−--
533−-- /Caution: not scalable for infinite streams/
534−--
535−-- /Unimplemented/
536−--
537−{-# INLINE nubBy #-}
538−nubBy :: -- Monad m =>
539− (a -> a -> Bool) -> Stream m a -> Stream m a
540−nubBy = undefined -- fromStreamD . D.nubBy . toStreamD
541−
542-- | Deletes the first occurrence of the element in the stream that satisfies
535-- | Deletes the first occurrence of the element in the stream that satisfies
543-- the given equality predicate.
536-- the given equality predicate.
544--
537--
@@ -548,6 +541,7 @@ nubBy = undefined -- fromStreamD . D.nubBy . toStreamD
548--
541--
549{-# INLINE deleteBy #-}
542{-# INLINE deleteBy #-}
550deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
543deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a
544+-- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)
551deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
545deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
552
546
553------------------------------------------------------------------------------
547------------------------------------------------------------------------------
@@ -558,6 +552,7 @@ deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)
558--
552--
559{-# INLINE takeWhileM #-}
553{-# INLINE takeWhileM #-}
560takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
554takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
555+-- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))
561takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
556takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
562
557
563-- | Drop elements in the stream as long as the predicate succeeds and then
558-- | Drop elements in the stream as long as the predicate succeeds and then
@@ -565,12 +560,14 @@ takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m
565--
560--
566{-# INLINE dropWhile #-}
561{-# INLINE dropWhile #-}
567dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
562dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
563+-- dropWhile p = scanMaybe (FL.droppingWhile p)
568dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
564dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m
569
565
570-- | Same as 'dropWhile' but with a monadic predicate.
566-- | Same as 'dropWhile' but with a monadic predicate.
571--
567--
572{-# INLINE dropWhileM #-}
568{-# INLINE dropWhileM #-}
573dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
569dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a
570+-- dropWhileM p = scanMaybe (FL.droppingWhileM p)
574dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
571dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m
575
572
576------------------------------------------------------------------------------
573------------------------------------------------------------------------------
@@ -763,6 +760,7 @@ reassembleBy = undefined
763--
760--
764{-# INLINE indexed #-}
761{-# INLINE indexed #-}
765indexed :: Monad m => Stream m a -> Stream m (Int, a)
762indexed :: Monad m => Stream m a -> Stream m (Int, a)
763+-- indexed = scanMaybe FL.indexing
766indexed = fromStreamD . D.indexed . toStreamD
764indexed = fromStreamD . D.indexed . toStreamD
767
765
768-- |
766-- |
@@ -780,6 +778,7 @@ indexed = fromStreamD . D.indexed . toStreamD
780--
778--
781{-# INLINE indexedR #-}
779{-# INLINE indexedR #-}
782indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
780indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)
781+-- indexedR n = scanMaybe (FL.indexingRev n)
783indexedR n = fromStreamD . D.indexedR n . toStreamD
782indexedR n = fromStreamD . D.indexedR n . toStreamD
784
783
785------------------------------------------------------------------------------
784------------------------------------------------------------------------------
@@ -811,6 +810,7 @@ elemIndices a = findIndices (== a)
811--
810--
812{-# INLINE rollingMap #-}
811{-# INLINE rollingMap #-}
813rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
812rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b
813+-- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)
814rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
814rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
815
815
816-- | Like 'rollingMap' but with an effectful map function.
816-- | Like 'rollingMap' but with an effectful map function.
@@ -819,6 +819,7 @@ rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m
819--
819--
820{-# INLINE rollingMapM #-}
820{-# INLINE rollingMapM #-}
821rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
821rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b
822+-- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)
822rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
823rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m
823
824
824-- | Like 'rollingMap' but requires at least two elements in the stream,
825-- | Like 'rollingMap' but requires at least two elements in the stream,
@@ -841,7 +842,7 @@ rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m
841--
842--
842-- Equivalent to:
843-- Equivalent to:
843--
844--
844−-- >>> mapMaybe f = fmap fromJust . Stream.filter isJust . fmap f
845+-- >>> mapMaybe f = Stream.catMaybes . fmap f
845--
846--
846{-# INLINE mapMaybe #-}
847{-# INLINE mapMaybe #-}
847mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
848mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b
@@ -851,27 +852,23 @@ mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m
851--
852--
852-- Equivalent to:
853-- Equivalent to:
853--
854--
854−-- >>> mapMaybeM f = fmap fromJust . Stream.filter isJust . Stream.mapM f
855+-- >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f
856+--
857+-- >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)
855--
858--
856{-# INLINE_EARLY mapMaybeM #-}
859{-# INLINE_EARLY mapMaybeM #-}
857mapMaybeM :: Monad m
860mapMaybeM :: Monad m
858 => (a -> m (Maybe b)) -> Stream m a -> Stream m b
861 => (a -> m (Maybe b)) -> Stream m a -> Stream m b
859mapMaybeM f = fmap fromJust . filter isJust . mapM f
862mapMaybeM f = fmap fromJust . filter isJust . mapM f
860
863
861−-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.
862−--
863−-- /Pre-release/
864−--
865−{-# INLINE catMaybes #-}
866−catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a
867−catMaybes = fmap fromJust . filter isJust
868−
869------------------------------------------------------------------------------
864------------------------------------------------------------------------------
870-- Either streams
865-- Either streams
871------------------------------------------------------------------------------
866------------------------------------------------------------------------------
872
867
873-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
868-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.
874--
869--
870+-- >>> lefts = fmap (fromLeft undefined) . Stream.filter isLeft
871+--
875-- /Pre-release/
872-- /Pre-release/
876--
873--
877{-# INLINE lefts #-}
874{-# INLINE lefts #-}
@@ -880,6 +877,8 @@ lefts = fmap (fromLeft undefined) . filter isLeft
880
877
881-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
878-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.
882--
879--
880+-- >>> rights = fmap (fromRight undefined) . Stream.filter isRight
881+--
883-- /Pre-release/
882-- /Pre-release/
884--
883--
885{-# INLINE rights #-}
884{-# INLINE rights #-}
@@ -889,6 +888,8 @@ rights = fmap (fromRight undefined) . filter isRight
889-- | Remove the either wrapper and flatten both lefts and as well as rights in
888-- | Remove the either wrapper and flatten both lefts and as well as rights in
890-- the output stream.
889-- the output stream.
891--
890--
891+-- >>> both = fmap (either id id)
892+--
892-- /Pre-release/
893-- /Pre-release/
893--
894--
894{-# INLINE both #-}
895{-# INLINE both #-}