on master help sign in
update and reorganize documentation
commit7a3a0733513f744a19c1c3660283476d4a945ac5 authorHarendra Kumar <harendra.kumar@gmail.com> dateTue 13 Aug 2019, 15:19 (7 years ago) parent5d99c94
3 files changed+514−621
src/Streamly/Prelude.hs+427−124 src/Streamly/Prelude/Internal.hs+68−496 src/Streamly/Streams/Prelude.hs+19−1
src/Streamly/Prelude.hs+427−124
@@ -171,12 +171,14 @@ module Streamly.Prelude
171171-- parallel and then combine the results using a monoid.
172172
173173 -- ** Right Folds
174+ -- $rightfolds
174175 , foldrM
175176 -- , foldrS
176177 -- , foldrT
177178 , foldr
178179
179180 -- ** Left Folds
181+ -- $leftfolds
180182 , foldl'
181183 , foldl1'
182184 , foldlM'
@@ -245,6 +247,19 @@ module Streamly.Prelude
245247 , and
246248 , or
247249
250+ -- ** Multi-Stream folds
251+ , eqBy
252+ , cmpBy
253+ , isPrefixOf
254+ -- , isSuffixOf
255+ -- , isInfixOf
256+ , isSubsequenceOf
257+
258+ -- trimming sequences
259+ , stripPrefix
260+ -- , stripSuffix
261+ -- , stripInfix
262+
248263 -- * Transformation
249264
250265 --, transform
@@ -264,7 +279,11 @@ module Streamly.Prelude
264279 , map
265280 , sequence
266281 , mapM
282+
283+ -- ** Special Maps
267284 , mapM_
285+ , trace
286+ , tap
268287
269288 -- ** Scanning
270289 --
@@ -342,12 +361,6 @@ module Streamly.Prelude
342361 -- , lprescanl'
343362 -- , lprescanlM'
344363
345− -- ** Indexing
346− , indexed
347− , indexedR
348− -- , timestamped
349− -- , timestampedR -- timer
350−
351364 -- ** Filtering
352365 -- | Remove some elements from the stream based on a predicate. In
353366 -- imperative terms a filter over a stream corresponds to a loop with a
@@ -356,113 +369,61 @@ module Streamly.Prelude
356369 , filter
357370 , filterM
358371
359− -- ** Stateful Filters
360− , take
361− -- , takeEnd
362− , takeWhile
363− , takeWhileM
364− -- , takeWhileEnd
365− , drop
366− -- , dropEnd
367− , dropWhile
368− , dropWhileM
369− -- , dropWhileEnd
370− -- , dropAround
371− , deleteBy
372− , uniq
373− -- , uniqBy -- by predicate e.g. to remove duplicate "/" in a path
374− -- , uniqOn -- to remove duplicate sequences
375− -- , pruneBy -- dropAround + uniqBy - like words
376−
377372 -- ** Mapping Filters
378373 -- | Mapping along with filtering
379374
380375 , mapMaybe
381376 , mapMaybeM
382377
383− -- ** Scanning Filters
384− -- | Stateful transformation along with filtering
385−
386− , findIndices
387− , elemIndices
388− -- , seqIndices -- search a sequence in the stream
378+ -- ** Deleting Elements
379+ -- | Deleting elements is a special case of de-interleaving streams.
380+ , deleteBy
381+ , uniq
382+ -- , uniqBy -- by predicate e.g. to remove duplicate "/" in a path
383+ -- , uniqOn -- to remove duplicate sequences
384+ -- , pruneBy -- dropAround + uniqBy - like words
389385
390− -- ** Insertion
391− -- | Insertion adds more elements to the stream.
386+ -- ** Inserting Elements
387+ -- | Inserting elements is a special case of interleaving/merging streams.
392388
393389 , insertBy
394390 , intersperseM
395391 , intersperse
396392 -- , insertAfterEach
397393 -- , intersperseBySpan
394+ -- , intersperseByIndices -- using an index function/stream
398395 -- , intersperseByTime
396+ -- , intersperseByEvent
399397
400− -- ** Reordering
401− , reverse
402− -- , reverse'
403−
404− -- * Multi-Stream Operations
405− -- | New streams can be constructed by appending, merging or zipping
406− -- existing streams.
407−
408− -- ** Appending
409− -- | Streams form a 'Semigroup' and a 'Monoid' under the append
410− -- operation.
411− --
412− -- @
413− --
414− -- -------Stream m a------|-------Stream m a------|=>----Stream m a---
415− --
416− -- @
417− --
418− -- @
419− -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
420− -- [1,2,3,4]
421− -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
422− -- [1,2,3,4]
423− -- @
424−
425− -- ** Merging
426− -- | Streams form a commutative semigroup under the merge
427− -- operation.
428− --
429− -- @
430− --
431− -- -------Stream m a------|
432− -- |=>----Stream m a---
433− -- -------Stream m a------|
434− -- @
435− --
436−
437− -- , merge
438− , mergeBy
439− , mergeByM
440− , mergeAsyncBy
441− , mergeAsyncByM
398+ -- -- * Inserting Streams in Streams
399+ -- , interposeBy
400+ -- , intercalate
442401
443− -- ** Zipping
444− -- |
445− -- @
446− --
447− -- -------Stream m a------|
448− -- |=>----Stream m c---
449− -- -------Stream m b------|
450− -- @
451− --
452− , zipWith
453− , zipWithM
454− , zipAsyncWith
455− , zipAsyncWithM
402+ -- ** Indexing
403+ -- | Indexing can be considered as a special type of zipping where we zip a
404+ -- stream with an index stream.
405+ , indexed
406+ , indexedR
407+ -- , timestamped
408+ -- , timestampedR -- timer
456409
457− -- ** Nested Streams
458− -- | Stream operations represent loops in imperative programming, streams
459− -- of streams represent nested loops.
410+ -- ** Reordering Elements
411+ , reverse
412+ -- , reverse'
460413
461− , concatMapM
462− , concatMap
463− , concatMapBy
464− -- , interposeBy
465− -- , intercalate
414+ -- ** Trimming
415+ -- | Take or remove elements from one or both ends of a stream.
416+ , take
417+ -- , takeEnd
418+ , takeWhile
419+ , takeWhileM
420+ -- , takeWhileEnd
421+ , drop
422+ -- , dropEnd
423+ , dropWhile
424+ , dropWhileM
425+ -- , dropWhileEnd
426+ -- , dropAround
466427
467428 -- -- ** Breaking
468429
@@ -480,10 +441,11 @@ module Streamly.Prelude
480441 -- , spanByRolling
481442
482443 -- By sequences
483− -- breakOnSeq
444+ -- breakOnSeq/breakOnArray -- on a fixed sequence
445+ -- breakOnStream -- on a stream
484446
485− -- ** Splitting
486− -- | Streams can be split into segments in space or in time. We use the
447+ -- ** Slicing
448+ -- | Streams can be sliced into segments in space or in time. We use the
487449 -- term @chunk@ to refer to a spatial length of the stream (spatial window)
488450 -- and the term @session@ to refer to a length in time (time window).
489451
@@ -494,26 +456,59 @@ module Streamly.Prelude
494456
495457 -- , groupScan
496458
497− -- -- *** Chunks
498459 , chunksOf
499460 , sessionsOf
500461
501− -- , lchunksOf
502− -- , lsessionsOf
462+ -- ** Searching
463+ -- | Finding the presence or location of an element, a sequence of elements
464+ -- or another stream within a stream.
465+
466+ -- -- ** Searching Elements
467+ , findIndices
468+ , elemIndices
469+
470+ -- -- *** Searching Sequences
471+ -- , seqIndices -- search a sequence in the stream
472+
473+ -- -- *** Searching Multiple Sequences
474+ -- , seqIndices -- search a sequence in the stream
475+
476+ -- -- ** Searching Streams
477+ -- -- | Finding a stream within another stream.
503478
504− -- -- *** Using Element Separators
505− -- On == Dropping the separator
479+ -- ** Splitting
480+ -- | In general we can express splitting in terms of parser combinators.
481+ -- These are some common use functions for convenience and efficiency.
482+ -- While parsers can fail these functions are designed to transform a
483+ -- stream without failure with a predefined behavior for all cases.
484+ --
485+ -- In general, there are three possible ways of combining stream segments
486+ -- with a separator. The separator could be prefixed to each segment,
487+ -- suffixed to each segment, or it could be infixed between segments.
488+ -- 'intersperse' and 'intercalate' operations are examples of infixed
489+ -- combining whereas 'unlines' is an example of suffixed combining. When we
490+ -- split a stream with separators we can split in three different ways,
491+ -- each being an opposite of the three ways of combining.
492+ --
493+ -- Splitting may keep the separator or drop it. Depending on how we split,
494+ -- the separator may be kept attached to the stream segments in prefix or
495+ -- suffix position or as a separate element in infix position. Combinators
496+ -- like 'splitOn' that use @On@ in their names drop the separator and
497+ -- combinators that use 'By' in their names keep the separator. When a
498+ -- segment is missing it is considered as empty, therefore, we never
499+ -- encounter an error in parsing.
500+
501+ -- -- ** Splitting By Elements
506502 , splitOn
507503 , splitOnSuffix
508504 -- , splitOnPrefix
509505
510− -- By == Keeping the separator
511506 -- , splitBy
512507 , splitBySuffix
513508 -- , splitByPrefix
514− , wordsBy -- stripAndCompactBy
509+ , wordsBy -- strip, compact and split
515510
516− -- -- *** Using Sequence Separators
511+ -- -- *** Splitting By Sequences
517512 -- , splitOnSeq
518513 -- , splitOnSuffixSeq
519514 -- , splitOnPrefixSeq
@@ -524,20 +519,21 @@ module Streamly.Prelude
524519 -- , splitBySeqPrefix
525520 -- , wordsBySeq
526521
527− -- Splitting using multiple sequence separators
522+ -- Splitting By Multiple Sequences
528523 -- , splitOnAnySeq
529524 -- , splitOnAnySuffixSeq
530525 -- , splitOnAnyPrefixSeq
531526
527+ -- -- ** Splitting By Streams
528+ -- -- | Splitting a stream using another stream as separator.
529+
532530 -- ** Grouping
531+ -- | Splitting a stream by combining multiple contiguous elements into
532+ -- groups using some criterion.
533533 , groups
534534 , groupsBy
535535 , groupsByRolling
536536
537− -- ** Distributing
538− , trace
539− , tap
540−
541537 {-
542538 -- * Windowed Classification
543539 -- | Split the stream into windows or chunks in space or time. Each window
@@ -572,25 +568,100 @@ module Streamly.Prelude
572568 -- , slidingSessionBuffer
573569 -}
574570
575− -- ** Containers of Streams
571+ -- * Combining Streams
572+ -- | New streams can be constructed by appending, merging or zipping
573+ -- existing streams.
574+
575+ -- ** Appending
576+ -- | Streams form a 'Semigroup' and a 'Monoid' under the append
577+ -- operation. Appending can be considered as a generalization of the `cons`
578+ -- operation to consing a stream to a stream.
579+ --
580+ -- @
581+ --
582+ -- -------Stream m a------|-------Stream m a------|=>----Stream m a---
583+ --
584+ -- @
585+ --
586+ -- @
587+ -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
588+ -- [1,2,3,4]
589+ -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
590+ -- [1,2,3,4]
591+ -- @
592+
593+ -- ** Merging
594+ -- | Streams form a commutative semigroup under the merge
595+ -- operation. Merging can be considered as a generalization of inserting an
596+ -- element in a stream to interleaving a stream with another stream.
597+ --
598+ -- @
599+ --
600+ -- -------Stream m a------|
601+ -- |=>----Stream m a---
602+ -- -------Stream m a------|
603+ -- @
604+ --
605+
606+ -- , merge
607+ , mergeBy
608+ , mergeByM
609+ , mergeAsyncBy
610+ , mergeAsyncByM
611+
612+ -- ** Zipping
613+ -- |
614+ -- @
615+ --
616+ -- -------Stream m a------|
617+ -- |=>----Stream m c---
618+ -- -------Stream m b------|
619+ -- @
620+ --
621+ , zipWith
622+ , zipWithM
623+ , zipAsyncWith
624+ , zipAsyncWithM
625+
626+ -- ** Folding Containers of Streams
576627 -- | These are variants of standard 'Foldable' fold functions that use a
577628 -- polymorphic stream sum operation (e.g. 'async' or 'wSerial') to fold a
578− -- finite container of streams.
629+ -- finite container of streams. Note that these are just special cases of
630+ -- the more general 'concatMapBy' operation.
579631 --
580− , foldWith
581632 , foldMapWith
582633 , forEachWith
634+ , foldWith
583635
584− -- ** Folding
585− , eqBy
586− , cmpBy
587− , isPrefixOf
588− -- , isSuffixOf
589− -- , isInfixOf
590− , isSubsequenceOf
591− , stripPrefix
592− -- , stripSuffix
593− -- , stripInfix
636+ -- ** Folding Streams of Streams
637+ -- | Stream operations like map and filter represent loop processing in
638+ -- imperative programming terms. Similarly, the imperative concept of
639+ -- nested loops are represented by streams of streams. The 'concatMap'
640+ -- operation represents nested looping.
641+ -- A 'concatMap' operation loops over the input stream and then for each
642+ -- element of the input stream generates another stream and then loops over
643+ -- that inner stream as well producing effects and generating a single
644+ -- output stream.
645+ -- The 'Monad' instances of different stream types provide a more
646+ -- convenient way of writing nested loops. Note that the monad bind
647+ -- operation is just @flip concatMap@.
648+ --
649+ -- One dimension loops are just a special case of nested loops. For
650+ -- example, 'concatMap' can degenerate to a simple map operation:
651+ --
652+ -- > map f m = S.concatMap (\x -> S.yield (f x)) m
653+ --
654+ -- Similarly, 'concatMap' can perform filtering by mapping an element to a
655+ -- 'nil' stream:
656+ --
657+ -- > filter p m = S.concatMap (\x -> if p x then S.yield x else S.nil) m
658+ --
659+
660+ -- XXX add stateful concatMapBy
661+ , concatMapBy
662+ --, bindWith
663+ , concatMap
664+ , concatMapM
594665
595666 -- * Exceptions
596667 , before
@@ -623,3 +694,235 @@ import Prelude
623694 scanl, scanl1, repeat, replicate, concatMap, span)
624695
625696import Streamly.Prelude.Internal
697+
698+-- $rightfolds
699+--
700+-- Let's take a closer look at the @foldr@ definition for lists, as given
701+-- earlier:
702+--
703+-- @
704+-- foldr f z (x:xs) = x \`f` foldr f z xs
705+-- @
706+--
707+-- @foldr@ invokes the fold step function @f@ as @f x (foldr f z xs)@. At each
708+-- invocation of @f@, @foldr@ gives us the next element in the input container
709+-- @x@ and a recursive expression @foldr f z xs@ representing the yet unbuilt
710+-- (lazy thunk) part of the output.
711+--
712+-- When @f x xs@ is lazy in @xs@ it can consume the input one element at a time
713+-- in FIFO order to build a lazy output expression. For example,
714+--
715+-- > f x remaining = show x : remaining
716+--
717+-- @take 2 $ foldr f [] (1:2:undefined)@ would consume the input lazily on
718+-- demand, consuming only first two elements and resulting in ["1", "2"]. @f@
719+-- can terminate recursion by not evaluating the @remaining@ part:
720+--
721+-- > f 2 remaining = show 2 : []
722+-- > f x remaining = show x : remaining
723+--
724+-- @f@ would terminate recursion whenever it sees element @2@ in the input.
725+-- Therefore, @take 2 $ foldr f [] (1:2:undefined)@ would work without any
726+-- problem.
727+--
728+-- On the other hand, if @f a b@ is strict in @b@ it would end up consuming the
729+-- whole input right away and expanding the recursive expression @b@ (i.e.
730+-- @foldr f z xs@) fully before it yields an output expression, resulting in
731+-- the following /right associated expression/:
732+--
733+-- @
734+-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
735+-- @
736+--
737+-- For example,
738+--
739+-- > f x remaining = x + remaining
740+--
741+-- With this definition, @foldr f 0 [1..1000]@, would recurse completely until
742+-- it reaches the terminating case @... `f` (1000 `f` 0)@, and then
743+-- start reducing the whole expression from right to left, therefore, consuming
744+-- the input elements in LIFO order. Thus, such an evaluation would require
745+-- memory proportional to the size of input. Try out @foldr (+) 0 (map (\\x ->
746+-- trace (show x) x) [1..10])@.
747+--
748+-- Notice the order of the arguments to the step function @f a b@. It follows
749+-- the order of @a@ and @b@ in the right associative recursive expression
750+-- generated by expanding @a \`f` b@.
751+--
752+-- A right fold is a pull fold, the step function is the puller, it can pull
753+-- more data from the input container by using its second argument in the
754+-- output expression or terminate pulling by not using it. As a corollary:
755+--
756+-- 1. a step function which is lazy in its second argument (usually functions
757+-- or constructors that build a lazy structure e.g. @(:)@) can pull lazily on
758+-- demand.
759+-- 2. a step function strict in its second argument (usually reducers e.g.
760+-- (+)) would end up pulling all of its input and buffer it in memory before
761+-- potentially reducing it.
762+--
763+-- A right fold is suitable for lazy reconstructions e.g. transformation,
764+-- mapping, filtering of /right associated input structures/ (e.g. cons lists).
765+-- Whereas a left fold is suitable for reductions (e.g. summing a stream of
766+-- numbers) of right associated structures. Note that these roles will reverse
767+-- for left associated structures (e.g. snoc lists). Most of our observations
768+-- here assume right associated structures, lists being the canonical example.
769+--
770+-- 1. A lazy FIFO style pull using a right fold allows pulling a potentially
771+-- /infinite/ input stream lazily, perform transformations on it, and
772+-- reconstruct a new structure without having to buffer the whole structure. In
773+-- contrast, a left fold would buffer the entire structure before the
774+-- reconstructed structure can be consumed.
775+-- 2. Even if buffering the entire input structure is ok, we need to keep in
776+-- mind that a right fold reconstructs structures in a FIFO style, whereas a
777+-- left fold reconstructs in a LIFO style, thereby reversing the order of
778+-- elements..
779+-- 3. A right fold has termination control and therefore can terminate early
780+-- without going throught the entire input, a left fold cannot terminate
781+-- without consuming all of its input. For example, a right fold
782+-- implementation of 'or' can terminate as soon as it finds the first 'True'
783+-- element, whereas a left fold would necessarily go through the entire input
784+-- irrespective of that.
785+-- 4. Reduction (e.g. using (+) on a stream of numbers) using a right fold
786+-- occurs in a LIFO style, which means that the entire input gets buffered
787+-- before reduction starts. Whereas with a strict left fold reductions occur
788+-- incrementally in FIFO style. Therefore, a strict left fold is more suitable
789+-- for reductions.
790+--
791+
792+-- $leftfolds
793+--
794+-- Note that the observations below about the behavior of a left fold assume
795+-- that we are working on a right associated structure like cons lists and
796+-- streamly streams. If we are working on a left associated structure (e.g.
797+-- snoc lists) the roles of right and left folds would reverse.
798+--
799+-- Let's take a closer look at the @foldl@ definition for lists given above:
800+--
801+-- @
802+-- foldl f z (x:xs) = foldl f (z \`f` x) xs
803+-- @
804+--
805+-- @foldl@ calls itself recursively, in each call it invokes @f@ as @f z x@
806+-- providing it with the result accumulated till now @z@ (the state) and the
807+-- next element from the input container. First call to @f@ is supplied with
808+-- the initial value of the accumulator @z@ and each subsequent call uses the
809+-- output of the previous call to @f z x@.
810+--
811+-- >> foldl' (+) 0 [1,2,3]
812+-- > 6
813+--
814+-- The recursive call at the head of the output expression is bound to be
815+-- evaluated until recursion terminates, therefore, a left fold always
816+-- /consumes the whole input container/. The following would result in an
817+-- error, even though the fold is not using the values at all:
818+--
819+-- >> foldl' (\_ _ -> 0) 0 (1:undefined)
820+-- > *** Exception: Prelude.undefined
821+--
822+-- As @foldl@ recurses, it builds the left associated expression shown below.
823+-- Notice, the order of the arguments to the step function @f b a@. It follows
824+-- the left associative recursive expression generated by expanding @b \`f` a@.
825+--
826+-- @
827+-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
828+-- @
829+--
830+--
831+-- The strict left fold @foldl'@ forces the reduction of its argument @z \`f`
832+-- x@ before using it, therefore it never builds the whole expression in
833+-- memory. Thus, @z \`f` x1@ would get reduced to @z1@ and then @z1 \`f` x2@
834+-- would get reduced to @z2@ and so on, incrementally reducing the expression
835+-- from left to right as it recurses, consuming the input in FIFO order. Try
836+-- out @foldl' (+) 0 (map (\\x -> trace (show x) x) [1..10])@ to see how it
837+-- works. For example:
838+--
839+-- @
840+-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
841+-- 10
842+-- @
843+--
844+-- @
845+-- 0 + 1 = 1
846+-- 1 + 2 = 3
847+-- 3 + 3 = 6
848+-- 6 + 4 = 10
849+-- @
850+--
851+-- However, @foldl'@ evaluates the accumulator only to WHNF. It may further
852+-- help if the step function uses a strict data structure as accumulator to
853+-- improve performance and to keep the expression fully reduced at all times
854+-- during the fold.
855+--
856+-- A left fold can also build a new structure instead of reducing one if a
857+-- constructor is used as a fold step. However, it may not be very useful
858+-- because it will consume the whole input and construct the new structure in
859+-- memory before we can consume it. Thus the whole structure gets buffered in
860+-- memory. When the list constructor is used it would build a new list in
861+-- reverse (LIFO) order:
862+--
863+-- @
864+-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
865+-- [4,3,2,1]
866+-- @
867+--
868+-- A left fold is a push fold. The producer pushes its contents to the step
869+-- function of the fold. The step function therefore has no control to stop the
870+-- input, it can only discard it if it does not need it. We can also consider a
871+-- left fold as a state machine where the state is store in the accumulator,
872+-- the state can be modified based on new inputs that are pushed to the fold.
873+--
874+-- In general, a strict left fold is a reducing fold, whereas a right fold is a
875+-- constructing fold. A strict left fold reduces in a FIFO order whereas it
876+-- constructs in a LIFO order, and vice-versa for the right fold. See the
877+-- documentation of 'foldrM' for a discussion on where a left or right fold is
878+-- suitable.
879+--
880+-- To perform a left fold lazily without having to consume all the input one
881+-- can use @scanl@ to stream the intermediate results of the fold and consume
882+-- the resulting stream lazily.
883+--
884+
885+-- $runningfolds
886+--
887+-- "Streamly.Fold" module defines composable left folds which can be combined
888+-- together in many interesting ways. Those folds can be run using 'runFold'.
889+-- The following two ways of folding are equivalent in functionality and
890+-- performance,
891+--
892+-- >>> S.runFold FL.sum (S.enumerateFromTo 1 100)
893+-- 5050
894+-- >>> S.sum (S.enumerateFromTo 1 100)
895+-- 5050
896+--
897+-- However, left folds cannot terminate early even if it does not need to
898+-- consume more input to determine the result. Therefore, the performance is
899+-- equivalent only for full folds like 'sum' and 'length'. For partial folds
900+-- like 'head' or 'any' the the folds defined in this module may be much more
901+-- efficient because they are implemented as right folds that terminate as soon
902+-- as we get the result. Note that when a full fold is composed with a partial
903+-- fold in parallel the performance is not impacted as we anyway have to
904+-- consume the whole stream due to the full fold.
905+--
906+-- >>> S.head (1 `S.cons` undefined)
907+-- Just 1
908+-- >>> S.runFold FL.head (1 `S.cons` undefined)
909+-- *** Exception: Prelude.undefined
910+--
911+-- However, we can wrap the fold in a scan to convert it into a lazy stream of
912+-- fold steps. We can then terminate the stream whenever we want. For example,
913+--
914+-- >>> S.toList $ S.take 1 $ S.runScan FL.head (1 `S.cons` undefined)
915+-- [Nothing]
916+--
917+-- The following example extracts the input stream up to a point where the
918+-- running average of elements is no more than 10:
919+--
920+-- @
921+-- > S.toList
922+-- $ S.map (fromJust . fst)
923+-- $ S.takeWhile (\\(_,x) -> x <= 10)
924+-- $ S.runPostscan ((,) \<$> FL.last \<*> avg) (S.enumerateFromTo 1.0 100.0)
925+-- @
926+-- @
927+-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]
928+-- @
src/Streamly/Prelude/Internal.hs+68−496
@@ -22,12 +22,6 @@ module Streamly.Prelude.Internal
2222 (
2323 -- * Construction
2424 -- ** Primitives
25− -- | Primitives to construct a stream from pure values or monadic actions.
26− -- All other stream construction and generation combinators described later
27− -- can be expressed in terms of these primitives. However, the special
28− -- versions provided in this module can be much more efficient in most
29− -- cases. Users can create custom combinators using these primitives.
30−
3125 K.nil
3226 , K.cons
3327 , (K..:)
@@ -36,7 +30,6 @@ module Streamly.Prelude.Internal
3630 , (|:)
3731
3832 -- ** From Values
39− -- | Generate a monadic stream from a seed value or values.
4033 , yield
4134 , yieldM
4235 , K.repeat
@@ -44,27 +37,12 @@ module Streamly.Prelude.Internal
4437 , replicate
4538 , replicateM
4639
47− -- Note: Using enumeration functions e.g. 'Prelude.enumFromThen' turns out
48− -- to be slightly faster than the idioms like @[from, then..]@.
49− --
5040 -- ** Enumeration
51− -- | We can use the 'Enum' type class to enumerate a type producing a list
52− -- and then convert it to a stream:
53− --
54− -- @
55− -- 'fromList' $ 'Prelude.enumFromThen' from then
56− -- @
57− --
58− -- However, this is not particularly efficient.
59− -- The 'Enumerable' type class provides corresponding functions that
60− -- generate a stream instead of a list, efficiently.
61−
6241 , Enumerable (..)
6342 , enumerate
6443 , enumerateTo
6544
6645 -- ** From Generators
67− -- | Generate a monadic stream from a seed value and a generator function.
6846 , unfoldr
6947 , unfoldrM
7048 , iterate
@@ -73,8 +51,6 @@ module Streamly.Prelude.Internal
7351 , fromIndicesM
7452
7553 -- ** From Containers
76− -- | Convert an input structure, container or source into a stream. All of
77− -- these can be expressed in terms of primitives.
7854 , P.fromList
7955 , fromListM
8056 , K.fromFoldable
@@ -83,68 +59,11 @@ module Streamly.Prelude.Internal
8359 -- * Elimination
8460
8561 -- ** Deconstruction
86− -- | It is easy to express all the folds in terms of the 'uncons' primitive,
87− -- however the specific implementations provided later are generally more
88− -- efficient.
89− --
9062 , uncons
9163 , tail
9264 , init
9365
9466 -- ** Folding
95−-- | In imperative terms a fold can be considered as a loop over the stream
96−-- that reduces the stream to a single value.
97−-- Left and right folds use a fold function @f@ and an identity element @z@
98−-- (@zero@) to recursively deconstruct a structure and then combine and reduce
99−-- the values or transform and reconstruct a new container.
100−--
101−-- In general, a right fold is suitable for transforming and reconstructing a
102−-- right associated structure (e.g. cons lists and streamly streams) and a left
103−-- fold is suitable for reducing a right associated structure. The behavior of
104−-- right and left folds are described in detail in the individual fold's
105−-- documentation. To illustrate the two folds for cons lists:
106−--
107−-- > foldr :: (a -> b -> b) -> b -> [a] -> b
108−-- > foldr f z [] = z
109−-- > foldr f z (x:xs) = x `f` foldr f z xs
110−-- >
111−-- > foldl :: (b -> a -> b) -> b -> [a] -> b
112−-- > foldl f z [] = z
113−-- > foldl f z (x:xs) = foldl f (z `f` x) xs
114−--
115−-- @foldr@ is conceptually equivalent to:
116−--
117−-- > foldr f z [] = z
118−-- > foldr f z [x] = f x z
119−-- > foldr f z xs = foldr f (foldr f z (tail xs)) [head xs]
120−--
121−-- @foldl@ is conceptually equivalent to:
122−--
123−-- > foldl f z [] = z
124−-- > foldl f z [x] = f z x
125−-- > foldl f z xs = foldl f (foldl f z (init xs)) [last xs]
126−--
127−-- Left and right folds are duals of each other.
128−--
129−-- @
130−-- foldr f z xs = foldl (flip f) z (reverse xs)
131−-- foldl f z xs = foldr (flip f) z (reverse xs)
132−-- @
133−--
134−-- More generally:
135−--
136−-- @
137−-- foldr f z xs = foldl g id xs z where g k x = k . f x
138−-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
139−-- @
140−--
141−
142−-- As a general rule, foldr cannot have state and foldl cannot have control.
143−
144−-- NOTE: Folds are inherently serial as each step needs to use the result of
145−-- the previous step. However, it is possible to fold parts of the stream in
146−-- parallel and then combine the results using a monoid.
147−
14867 -- ** Right Folds
14968 , foldrM
15069 , foldrS
@@ -157,15 +76,11 @@ module Streamly.Prelude.Internal
15776 , foldlM'
15877
15978 -- ** Composable Left Folds
160− -- $runningfolds
161−
16279 , runFold
16380
16481 -- ** Full Folds
165− -- | Folds that are guaranteed to evaluate the whole stream.
16682
16783 -- -- ** To Summary (Full Folds)
168− -- -- | Folds that summarize the stream to a single value.
16984 , drain
17085 , last
17186 , length
@@ -174,8 +89,6 @@ module Streamly.Prelude.Internal
17489 --, mconcat
17590
17691 -- -- ** To Summary (Maybe) (Full Folds)
177− -- -- | Folds that summarize a non-empty stream to a 'Just' value and return
178− -- 'Nothing' for an empty stream.
17992 , maximumBy
18093 , maximum
18194 , minimumBy
@@ -186,18 +99,11 @@ module Streamly.Prelude.Internal
18699 , toStreamRev
187100
188101 -- ** Lazy Folds
189− --
190− -- | Folds that generate a lazy structure. Note that the generated
191− -- structure may not be lazy if the underlying monad is strict.
192102
193103 -- -- ** To Containers (Full Folds)
194− -- -- | Convert or divert a stream into an output structure, container or
195− -- sink.
196104 , toList
197105
198106 -- ** Partial Folds
199− -- | Folds that may terminate before evaluating the whole stream. These
200− -- folds strictly evaluate the stream until the result is determined.
201107
202108 -- -- ** To Elements (Partial Folds)
203109 , drainN
@@ -213,7 +119,6 @@ module Streamly.Prelude.Internal
213119 , elemIndex
214120
215121 -- -- ** To Boolean (Partial Folds)
216− -- -- | Folds that summarize the stream to a boolean value.
217122 , null
218123 , elem
219124 , notElem
@@ -223,78 +128,15 @@ module Streamly.Prelude.Internal
223128 , or
224129
225130 -- * Transformation
226−
227131 , transform
228132
229133 -- ** Mapping
230− -- | In imperative terms a map operation can be considered as a loop over
231− -- the stream that transforms the stream into another stream by performing
232− -- an operation on each element of the stream.
233− --
234− -- 'map' is the least powerful transformation operation with strictest
235− -- guarantees. A map, (1) is a stateless loop which means that no state is
236− -- allowed to be carried from one iteration to another, therefore,
237− -- operations on different elements are guaranteed to not affect each
238− -- other, (2) is a strictly one-to-one transformation of stream elements
239− -- which means it guarantees that no elements can be added or removed from
240− -- the stream, it can merely transform them.
241134 , Serial.map
242135 , sequence
243136 , mapM
244137 , mapM_
245138
246139 -- ** Scanning
247− --
248− -- | A scan is more powerful than map. While a 'map' is a stateless loop, a
249− -- @scan@ is a stateful loop which means that a state can be shared across
250− -- all the loop iterations, therefore, future iterations can be impacted by
251− -- the state changes made by the past iterations. A scan yields the state
252− -- of the loop after each iteration. Like a map, a @postscan@ or @prescan@
253− -- does not add or remove elements in the stream, it just transforms them.
254− -- However, a @scan@ adds one extra element to the stream.
255− --
256− -- A left associative scan, also known as a prefix sum, can be thought of
257− -- as a stream transformation consisting of left folds of all prefixes of a
258− -- stream. Another way of thinking about it is that it streams all the
259− -- intermediate values of the accumulator while applying a left fold on the
260− -- input stream. A right associative scan, on the other hand, can be
261− -- thought of as a stream consisting of right folds of all the suffixes of
262− -- a stream.
263− --
264− -- The following equations hold for lists:
265− --
266− -- > scanl f z xs == map (foldl f z) $ inits xs
267− -- > scanr f z xs == map (foldr f z) $ tails xs
268− --
269− -- @
270− -- > scanl (+) 0 [1,2,3,4]
271− -- 0 = 0
272− -- 0 + 1 = 1
273− -- 0 + 1 + 2 = 3
274− -- 0 + 1 + 2 + 3 = 6
275− -- 0 + 1 + 2 + 3 + 4 = 10
276− --
277− -- > scanr (+) 0 [1,2,3,4]
278− -- 1 + 2 + 3 + 4 + 0 = 10
279− -- 2 + 3 + 4 + 0 = 9
280− -- 3 + 4 + 0 = 7
281− -- 4 + 0 = 4
282− -- 0 = 0
283− -- @
284− --
285− -- Left and right scans are duals:
286− --
287− -- > scanr f z xs == reverse $ scanl (flip f) z (reverse xs)
288− -- > scanl f z xs == reverse $ scanr (flip f) z (reverse xs)
289− --
290− -- A scan is a stateful map i.e. a combination of map and fold:
291− --
292− -- > map f xs = tail $ scanl (\_ x -> f x) z xs
293− -- > map f xs = reverse $ head $ scanr (\_ x -> f x) z xs
294− --
295− -- > foldl f z xs = last $ scanl f z xs
296− -- > foldr f z xs = head $ scanr f z xs
297−
298140 -- ** Left scans
299141 , scanl'
300142 , scanlM'
@@ -326,9 +168,6 @@ module Streamly.Prelude.Internal
326168 -- , timestampedR -- timer
327169
328170 -- ** Filtering
329− -- | Remove some elements from the stream based on a predicate. In
330− -- imperative terms a filter over a stream corresponds to a loop with a
331− -- @continue@ clause for the cases when the predicate fails.
332171
333172 , filter
334173 , filterM
@@ -352,21 +191,15 @@ module Streamly.Prelude.Internal
352191 -- , pruneBy -- dropAround + uniqBy - like words
353192
354193 -- ** Mapping Filters
355− -- | Mapping along with filtering
356−
357194 , mapMaybe
358195 , mapMaybeM
359196
360197 -- ** Scanning Filters
361− -- | Stateful transformation along with filtering
362−
363198 , findIndices
364199 , elemIndices
365200 -- , seqIndices -- search a sequence in the stream
366201
367202 -- ** Insertion
368− -- | Insertion adds more elements to the stream.
369−
370203 , insertBy
371204 , intersperseM
372205 , intersperse
@@ -379,37 +212,10 @@ module Streamly.Prelude.Internal
379212 , reverse'
380213
381214 -- * Multi-Stream Operations
382− -- | New streams can be constructed by appending, merging or zipping
383− -- existing streams.
384215
385216 -- ** Appending
386− -- | Streams form a 'Semigroup' and a 'Monoid' under the append
387− -- operation.
388− --
389− -- @
390− --
391− -- -------Stream m a------|-------Stream m a------|=>----Stream m a---
392− --
393− -- @
394− --
395− -- @
396− -- >> S.toList $ S.fromList [1,2] \<> S.fromList [3,4]
397− -- [1,2,3,4]
398− -- >> S.toList $ fold $ [S.fromList [1,2], S.fromList [3,4]]
399− -- [1,2,3,4]
400− -- @
401217
402218 -- ** Merging
403− -- | Streams form a commutative semigroup under the merge
404− -- operation.
405− --
406− -- @
407− --
408− -- -------Stream m a------|
409− -- |=>----Stream m a---
410− -- -------Stream m a------|
411− -- @
412− --
413219
414220 -- , merge
415221 , mergeBy
@@ -418,23 +224,12 @@ module Streamly.Prelude.Internal
418224 , mergeAsyncByM
419225
420226 -- ** Zipping
421− -- |
422− -- @
423− --
424− -- -------Stream m a------|
425− -- |=>----Stream m c---
426− -- -------Stream m b------|
427− -- @
428− --
429227 , zipWith
430228 , zipWithM
431229 , Z.zipAsyncWith
432230 , Z.zipAsyncWithM
433231
434232 -- ** Nested Streams
435− -- | Stream operations represent loops in imperative programming, streams
436− -- of streams represent nested loops.
437−
438233 , concatMapM
439234 , concatMap
440235 , K.concatMapBy
@@ -460,15 +255,6 @@ module Streamly.Prelude.Internal
460255 -- , breakOnSeq
461256
462257 -- ** Splitting
463− -- | Streams can be split into segments in space or in time. We use the
464− -- term @chunk@ to refer to a spatial length of the stream (spatial window)
465− -- and the term @session@ to refer to a length in time (time window).
466−
467− -- In imperative terms, grouped folding can be considered as a nested loop
468− -- where we loop over the stream to group elements and then loop over
469− -- individual groups to fold them to a single value that is yielded in the
470− -- output stream.
471−
472258 -- , groupScan
473259
474260 -- -- *** Chunks
@@ -476,12 +262,10 @@ module Streamly.Prelude.Internal
476262 , sessionsOf
477263
478264 -- -- *** Using Element Separators
479− -- On == Dropping the separator
480265 , splitOn
481266 , splitOnSuffix
482267 -- , splitOnPrefix
483268
484− -- By == Keeping the separator
485269 -- , splitBy
486270 , splitBySuffix
487271 -- , splitByPrefix
@@ -513,31 +297,18 @@ module Streamly.Prelude.Internal
513297 , tap
514298
515299 -- * Windowed Classification
516− -- | Split the stream into windows or chunks in space or time. Each window
517− -- can be associated with a key, all events associated with a particular
518− -- key in the window can be folded to a single result. The stream is split
519− -- into windows of specified size, the window can be terminated early if
520− -- the closing flag is specified in the input stream.
521− --
522− -- The term "chunk" is used for a space window and the term "session" is
523− -- used for a time window.
524300
525301 -- ** Tumbling Windows
526− -- | A new window starts after the previous window is finished.
527302 -- , classifyChunksOf
528303 , classifySessionsBy
529304 , classifySessionsOf
530305
531306 -- ** Keep Alive Windows
532− -- | The window size is extended if an event arrives within the specified
533− -- window size. This can represent sessions with idle or inactive timeout.
534307 -- , classifyKeepAliveChunks
535308 , classifyKeepAliveSessions
536309
537310 {-
538311 -- ** Sliding Windows
539− -- | A new window starts after the specified slide from the previous
540− -- window. Therefore windows can overlap.
541312 , classifySlidingChunks
542313 , classifySlidingSessions
543314 -}
@@ -546,10 +317,6 @@ module Streamly.Prelude.Internal
546317 -- , slidingSessionBuffer
547318
548319 -- ** Containers of Streams
549− -- | These are variants of standard 'Foldable' fold functions that use a
550− -- polymorphic stream sum operation (e.g. 'async' or 'wSerial') to fold a
551− -- finite container of streams.
552− --
553320 , foldWith
554321 , foldMapWith
555322 , forEachWith
@@ -613,6 +380,7 @@ import qualified System.IO as IO
613380import Streamly.Enumeration (Enumerable(..), enumerate, enumerateTo)
614381import Streamly.Fold.Types (Fold (..))
615382import Streamly.Memory.Array (Array)
383+-- import Streamly.Memory.Ring (Ring)
616384import Streamly.SVar (MonadAsync, defState)
617385import Streamly.Streams.Async (mkAsync')
618386import Streamly.Streams.Combinators (maxYields)
@@ -1003,101 +771,21 @@ fromHandle h = go
1003771-- Elimination by Folding
1004772------------------------------------------------------------------------------
1005773
1006−-- | Right associative/pull fold.
774+-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs
775+-- an output structure using the step function @build@. @build@ is invoked with
776+-- the next input element and the remaining (lazy) tail of the output
777+-- structure. It builds a lazy output expression using the two. When the "tail
778+-- structure" in the output expression is evaluated it calls @build@ again thus
779+-- lazily consuming the input @stream@ until either the output expression built
780+-- by @build@ is free of the "tail" or the input is exhausted in which case
781+-- @final@ is used as the terminating case for the output structure. For more
782+-- details see the description in the previous section.
1007783--
1008784-- Example, determine if any element is 'odd' in a stream:
1009785--
1010786-- >>> S.foldrM (\x xs -> if odd x then return True else xs) (return False) $ S.fromList (2:4:5:undefined)
1011787-- > True
1012788--
1013−-- Let's take a closer look at the @foldr@ definition for lists, as given
1014−-- earlier:
1015−--
1016−-- @
1017−-- foldr f z (x:xs) = x \`f` foldr f z xs
1018−-- @
1019−--
1020−-- @foldr@ invokes the fold step function @f@ as @f x (foldr f z xs)@. At each
1021−-- invocation of @f@ @foldr@ gives us the next element in the input container
1022−-- @x@ and a recursive expression @foldr f z xs@ representing the yet unbuilt
1023−-- (lazy thunk) part of the output. Therefore, when @f x xs@ is lazy in @xs@
1024−-- it can consume the input one element at a time in FIFO order to build a lazy
1025−-- output expression. For example,
1026−--
1027−-- > f x remaining = show x : remaining
1028−--
1029−-- @take 2 $ foldr f [] (1:2:undefined)@ would consume the input lazily on
1030−-- demand, consuming only first two elements and resulting in ["1", "2"]. @f@
1031−-- can terminate recursion by not evaluating the @remaining@ part:
1032−--
1033−-- > f 2 remaining = show 2 : []
1034−-- > f x remaining = show x : remaining
1035−--
1036−-- @f@ would terminate recursion whenever it sees element @2@ in the input.
1037−-- Therefore, @foldr f [] (1:2:undefined)@ would work as before. If @f a b@ is
1038−-- strict in @b@ it would end up consuming the whole input right away and
1039−-- expanding the recursive expression @b@ (i.e. @foldr f z xs@) fully before
1040−-- it yields an output expression, resulting in the following /right
1041−-- associated expression/:
1042−--
1043−-- @
1044−-- foldr f z xs == x1 \`f` (x2 \`f` ...(xn \`f` z))
1045−-- @
1046−--
1047−-- For example,
1048−--
1049−-- > f x remaining = x + remaining
1050−--
1051−-- With this definition, @foldr f 0 [1..1000]@, would recurse completely until
1052−-- it reaches the recursion terminating case @... `f` (1000 `f` 0)@, and then
1053−-- start reducing the whole expression from right to left, therefore, consuming
1054−-- the input elements in LIFO order. Thus, such an evaluation would require
1055−-- memory proportional to the size of input. Try out @foldr (+) 0 (map (\\x ->
1056−-- trace (show x) x) [1..10])@.
1057−--
1058−-- Notice, the order of the arguments to the step function @f a b@. It follows
1059−-- the order of @a@ and @b@ in the right associative recursive expression
1060−-- generated by expanding @a \`f` b@.
1061−--
1062−-- A right fold is a pull fold, the step function is the puller, it can pull
1063−-- more data from the input container by using its second argument in the
1064−-- output expression or terminate pulling by not using it. As a corollary:
1065−--
1066−-- 1. a step function which is lazy in its second argument (usually functions
1067−-- or constructors that build a lazy structure e.g. @(:)@) can pull lazily on
1068−-- demand.
1069−-- 2. a step function strict in its second argument (usually reducers e.g.
1070−-- (+)) would end up pulling all of its input and buffer it in memory before
1071−-- potentially reducing it.
1072−--
1073−-- A right fold is suitable for lazy reconstructions e.g. transformation,
1074−-- mapping, filtering of /right associated input structures/ (e.g. cons lists).
1075−-- Whereas a left fold is suitable for reductions (e.g. summing a stream of
1076−-- numbers) of right associated structures. Note that these roles will reverse
1077−-- for left associated structures (e.g. snoc lists). Most of our observations
1078−-- here assume right associated structures, lists being the canonical example.
1079−--
1080−-- 1. A lazy FIFO style pull using a right fold allows pulling a potentially
1081−-- /infinite/ input stream lazily, perform transformations on it, and
1082−-- reconstruct a new structure without having to buffer the whole structure. In
1083−-- contrast, a left fold would buffer the entire structure before the
1084−-- reconstructed structure can be consumed.
1085−-- 2. Even if buffering the entire input structure is ok, we need to keep in
1086−-- mind that a right fold reconstructs structures in a FIFO style, whereas a
1087−-- left fold reconstructs in a LIFO style, thereby reversing the order of
1088−-- elements..
1089−-- 3. A right fold has termination control and therefore can terminate early
1090−-- without going throught the entire input, a left fold cannot terminate
1091−-- without consuming all of its input. For example, a right fold
1092−-- implementation of 'or' can terminate as soon as it finds the first 'True'
1093−-- element, whereas a left fold would necessarily go through the entire input
1094−-- irrespective of that.
1095−-- 4. Reduction (e.g. using (+) on a stream of numbers) using a right fold
1096−-- occurs in a LIFO style, which means that the entire input gets buffered
1097−-- before reduction starts. Whereas with a strict left fold reductions occur
1098−-- incrementally in FIFO style. Therefore, a strict left fold is more suitable
1099−-- for reductions.
1100−--
1101789-- /Since: 0.7.0 (signature changed)/
1102790--
1103791-- /Since: 0.2.0 (signature changed)/
@@ -1155,7 +843,8 @@ foldrT :: (IsStream t, Monad m, Monad (s m), MonadTrans s)
1155843 => (a -> s m b -> s m b) -> s m b -> t m a -> s m b
1156844foldrT f z s = S.foldrT f z (toStreamS s)
1157845
1158−-- | Right fold for lazy monads or pure streams.
846+-- | Right fold, lazy for lazy monads and pure streams, and strict for strict
847+-- monads.
1159848--
1160849-- Please avoid using this routine in strict monads like IO unless you need a
1161850-- strict right fold. This is provided only for use in lazy monads (e.g.
@@ -1192,96 +881,13 @@ foldr1 f m = S.foldr1 f (toStreamS m)
1192881foldx :: Monad m => (x -> a -> x) -> x -> (x -> b) -> SerialT m a -> m b
1193882foldx = P.foldlx'
1194883
1195−-- | Strict left associative/push fold. Note that the observations below about
1196−-- the behavior of a left fold assume that we are working on a right associated
1197−-- structure like cons lists and streamly streams. If we are working on a left
1198−-- associated structure (e.g. snoc lists) the roles of right and left folds
1199−-- would reverse.
1200−--
1201−-- Let's take a closer look at the @foldl@ definition for lists given above:
1202−--
1203−-- @
1204−-- foldl f z (x:xs) = foldl f (z \`f` x) xs
1205−-- @
1206−--
1207−-- @foldl@ calls itself recursively, in each call it invokes @f@ as @f z x@
1208−-- providing it with the result accumulated till now @z@ (the state) and the
1209−-- next element from the input container. First call to @f@ is supplied with
1210−-- the initial value of the accumulator @z@ and each subsequent call uses the
1211−-- output of the previous call to @f z x@.
1212−--
1213−-- >> foldl' (+) 0 [1,2,3]
1214−-- > 6
1215−--
1216−-- The recursive call at the head of the output expression is bound to be
1217−-- evaluated until recursion terminates, therefore, a left fold always
1218−-- /consumes the whole input container/. The following would result in an
1219−-- error, even though the fold is not using the values at all:
1220−--
1221−-- >> foldl' (\_ _ -> 0) 0 (1:undefined)
1222−-- > *** Exception: Prelude.undefined
1223−--
1224−-- As @foldl@ recurses, it builds the left associated expression shown below.
1225−-- Notice, the order of the arguments to the step function @f b a@. It follows
1226−-- the left associative recursive expression generated by expanding @b \`f` a@.
1227−--
1228−-- @
1229−-- foldl f z xs == (((z \`f` x1) \`f` x2) ...) \`f` xn
1230−-- @
1231−--
1232−--
1233−-- The strict left fold @foldl'@ forces the reduction of its argument @z \`f`
1234−-- x@ before using it, therefore it never builds the whole expression in
1235−-- memory. Thus, @z \`f` x1@ would get reduced to @z1@ and then @z1 \`f` x2@
1236−-- would get reduced to @z2@ and so on, incrementally reducing the expression
1237−-- from left to right as it recurses, consuming the input in FIFO order. Try
1238−-- out @foldl' (+) 0 (map (\\x -> trace (show x) x) [1..10])@ to see how it
1239−-- works. For example:
1240−--
1241−-- @
1242−-- > S.foldl' (+) 0 $ S.fromList [1,2,3,4]
1243−-- 10
1244−-- @
1245−--
1246−-- @
1247−-- 0 + 1 = 1
1248−-- 1 + 2 = 3
1249−-- 3 + 3 = 6
1250−-- 6 + 4 = 10
1251−-- @
1252−--
1253−-- However, @foldl'@ evaluates the accumulator only to WHNF. It may further
1254−-- help if the step function uses a strict data structure as accumulator to
1255−-- improve performance and to keep the expression fully reduced at all times
1256−-- during the fold.
1257−--
1258−-- A left fold can also build a new structure instead of reducing one if a
1259−-- constructor is used as a fold step. However, it may not be very useful
1260−-- because it will consume the whole input and construct the new structure in
1261−-- memory before we can consume it. Thus the whole structure gets buffered in
1262−-- memory. When the list constructor is used it would build a new list in
1263−-- reverse (LIFO) order:
1264−--
1265−-- @
1266−-- > S.foldl' (flip (:)) [] $ S.fromList [1,2,3,4]
1267−-- [4,3,2,1]
1268−-- @
1269−--
1270−-- A left fold is a push fold. The producer pushes its contents to the step
1271−-- function of the fold. The step function therefore has no control to stop the
1272−-- input, it can only discard it if it does not need it. We can also consider a
1273−-- left fold as a state machine where the state is store in the accumulator,
1274−-- the state can be modified based on new inputs that are pushed to the fold.
1275−--
1276−-- In general, a strict left fold is a reducing fold, whereas a right fold is a
1277−-- constructing fold. A strict left fold reduces in a FIFO order whereas it
1278−-- constructs in a LIFO order, and vice-versa for the right fold. See the
1279−-- documentation of 'foldrM' for a discussion on where a left or right fold is
1280−-- suitable.
1281−--
1282−-- To perform a left fold lazily without having to consume all the input one
1283−-- can use @scanl@ to stream the intermediate results of the fold and consume
1284−-- the resulting stream lazily.
884+-- | Left associative/strict push fold. @foldl' reduce initial stream@ invokes
885+-- @reduce@ with the accumulator and the next input in the input stream, using
886+-- @initial@ as the initial value of the current value of the accumulator. When
887+-- the input is exhausted the current value of the accumulator is returned.
888+-- Make sure to use a strict data structure for accumulator to not build
889+-- unnecessary lazy expressions unless that's what you want. See the previous
890+-- section for more details.
1285891--
1286892-- @since 0.2.0
1287893{-# INLINE foldl' #-}
@@ -1321,51 +927,6 @@ foldlM' step begin m = S.foldlM' step begin $ toStreamS m
1321927-- Running a Fold
1322928------------------------------------------------------------------------------
1323929
1324−-- $runningfolds
1325−--
1326−-- "Streamly.Fold" module defines composable left folds which can be combined
1327−-- together in many interesting ways. Those folds can be run using 'runFold'.
1328−-- The following two ways of folding are equivalent in functionality and
1329−-- performance,
1330−--
1331−-- >>> S.runFold FL.sum (S.enumerateFromTo 1 100)
1332−-- 5050
1333−-- >>> S.sum (S.enumerateFromTo 1 100)
1334−-- 5050
1335−--
1336−-- However, left folds cannot terminate early even if it does not need to
1337−-- consume more input to determine the result. Therefore, the performance is
1338−-- equivalent only for full folds like 'sum' and 'length'. For partial folds
1339−-- like 'head' or 'any' the the folds defined in this module may be much more
1340−-- efficient because they are implemented as right folds that terminate as soon
1341−-- as we get the result. Note that when a full fold is composed with a partial
1342−-- fold in parallel the performance is not impacted as we anyway have to
1343−-- consume the whole stream due to the full fold.
1344−--
1345−-- >>> S.head (1 `S.cons` undefined)
1346−-- Just 1
1347−-- >>> S.runFold FL.head (1 `S.cons` undefined)
1348−-- *** Exception: Prelude.undefined
1349−--
1350−-- However, we can wrap the fold in a scan to convert it into a lazy stream of
1351−-- fold steps. We can then terminate the stream whenever we want. For example,
1352−--
1353−-- >>> S.toList $ S.take 1 $ S.runScan FL.head (1 `S.cons` undefined)
1354−-- [Nothing]
1355−--
1356−-- The following example extracts the input stream up to a point where the
1357−-- running average of elements is no more than 10:
1358−--
1359−-- @
1360−-- > S.toList
1361−-- $ S.map (fromJust . fst)
1362−-- $ S.takeWhile (\\(_,x) -> x <= 10)
1363−-- $ S.runPostscan ((,) \<$> FL.last \<*> avg) (S.enumerateFromTo 1.0 100.0)
1364−-- @
1365−-- @
1366−-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]
1367−-- @
1368−
1369930-- | Fold a stream using the supplied left fold.
1370931--
1371932-- >>> S.runFold FL.sum (S.enumerateFromTo 1 100)
@@ -1673,7 +1234,7 @@ elemIndex a = findIndex (== a)
16731234------------------------------------------------------------------------------
16741235
16751236-- | Returns 'True' if the first stream is the same as or a prefix of the
1676−-- second.
1237+-- second. A stream is a prefix of itself.
16771238--
16781239-- @
16791240-- > S.isPrefixOf (S.fromList "hello") (S.fromList "hello" :: SerialT IO Char)
@@ -1687,7 +1248,7 @@ isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)
16871248
16881249-- | Returns 'True' if all the elements of the first stream occur, in order, in
16891250-- the second stream. The elements do not have to occur consecutively. A stream
1690−-- is treated as a subsequence of itself.
1251+-- is a subsequence of itself.
16911252--
16921253-- @
16931254-- > S.isSubsequenceOf (S.fromList "hlo") (S.fromList "hello" :: SerialT IO Char)
@@ -1877,7 +1438,7 @@ scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
18771438-- thus modularizing the stream processing. This can be useful in
18781439-- stateful or event-driven programming.
18791440--
1880−-- Consider the following monolothic example, computing the sum and the product
1441+-- Consider the following monolithic example, computing the sum and the product
18811442-- of the elements in a stream in one go using a @foldl'@:
18821443--
18831444-- @
@@ -2132,6 +1693,12 @@ sequence m = fromStreamS $ S.sequence (toStreamS m)
21321693-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'
21331694-- elements, and return a stream of values extracted from 'Just'.
21341695--
1696+-- Equivalent to:
1697+--
1698+-- @
1699+-- mapMaybe f = S.map 'fromJust' . S.filter 'isJust' . S.map f
1700+-- @
1701+--
21351702-- @since 0.3.0
21361703{-# INLINE mapMaybe #-}
21371704mapMaybe :: (IsStream t, Monad m) => (a -> Maybe b) -> t m a -> t m b
@@ -2139,6 +1706,12 @@ mapMaybe f m = fromStreamS $ S.mapMaybe f $ toStreamS m
21391706
21401707-- | Like 'mapMaybe' but maps a monadic function.
21411708--
1709+-- Equivalent to:
1710+--
1711+-- @
1712+-- mapMaybeM f = S.map 'fromJust' . S.filter 'isJust' . S.mapM f
1713+-- @
1714+--
21421715-- /Concurrent (do not use with 'parallely' on infinite streams)/
21431716--
21441717-- @since 0.3.0
@@ -2162,10 +1735,9 @@ mapMaybeMSerial f m = fromStreamD $ D.mapMaybeM f $ toStreamD m
21621735-- /Note:/ 'reverse'' is much faster than this, use that when performance
21631736-- matters.
21641737--
2165−-- |
21661738-- > reverse = S.foldlT (flip S.cons) S.nil
21671739--
2168−-- Returns the elements of the stream in reverse order. The stream must be
1740+-- | Returns the elements of the stream in reverse order. The stream must be
21691741-- finite. Note that this necessarily buffers the entire stream in memory.
21701742--
21711743-- /Since 0.7.0 (Monad m constraint)/
@@ -2426,7 +1998,7 @@ mergeByM f m1 m2 = fromStreamS $ S.mergeByM f (toStreamS m1) (toStreamS m2)
24261998
24271999{-
24282000-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.
2429−{-# INLINABLE mergeUptoShortest #-}
2001+{-# INLINABLE mergeEndByAny #-}
24302002mergeEndByAny
24312003 :: (IsStream t, Monad m)
24322004 => (a -> a -> m Ordering) -> t m a -> t m a -> t m a
@@ -2484,35 +2056,23 @@ mergeAsyncByM f m1 m2 = K.mkStream $ \st stp sng yld -> do
24842056-- Nesting
24852057------------------------------------------------------------------------------
24862058
2487−-- | Map each element of the stream to produce a stream and then flatten the
2488−-- results into a single stream.
2489−--
2490−-- > concatMap f = concatMapM (return . f)
2491−--
2492−-- In imperative terms, a 'concatMap' corresponds to nested loops. We loop over
2493−-- the input stream and then for each element of the input stream another
2494−-- stream is generated and then we loop over that inner stream as well to yield
2495−-- each element of that stream to generate a single output stream.
2496−--
2497−-- 'concatMap' can perform filtering by mapping an element to a 'nil'
2498−-- stream:
2499−--
2500−-- > filter p m = S.concatMap (\x -> if p x then S.yield x else S.nil) m
2501−--
2502−-- Its a map, therefore it can degenerate to a simple map operation as well:
2059+-- | Map a stream producing function on each element of the stream and then
2060+-- flatten the results into a single stream.
25032061--
2504−-- > map f m = S.concatMap (\x -> S.yield (f x)) m
2505−--
2506−-- 'concatMap' is not inherently stateful, however, it can be combined with a
2507−-- scan to perform stateful operations.
2062+-- @
2063+-- concatMap = 'concatMapBy' 'Serial.serial'
2064+-- concatMap f = 'concatMapM' (return . f)
2065+-- @
25082066--
25092067-- @since 0.6.0
25102068{-# INLINE concatMap #-}
25112069concatMap ::(IsStream t, Monad m) => (a -> t m b) -> t m a -> t m b
25122070concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)
25132071
2514−-- | Map each element to a stream to produce a stream using a monadic function
2515−-- and then flatten the results into a single stream.
2072+-- | Map a stream producing monadic function on each element of the stream
2073+-- and then flatten the results into a single stream. Since the stream
2074+-- generation function is monadic, unlike 'concatMap', it can produce an
2075+-- effect at the beginning of each iteration of the inner loop.
25162076--
25172077-- @since 0.6.0
25182078{-# INLINE concatMapM #-}
@@ -2523,9 +2083,16 @@ concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)
25232083-- | A generalization of insertBy to inserting sequences.
25242084interposeBy :: (IsStream t, Monad m)
25252085 => (a -> a -> Ordering) -> t m a -> t m a -> t m a
2086+interposeBy = undefined
25262087
2088+-- XXX shall we intercalate an Array instead? A stream is exhaustible and
2089+-- should be used only once unless it is a pure stream. or we can have
2090+-- intercalateN to intercalate n elements from the input stream at a time and
2091+-- generate the stream by cycling the array.
2092+--
25272093-- | A generalization of intersperseM to intersperse sequences.
25282094intercalate :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
2095+intercalate = undefined
25292096-}
25302097
25312098------------------------------------------------------------------------------
@@ -2565,10 +2132,11 @@ intercalate :: (IsStream t, MonadAsync m) => t m a -> t m a -> t m a
25652132-- >>> splitAt_ 4 [1,2,3]
25662133-- > ([1,2,3],[])
25672134--
2135+-- @since 0.7.0
2136+
25682137-- This can be considered as a two-fold version of 'ltake' where we take both
25692138-- the segments instead of discarding the leftover.
25702139--
2571−-- @since 0.7.0
25722140{-# INLINE splitAt #-}
25732141splitAt
25742142 :: Monad m
@@ -2887,11 +2455,12 @@ breakOn pat f m = undefined
28872455-- separators. We can have a single split operation taking the splitter config
28882456-- as argument.
28892457
2890−-- | Split a stream on separator elements determined by a predicate, dropping
2891−-- the separator. Separator is considered as infixed between two segments, if
2892−-- one side of the separator is missing then it is parsed as an empty stream.
2893−-- With '-' representing elements and '.' as separator, 'splitOn' splits as
2894−-- follows:
2458+-- | Split on an infixed separator element, dropping the separator. Splits the
2459+-- stream on separator elements determined by the supplied predicate, separator
2460+-- is considered as infixed between two segments, if one side of the separator
2461+-- is missing then it is parsed as an empty stream. The supplied 'Fold' is
2462+-- applied on the split segments. With '-' representing non-separator elements
2463+-- and '.' as separator, 'splitOn' splits as follows:
28952464--
28962465-- @
28972466-- "--.--" => "--" "--"
@@ -2988,10 +2557,10 @@ splitOnSuffix
29882557splitOnSuffix predicate f m =
29892558 D.fromStreamD $ D.splitSuffixBy predicate f (D.toStreamD m)
29902559
2991−-- | Like 'splitBy' but ignores repeated separators or separators in leading
2992−-- or trailing position. Therefore, @"..a..b.."@ would be parsed as
2993−-- @["a","b"]@. In other words, it treats the input like words separated by
2994−-- whitespace elements determined by the predicate.
2560+-- | Like 'splitOn' after stripping leading, trailing, and repeated separators.
2561+-- Therefore, @".a..b."@ with '.' as the separator would be parsed as
2562+-- @["a","b"]@. In other words, its like parsing words from whitespace
2563+-- separated text.
29952564--
29962565-- > wordsBy' p xs = S.toList $ S.wordsBy p (FL.toList) (S.fromList xs)
29972566--
@@ -3007,6 +2576,9 @@ splitOnSuffix predicate f m =
30072576-- > words = wordsBy isSpace
30082577--
30092578-- @since 0.7.0
2579+
2580+-- It is equivalent to splitting in any of the infix/prefix/suffix styles
2581+-- followed by removal of empty segments.
30102582{-# INLINE wordsBy #-}
30112583wordsBy
30122584 :: (IsStream t, Monad m)
src/Streamly/Streams/Prelude.hs+19−1
@@ -275,6 +275,12 @@ foldbWith f = K.foldb f K.nil
275275--
276276-- @foldWith 'async' $ map return [1..3]@
277277--
278+-- Equivalent to:
279+--
280+-- @
281+-- foldWith f = S.foldMapWith f id
282+-- @
283+--
278284-- /Since: 0.7.0 ("Streamly.Prelude")/
279285--
280286-- /Since: 0.1.0 ("Streamly")/
@@ -284,11 +290,17 @@ foldWith :: (IsStream t, Foldable f)
284290foldWith f = Prelude.foldr f K.nil
285291
286292-- | A variant of 'foldMap' that allows you to map a monadic streaming action
287−-- on a 'Foldable' container and then fold it using the specified stream sum
293+-- on a 'Foldable' container and then fold it using the specified stream merge
288294-- operation.
289295--
290296-- @foldMapWith 'async' return [1..3]@
291297--
298+-- Equivalent to:
299+--
300+-- @
301+-- foldMapWith f g xs = S.concatMapWith f g (S.fromFoldable xs)
302+-- @
303+--
292304-- /Since: 0.7.0 ("Streamly.Prelude")/
293305--
294306-- /Since: 0.1.0 ("Streamly")/
@@ -300,6 +312,12 @@ foldMapWith f g = Prelude.foldr (f . g) K.nil
300312-- | Like 'foldMapWith' but with the last two arguments reversed i.e. the
301313-- monadic streaming function is the last argument.
302314--
315+-- Equivalent to:
316+--
317+-- @
318+-- forEachWith = flip S.foldMapWith
319+-- @
320+--
303321-- /Since: 0.7.0 ("Streamly.Prelude")/
304322--
305323-- /Since: 0.1.0 ("Streamly")/