@@ -113,16 +113,24 @@ module Streamly.Internal.Data.Stream.Expand
113 , bindWith
113 , bindWith
114 , concatSmapMWith
114 , concatSmapMWith
116− -- * ConcatPairsWith
116+ -- * MergeMapWith
117 -- | See the notes about suitable merge functions in the 'concatMapWith'
117 -- | See the notes about suitable merge functions in the 'concatMapWith'
118 -- section.
118 -- section.
119 , mergeMapWith
119 , mergeMapWith
121− -- * IterateMap
121+ -- * Iterate
122 -- | Map and flatten Trees of Streams
122 -- | Map and flatten Trees of Streams
123− , iterateMapWith
123+ , iterateConcatMapWith
124− , iterateSmapMWith
124+ , iterateMergeMapWith
125− , iterateMapLeftsWith
125+
126+ , iterateConcatMapDFS
127+ , iterateConcatMapBFS
129+ -- More experimental ops
130+ , iterateConcatMapBFSRev
131+ , iterateLeftsConcatMapWith
132+ , iterateConcatScanWith
126 , iterateUnfold
134 , iterateUnfold
@@ -130,7 +138,7 @@ where
130#include "inline.hs"
138#include "inline.hs"
132import Streamly.Internal.Data.Stream.Bottom
140import Streamly.Internal.Data.Stream.Bottom
133− ( concatEffect, concatMapM, concatMap, smapM, fromPure, fromEffect
141+ ( concatEffect, concatMapM, concatMap, smapM, fromPure
134 , zipWith, zipWithM)
142 , zipWith, zipWithM)
135import Streamly.Internal.Data.Stream.Type
143import Streamly.Internal.Data.Stream.Type
136 ( Stream, fromStreamD, fromStreamK, toStreamD, toStreamK
144 ( Stream, fromStreamD, fromStreamK, toStreamD, toStreamK
@@ -629,41 +637,128 @@ mergeMapWith par f m =
629 (toStreamK m)
637 (toStreamK m)
631------------------------------------------------------------------------------
639------------------------------------------------------------------------------
632−-- IterateMap - Map and flatten Trees of Streams
640+-- iterateConcatMap - Map and flatten Trees of Streams
633------------------------------------------------------------------------------
641------------------------------------------------------------------------------
635−-- | Like 'iterateM' but iterates after mapping a stream generator on the
643+-- | Yield an input element in the output stream, map a stream generator on it
636−-- output.
644+-- and repeat the process on the resulting stream. Resulting streams are
645+-- flattened using the 'concatMapWith' combinator. This can be used for a depth
646+-- first style (DFS) traversal of a tree like structure.
648+-- Example, list a directory tree using DFS:
650+-- >>> f = either Dir.readEitherPaths (const Stream.nil)
651+-- >>> input = Stream.fromPure (Left ".")
652+-- >>> ls = Stream.iterateConcatMapWith Stream.append f input
654+-- Note that 'iterateM' is a special case of 'iterateConcatMapWith':
656+-- >>> iterateM f = Stream.iterateConcatMapWith Stream.append (Stream.fromEffect . f) . Stream.fromEffect
662+{-# INLINE iterateConcatMapWith #-}
663+iterateConcatMapWith ::
664+ (Stream m a -> Stream m a -> Stream m a)
665+ -> (a -> Stream m a)
668+iterateConcatMapWith combine f = iterateStream
672+ iterateStream = concatMapWith combine generate
674+ generate x = fromPure x `combine` iterateStream (f x)
676+-- | Traverse the stream in depth first style (DFS). Map each element in the
677+-- input stream to a stream and flatten, recursively map the resulting elements
678+-- as well to a stream and flatten until no more streams are generated.
680+-- Example, list a directory tree using DFS:
682+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
683+-- >>> input = Stream.fromPure (Left ".")
684+-- >>> ls = Stream.iterateConcatMapDFS f input
686+-- This is equivalent to using @iterateConcatMapWith Stream.append@.
689+{-# INLINE iterateConcatMapDFS #-}
690+iterateConcatMapDFS :: Monad m =>
691+ (a -> Maybe (Stream m a))
694+iterateConcatMapDFS f stream =
696+ $ D.iterateConcatMapDFS (fmap toStreamD . f ) (toStreamD stream)
698+-- | Similar to 'iterateConcatMapDFS' except that it traverses the stream in
699+-- breadth first style (BFS). First, all the elements in the input stream are
700+-- emitted, and then their traversals are emitted.
638−-- Yield an input element in the output stream, map a stream generator on it
702+-- Example, list a directory tree using BFS:
639−-- and then do the same on the resulting stream. This can be used for a depth
640−-- first traversal of a tree like structure.
642−-- Note that 'iterateM' is a special case of 'iterateMapWith':
704+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)
705+-- >>> input = Stream.fromPure (Left ".")
706+-- >>> ls = Stream.iterateConcatMapBFS f input
644−-- >>> iterateM f = Stream.iterateMapWith Stream.append (Stream.fromEffect . f) . Stream.fromEffect
708+-- /Pre-release/
709+{-# INLINE iterateConcatMapBFS #-}
710+iterateConcatMapBFS :: Monad m =>
711+ (a -> Maybe (Stream m a))
714+iterateConcatMapBFS f stream =
716+ $ D.iterateConcatMapBFS (fmap toStreamD . f ) (toStreamD stream)
718+-- | Same as 'iterateConcatMapBFS' except that the traversal of the last
719+-- element on a level is emitted first and then going backwards up to the first
720+-- element (reversed ordering). This may be slightly faster than
721+-- 'iterateConcatMapBFS'.
723+{-# INLINE iterateConcatMapBFSRev #-}
724+iterateConcatMapBFSRev :: Monad m =>
725+ (a -> Maybe (Stream m a))
728+iterateConcatMapBFSRev f stream =
730+ $ D.iterateConcatMapBFSRev (fmap toStreamD . f ) (toStreamD stream)
732+-- | Like 'iterateConcatMapWith' but uses the pairwise flattening combinator
733+-- 'mergeMapWith' for flattening the resulting streams. This can be used for a
734+-- balanced traversal of a tree like structure.
646−-- It can be used to traverse a tree structure. For example, to list a
647−-- directory tree:
736+-- Example, list a directory tree using balanced traversal:
738+-- >>> f = either Dir.readEitherPaths (const Stream.nil)
649-- >>> input = Stream.fromPure (Left ".")
739-- >>> input = Stream.fromPure (Left ".")
650−-- >>> f = either Dir.readEither (const Stream.nil)
651−-- >>> ls = Stream.iterateMapWith Stream.append f input
740+-- >>> ls = Stream.iterateMergeMapWith Stream.interleave f input
653-- /Pre-release/
744-- /Pre-release/
655−{-# INLINE iterateMapWith #-}
746+{-# INLINE iterateMergeMapWith #-}
656−iterateMapWith ::
747+iterateMergeMapWith ::
657 (Stream m a -> Stream m a -> Stream m a)
748 (Stream m a -> Stream m a -> Stream m a)
658 -> (a -> Stream m a)
749 -> (a -> Stream m a)
659 -> Stream m a
750 -> Stream m a
660 -> Stream m a
751 -> Stream m a
661−iterateMapWith combine f = concatMapWith combine go
752+iterateMergeMapWith combine f = iterateStream
663− go x = fromPure x `combine` concatMapWith combine go (f x)
756+ iterateStream = mergeMapWith combine generate
758+ generate x = fromPure x `combine` iterateStream (f x)
665−-- | Same as @iterateMapWith Stream.serial@ but more efficient due to stream
760+-- | Same as @iterateConcatMapWith Stream.append@ but more efficient due to
666−-- fusion.
761+-- stream fusion.
668-- /Unimplemented/
763-- /Unimplemented/
669{-# INLINE iterateUnfold #-}
764{-# INLINE iterateUnfold #-}
@@ -689,27 +784,45 @@ iterateUnfold = undefined
690-- /Pre-release/
785-- /Pre-release/
692−{-# INLINE iterateSmapMWith #-}
787+{-# INLINE iterateConcatScanWith #-}
693−iterateSmapMWith
788+iterateConcatScanWith
694 :: Monad m
789 :: Monad m
695 => (Stream m a -> Stream m a -> Stream m a)
790 => (Stream m a -> Stream m a -> Stream m a)
696 -> (b -> a -> m (b, Stream m a))
791 -> (b -> a -> m (b, Stream m a))
698 -> Stream m a
793 -> Stream m a
699 -> Stream m a
794 -> Stream m a
700−iterateSmapMWith combine f initial stream =
795+iterateConcatScanWith combine f initial stream =
701− concatMap
796+ concatEffect $ do
702− (\b -> concatMapWith combine (go b) stream)
797+ b <- initial
703− (fromEffect initial)
798+ iterateStream (b, stream)
707− go b a = fromPure a `combine` feedback b a
802+ iterateStream (b, s) = pure $ concatMapWith combine (generate b) s
709− feedback b a =
804+ generate b a = fromPure a `combine` feedback b a
711− (\(b1, s) -> concatMapWith combine (go b1) s)
806+ feedback b a = concatEffect $ f b a >>= iterateStream
712− (fromEffect $ f b a)
807+
808+-- Next stream is to be generated by the return value of the previous stream. A
809+-- general intuitive way of doing that could be to use an appending monad
810+-- instance for streams where the result of the previous stream is used to
811+-- generate the next one. In the first pass we can just emit the values in the
812+-- stream and keep building a buffered list/stream, once done we can then
813+-- process the buffered stream.
815+{-# INLINE iterateConcatScan #-}
819+ -> (b -> m (Maybe (b, Stream m a)))
822+iterateConcatScan scanner generate initial =
824+ $ D.iterateConcatScan
825+ scanner (fmap (fmap (fmap toStreamD)) . generate) initial
714------------------------------------------------------------------------------
827------------------------------------------------------------------------------
715-- Either streams
828-- Either streams
@@ -731,24 +844,27 @@ concatMapEitherWith
731concatMapEitherWith = undefined
844concatMapEitherWith = undefined
847+-- XXX We should prefer using the Maybe stream returning signatures over this.
848+-- This API should perhaps be removed in favor of those.
734-- | In an 'Either' stream iterate on 'Left's. This is a special case of
850-- | In an 'Either' stream iterate on 'Left's. This is a special case of
735−-- 'iterateMapWith':
851+-- 'iterateConcatMapWith':
737−-- >>> iterateMapLeftsWith combine f = Stream.iterateMapWith combine (either f (const Stream.nil))
853+-- >>> iterateLeftsConcatMapWith combine f = Stream.iterateConcatMapWith combine (either f (const Stream.nil))
739-- To traverse a directory tree:
855-- To traverse a directory tree:
741-- >>> input = Stream.fromPure (Left ".")
857-- >>> input = Stream.fromPure (Left ".")
742−-- >>> ls = Stream.iterateMapLeftsWith Stream.append Dir.readEither input
858+-- >>> ls = Stream.iterateLeftsConcatMapWith Stream.append Dir.readEither input
744-- /Pre-release/
860-- /Pre-release/
746−{-# INLINE iterateMapLeftsWith #-}
862+{-# INLINE iterateLeftsConcatMapWith #-}
747−iterateMapLeftsWith
863+iterateLeftsConcatMapWith
748 :: (b ~ Either a c)
864 :: (b ~ Either a c)
749 => (Stream m b -> Stream m b -> Stream m b)
865 => (Stream m b -> Stream m b -> Stream m b)
750 -> (a -> Stream m b)
866 -> (a -> Stream m b)
751 -> Stream m b
867 -> Stream m b
752 -> Stream m b
868 -> Stream m b
753−iterateMapLeftsWith combine f =
869+iterateLeftsConcatMapWith combine f =
754− iterateMapWith combine (either f (const nil))
870+ iterateConcatMapWith combine (either f (const nil))