on master help sign in
Add (commented as of now) custom scan operations
commit4f1b845e32fd35131902248036fb1cbb2f7e0329 authorHarendra Kumar <harendra@composewell.com> dateTue 7 Mar 2023, 00:15 (4 years ago) parent2562e3e
2 files changed+74
core/src/Streamly/Data/Stream.hs+17 core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+57
core/src/Streamly/Data/Stream.hs+17
@@ -292,9 +292,26 @@ module Streamly.Data.Stream
292292 --
293293 -- See also: "Streamly.Internal.Data.Stream.Transform" for
294294 -- @Pre-release@ functions.
295+
296+ {-
297+ -- ** Left scans
298+ -- | We can perform scans using folds with the 'scan' combinator in the
299+ -- next section. However, the combinators supplied in this section are
300+ -- better amenable to stream fusion when combined with other operations.
301+ -- Note that 'postscan' using folds fuses well and does not require custom
302+ -- combinators like these.
303+ , scanl'
304+ , scanlM'
305+ , scanl1'
306+ , scanl1M'
307+ -}
308+
309+ -- ** Scanning By 'Fold'
295310 , scan
296311 , postscan
297312 -- XXX postscan1 can be implemented using Monoids or Refolds.
313+
314+ -- ** Specific scans
298315 -- Indexing can be considered as a special type of zipping where we zip a
299316 -- stream with an index stream.
300317 , indexed
core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+57
@@ -749,6 +749,8 @@ postscanlM fstep begin (Stream step state) = Stream step' Nothing
749749postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a
750750postscanl f seed = postscanlM (\a b -> return (f a b)) (return seed)
751751
752+-- | Like 'scanl'' but with a monadic step function and a monadic seed.
753+--
752754{-# INLINE_NORMAL scanlM' #-}
753755scanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b
754756scanlM' fstep begin (Stream step state) = Stream step' Nothing
@@ -783,6 +785,51 @@ scanlMAfter' :: Monad m
783785scanlMAfter' fstep initial done s =
784786 initial `consM` postscanlMAfter' fstep initial done s
785787
788+-- >>> scanl' f z xs = z `Stream.cons` postscanl' f z xs
789+
790+-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,
791+-- however it adds an extra element.
792+--
793+-- >>> Stream.toList $ Stream.scanl' (+) 0 $ Stream.fromList [1,2,3,4]
794+-- [0,1,3,6,10]
795+--
796+-- >>> Stream.toList $ Stream.scanl' (flip (:)) [] $ Stream.fromList [1,2,3,4]
797+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]
798+--
799+-- The output of 'scanl'' is the initial value of the accumulator followed by
800+-- all the intermediate steps and the final result of 'foldl''.
801+--
802+-- By streaming the accumulated state after each fold step, we can share the
803+-- state across multiple stages of stream composition. Each stage can modify or
804+-- extend the state, do some processing with it and emit it for the next stage,
805+-- thus modularizing the stream processing. This can be useful in
806+-- stateful or event-driven programming.
807+--
808+-- Consider the following monolithic example, computing the sum and the product
809+-- of the elements in a stream in one go using a @foldl'@:
810+--
811+-- >>> Stream.fold (Fold.foldl' (\(s, p) x -> (s + x, p * x)) (0,1)) $ Stream.fromList [1,2,3,4]
812+-- (10,24)
813+--
814+-- Using @scanl'@ we can make it modular by computing the sum in the first
815+-- stage and passing it down to the next stage for computing the product:
816+--
817+-- >>> :{
818+-- Stream.fold (Fold.foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1))
819+-- $ Stream.scanl' (\(s, _) x -> (s + x, x)) (0,1)
820+-- $ Stream.fromList [1,2,3,4]
821+-- :}
822+-- (10,24)
823+--
824+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF. To avoid building
825+-- lazy expressions inside the accumulator, it is recommended that a strict
826+-- data structure is used for accumulator.
827+--
828+-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)
829+-- >>> scanl' f z xs = Stream.scanlM' (\a b -> return (f a b)) (return z) xs
830+--
831+-- See also: 'usingStateT'
832+--
786833{-# INLINE scanl' #-}
787834scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b
788835scanl' f seed = scanlM' (\a b -> return (f a b)) (return seed)
@@ -835,6 +882,9 @@ scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
835882scanl1 f = scanl1M (\x y -> return (f x y))
836883
837884-- Adapted from the vector package
885+
886+-- | Like 'scanl1'' but with a monadic step function.
887+--
838888{-# INLINE_NORMAL scanl1M' #-}
839889scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a
840890scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)
@@ -856,6 +906,13 @@ scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)
856906 Skip s -> return $ Skip (s, Just acc)
857907 Stop -> return Stop
858908
909+-- | Like 'scanl'' but for a non-empty stream. The first element of the stream
910+-- is used as the initial value of the accumulator. Does nothing if the stream
911+-- is empty.
912+--
913+-- >>> Stream.toList $ Stream.scanl1' (+) $ Stream.fromList [1,2,3,4]
914+-- [1,3,6,10]
915+--
859916{-# INLINE scanl1' #-}
860917scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a
861918scanl1' f = scanl1M' (\x y -> return (f x y))