@@ -23,22 +23,36 @@ module Streamly.Internal.Data.Consumer.Type
2323 -- * Types
2424 Consumer (..)
2525
26− -- Combinators
27− , lmapM
28− , rmapM
26+ -- * Constructors
27+ , foldl'
2928
30− -- Consumers
29+ -- * Consumers
30+ -- ** Accumulators
31+ , sconcat
3132 , drainBy
33+
34+ -- * Combinators
35+ , lmapM
36+ , rmapM
3237 , take
3338 )
3439where
3540
3641import Control.Monad ((>=>))
42+#if __GLASGOW_HASKELL__ < 804
43+import Data.Semigroup (Semigroup((<>)))
44+#endif
3745import Fusion.Plugin.Types (Fuse(..))
3846import Streamly.Internal.Data.Fold.Step (Step(..), mapMStep)
3947
4048import Prelude hiding (take)
4149
50+-- $setup
51+-- >>> :m
52+-- >>> import qualified Streamly.Internal.Data.Consumer.Type as Consumer
53+-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
54+-- >>> import qualified Streamly.Internal.Data.Stream.IsStream as Stream
55+
4256-- All folds in the Fold module should be implemented using Consumers.
4357--
4458-- | Like 'Fold' except that the initial state of the accmulator can be
@@ -51,6 +65,27 @@ data Consumer m c a b =
5165 -- | @Fold @ @ step @ @ inject @ @ extract@
5266 forall s. Consumer (s -> a -> m (Step s b)) (c -> m (Step s b)) (s -> m b)
5367
68+------------------------------------------------------------------------------
69+-- Left fold constructors
70+------------------------------------------------------------------------------
71+
72+-- | Make a consumer from a left fold style pure step function.
73+--
74+-- If your 'Fold' returns only 'Partial' (i.e. never returns a 'Done') then you
75+-- can use @foldl'*@ constructors.
76+--
77+-- See also: @Streamly.Prelude.foldl'@
78+--
79+-- /Internal/
80+--
81+{-# INLINE foldl' #-}
82+foldl' :: Monad m => (b -> a -> b) -> Consumer m b a b
83+foldl' step =
84+ Consumer
85+ (\s a -> return $ Partial $ step s a)
86+ (return . Partial)
87+ return
88+
5489------------------------------------------------------------------------------
5590-- Mapping on input
5691------------------------------------------------------------------------------
@@ -101,6 +136,27 @@ drainBy f = Consumer step inject extract
101136
102137 extract _ = return ()
103138
139+------------------------------------------------------------------------------
140+-- Semigroup
141+------------------------------------------------------------------------------
142+
143+-- | Append the elements of an input stream to a provided starting value.
144+--
145+-- >>> stream = Stream.map Data.Monoid.Sum $ Stream.enumerateFromTo 1 10
146+-- >>> Stream.fold (Fold.fromConsumer Consumer.sconcat 10) stream
147+-- Sum {getSum = 65}
148+--
149+-- >>> sconcat = Consumer.foldl' (<>)
150+--
151+-- /Internal/
152+{-# INLINE sconcat #-}
153+sconcat :: (Monad m, Semigroup a) => Consumer m a a a
154+sconcat = foldl' (<>)
155+
156+------------------------------------------------------------------------------
157+-- Transformation
158+------------------------------------------------------------------------------
159+
104160-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x
105161{-# ANN type Tuple'Fused Fuse #-}
106162data Tuple'Fused a b = Tuple'Fused !a !b deriving Show