2-- Module : Streamly.Internal.Data.Stream.Reduce
3-- Copyright : (c) 2017 Composewell Technologies
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
9-- Reduce streams by streams, folds or parsers.
11module Streamly.Internal.Data.Stream.Reduce
13 -- * Reduce By Streams
20 -- Reduce a stream by folding or parsing chunks of the stream. Functions
21 -- generally ending in these shapes:
24 -- f (Fold m a b) -> Stream m a -> Stream m b
25 -- f (Parser m a b) -> Stream m a -> Stream m b
29 -- | Apply folds on a stream.
38 -- | Element unaware grouping.
41 -- * Reduce By Parsers
43 -- | Apply parsers on a stream.
53import Control.Monad.Catch (MonadThrow)
54import Control.Monad.IO.Class (MonadIO(..))
55import Streamly.Internal.Data.Array.Unboxed.Type (Array)
56import Streamly.Internal.Data.Fold.Type (Fold (..))
57import Streamly.Internal.Data.Parser (Parser (..))
58import Streamly.Internal.Data.Refold.Type (Refold (..))
59import Streamly.Internal.Data.Stream.Bottom (foldManyPost)
60import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)
61import Streamly.Internal.Data.Unboxed (Unboxed)
63import qualified Streamly.Internal.Data.Array.Unboxed.Type as Array
64import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
65import qualified Streamly.Internal.Data.Stream.StreamD as D
67import Prelude hiding (concatMap, map)
71-- >>> import Prelude hiding (zipWith, concatMap, concat)
72-- >>> import Streamly.Internal.Data.Stream as Stream
73-- >>> import qualified Streamly.Data.Fold as Fold
74-- >>> import qualified Streamly.Internal.Data.Fold as Fold
75-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
76-- >>> import qualified Streamly.Internal.Data.Parser as Parser
77-- >>> import qualified Streamly.Data.Array.Unboxed as Array
79------------------------------------------------------------------------------
81------------------------------------------------------------------------------
83-- | Drop prefix from the input stream if present.
88{-# INLINE dropPrefix #-}
91 Stream m a -> Stream m a -> Stream m a
92dropPrefix = error "Not implemented yet!"
94-- | Drop all matching infix from the input stream if present. Infix stream
95-- may be consumed multiple times.
97-- Space: @O(n)@ where n is the length of the infix.
100{-# INLINE dropInfix #-}
102 -- (Monad m, Eq a) =>
103 Stream m a -> Stream m a -> Stream m a
104dropInfix = error "Not implemented yet!"
106-- | Drop suffix from the input stream if present. Suffix stream may be
107-- consumed multiple times.
109-- Space: @O(n)@ where n is the length of the suffix.
112{-# INLINE dropSuffix #-}
114 -- (Monad m, Eq a) =>
115 Stream m a -> Stream m a -> Stream m a
116dropSuffix = error "Not implemented yet!"
118------------------------------------------------------------------------------
120------------------------------------------------------------------------------
122-- Splitting operations that take a predicate and a Fold can be
123-- expressed using parseMany. Operations like chunksOf, intervalsOf, split*,
124-- can be expressed using parseMany when used with an appropriate Parser.
126-- XXX We need takeGE/takeBetween to implement "some" using "many".
128-- | Apply a 'Fold' repeatedly on a stream and emit the fold outputs in the
131-- To sum every two contiguous elements in a stream:
133-- >>> f = Fold.take 2 Fold.sum
134-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.fromList [1..10]
137-- On an empty stream the output is empty:
139-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.fromList []
142-- Note @Stream.foldMany (Fold.take 0)@ would result in an infinite loop in a
145-- Note 'foldMany' on an empty stream results in an empty stream. Therefore,
146-- @Stream.fold f@ is not the same as @Stream.head . Stream.foldMany f@.
149{-# INLINE foldMany #-}
155foldMany f m = fromStreamD $ D.foldMany f (toStreamD m)
157-- | Like 'foldMany' but using the 'Refold' type instead of 'Fold'.
160{-# INLINE refoldMany #-}
161refoldMany :: Monad m =>
162 Refold m c a b -> m c -> Stream m a -> Stream m b
163refoldMany f action = fromStreamD . D.refoldMany f action . toStreamD
165-- | Apply a stream of folds to an input stream and emit the results in the
170{-# INLINE foldSequence #-}
173 Stream m (Fold m a b)
176foldSequence _f _m = undefined
178-- | Iterate a fold generator on a stream. The initial value @b@ is used to
179-- generate the first fold, the fold is applied on the stream and the result of
180-- the fold is used to generate the next fold and so on.
182-- >>> import Data.Monoid (Sum(..))
183-- >>> f x = return (Fold.take 2 (Fold.sconcat x))
184-- >>> s = fmap Sum $ Stream.fromList [1..10]
185-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s
188-- This is the streaming equivalent of monad like sequenced application of
189-- folds where next fold is dependent on the previous fold.
193{-# INLINE foldIterateM #-}
195 Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b
196foldIterateM f i m = fromStreamD $ D.foldIterateM f i (toStreamD m)
198-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be
199-- much more efficient due to stream fusion.
202{-# INLINE refoldIterateM #-}
203refoldIterateM :: Monad m =>
204 Refold m b a b -> m b -> Stream m a -> Stream m b
205refoldIterateM c i m = fromStreamD $ D.refoldIterateM c i (toStreamD m)
207------------------------------------------------------------------------------
209------------------------------------------------------------------------------
211-- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the
214-- This is the streaming equivalent of the 'Streamly.Internal.Data.Parser.many'
217-- >>> s = Stream.fromList [1..10]
218-- >>> parser = Parser.takeBetween 0 2 Fold.sum
219-- >>> Stream.fold Fold.toList $ Stream.parseMany parser s
222-- foldMany f = Stream.parseMany (Parser.fromFold f)
224-- Known Issues: When the parser fails there is no way to get the remaining
229{-# INLINE parseMany #-}
236 fromStreamD $ D.parseMany (ParserD.fromParserK p) (toStreamD m)
238-- | Same as parseMany but for StreamD streams.
242{-# INLINE parseManyD #-}
245 => ParserD.Parser m a b
249 fromStreamD $ D.parseMany p (toStreamD m)
251-- | Apply a stream of parsers to an input stream and emit the results in the
256{-# INLINE parseSequence #-}
259 Stream m (Parser m a b)
262parseSequence _f _m = undefined
264-- XXX Change the parser arguments' order
266-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,
267-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds
268-- @test@ is tried again and so on. The parser stops when @test@ succeeds. The
269-- output of @test@ is discarded and the output of @collect@ is emitted in the
270-- output stream. The parser fails if @collect@ fails.
274{-# INLINE parseManyTill #-}
281parseManyTill = undefined
283-- | Iterate a parser generating function on a stream. The initial value @b@ is
284-- used to generate the first parser, the parser is applied on the stream and
285-- the result is used to generate the next parser and so on.
287-- >>> import Data.Monoid (Sum(..))
288-- >>> s = Stream.fromList [1..10]
289-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) 0 $ fmap Sum s
292-- This is the streaming equivalent of monad like sequenced application of
293-- parsers where next parser is dependent on the previous parser.
297{-# INLINE parseIterate #-}
300 => (b -> Parser m a b)
304parseIterate f i m = fromStreamD $
305 D.parseIterate (ParserD.fromParserK . f) i (toStreamD m)
307------------------------------------------------------------------------------
309------------------------------------------------------------------------------
311-- | @arraysOf n stream@ groups the elements in the input stream into arrays of
314-- Same as the following but may be more efficient:
316-- >>> arraysOf n = Stream.foldMany (Array.writeN n)
319{-# INLINE arraysOf #-}
320arraysOf :: (MonadIO m, Unboxed a)
321 => Int -> Stream m a -> Stream m (Array a)
322arraysOf n = fromStreamD . Array.arraysOf n . toStreamD
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list