@@ -271,6 +271,7 @@ module Streamly.Internal.Prelude
271271 , intercalateSuffix
272272 , interpose
273273 , interposeSuffix
274+ , concatMapTreeWith
274275
275276 -- -- ** Breaking
276277
@@ -2614,6 +2615,72 @@ interposeSuffix :: (IsStream t, Monad m)
26142615interposeSuffix x unf str =
26152616 D.fromStreamD $ D.interposeSuffix (return x) unf (D.toStreamD str)
26162617
2618+------------------------------------------------------------------------------
2619+-- Flattening Trees
2620+------------------------------------------------------------------------------
2621+
2622+-- | Traverse a forest with recursive tree structures whose non-leaf nodes are
2623+-- of type @a@ and leaf nodes are of type @b@, flattening all the trees into
2624+-- streams and combining the streams into a single stream consisting of both
2625+-- leaf and non-leaf nodes.
2626+--
2627+-- 'concatMapTreeWith' is a generalization of 'concatMap', using a recursive
2628+-- feedback loop to append the non-leaf nodes back to the input stream enabling
2629+-- recursive traversal. 'concatMap' flattens a single level nesting whereas
2630+-- 'concatMapTreeWith' flattens a recursively nested structure.
2631+--
2632+-- Traversing a directory tree recursively is a canonical use case of
2633+-- 'concatMapTreeWith'.
2634+--
2635+-- /Internal/
2636+--
2637+{-# INLINE concatMapTreeWith #-}
2638+concatMapTreeWith
2639+ :: IsStream t
2640+ => (forall c. t m c -> t m c -> t m c)
2641+ -> (a -> t m (Either a b))
2642+ -> t m (Either a b)
2643+ -> t m (Either a b)
2644+concatMapTreeWith combine f xs = concatMapWith combine go xs
2645+ where
2646+ go (Left tree) = Left tree `K.cons` concatMapWith combine go (f tree)
2647+ go (Right leaf) = yield $ Right leaf
2648+
2649+{-
2650+-- | Like concatMapTreeWith but produces only stream of leaf elements.
2651+--
2652+{-# INLINE concatMapLeavesWith #-}
2653+concatMapLeavesWith :: (IsStream t, MonadAsync m)
2654+ => _ -> (a -> t m (Either a b)) -> t m (Either a b) -> t m b
2655+concatMapLeavesWith combine f xs = undefined
2656+-}
2657+
2658+{-
2659+{-# INLINE concatUnfoldTree #-}
2660+concatUnfoldTree :: (IsStream t, MonadAsync m)
2661+ => Unfold m a (Either a b) -> t m (Either a b) -> t m (Either a b)
2662+concatUnfoldTree unf xs = undefined
2663+
2664+------------------------------------------------------------------------------
2665+-- Feedback loop
2666+------------------------------------------------------------------------------
2667+
2668+-- Flatten a stream with a feedback loop back into the input. For example, if
2669+-- the output stream may generate an exception the exceptions can be fed back
2670+-- to the input to take any corrective action. For example, we may retry the
2671+-- action again or do nothing or log the errors. For the retry case we need a
2672+-- feedback loop.
2673+--
2674+-- We can perhaps even implement the SVar using this. The stream we are mapping
2675+-- on is the work queue. When evaluated it results in either a leaf element to
2676+-- yield or a tail stream to queue back to the work queue.
2677+--
2678+{-# INLINE concatMapLoopWith #-}
2679+concatMapLoopWith :: (IsStream t, MonadAsync m)
2680+ => _ -> (a -> t m (Either b c)) -> (b -> t m a) -> t m a -> t m c
2681+concatMapLoopWith combine f xs = undefined
2682+-}
2683+
26172684------------------------------------------------------------------------------
26182685-- Grouping/Splitting
26192686------------------------------------------------------------------------------