on 1946eb8 help sign in
524 lines 18 kB history raw
1-- |
2-- Module : Streamly.Data.Stream
3-- Copyright : (c) 2017 Composewell Technologies
4--
5-- License : BSD3
6-- Maintainer : streamly@composewell.com
7-- Stability : released
8-- Portability : GHC
9--
10-- To run examples in this module:
11--
12-- >>> import qualified Streamly.Data.Fold as Fold
13-- >>> import qualified Streamly.Data.Stream as Stream
14--
15-- We will add some more imports in the examples as needed.
16--
17-- For effectful streams we will use the following IO action:
18--
19-- >>> effect n = print n >> return n
20--
21-- = Overview
22--
23-- Streamly is a framework for modular data flow based programming and
24-- declarative concurrency. Powerful stream fusion framework in streamly
25-- allows high performance combinatorial programming even when using byte level
26-- streams. Streamly API is similar to Haskell lists.
27--
28-- == Console Echo Example
29--
30-- In the following example, 'repeatM' generates an infinite stream of 'String'
31-- by repeatedly performing the 'getLine' IO action. 'mapM' then applies
32-- 'putStrLn' on each element in the stream converting it to stream of '()'.
33-- Finally, 'drain' folds the stream to IO discarding the () values, thus
34-- producing only effects.
35--
36-- >>> import Data.Function ((&))
37--
38-- >>> :{
39-- echo =
40-- Stream.repeatM getLine -- Stream IO String
41-- & Stream.mapM putStrLn -- Stream IO ()
42-- & Stream.fold Fold.drain -- IO ()
43-- :}
44--
45-- This is a console echo program. It is an example of a declarative loop
46-- written using streaming combinators. Compare it with an imperative @while@
47-- loop.
48--
49-- Hopefully, this gives you an idea how we can program declaratively by
50-- representing loops using streams. In this module, you can find all
51-- "Data.List" like functions and many more powerful combinators to perform
52-- common programming tasks.
53--
54-- == Performance Notes
55--
56-- The 'Stream' type represents a stream by composing state as data which
57-- enables stream fusion. Stream fusion generates a tight loop without any
58-- constructor allocations between the stages, providing C like performance for
59-- the loop. Stream fusion works when multiple functions are combined in a
60-- pipeline statically. Therefore, the operations in this module must be
61-- inlined and must not be used recursively to allow for stream fusion.
62--
63-- Using the 'Stream' type binary stream construction operations like 'cons',
64-- 'append' etc. degrade quadratically (O(n^2)) when combined many times. If
65-- you need to combine these operations, say more than 50 times in a single
66-- loop, then you should use the continuation style stream type 'StreamK'
67-- instead. Also, if you need to use these operations in a recursive loop you
68-- should use 'StreamK' instead.
69--
70-- The 'StreamK' type represents a stream by composing function calls,
71-- therefore, a function call overhead is incurred at each composition. It is
72-- quite fast in general but may be a few times slower than a fused stream.
73-- However, it allows for scalable dynamic composition and control flow
74-- manipulation. Using the 'StreamK' type binary operations like 'cons' and
75-- 'append' provide linear (O(n)) performance.
76--
77-- 'Stream' and 'StreamK' types can be interconverted.
78--
79-- == Useful Idioms
80--
81-- >>> fromListM = Stream.sequence . Stream.fromList
82-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
83--
84-- Also see "Streamly.Internal.Data.Stream" module for many more @Pre-release@
85-- combinators. See the <https://github.com/composewell/streamly-examples>
86-- repository for many more real world examples of stream programming.
87--
88module Streamly.Data.Stream
89 (
90 Stream
91 -- * Construction
92 -- | Functions ending in the general shape @b -> Stream m a@.
93 --
94 -- See also: "Streamly.Internal.Data.Stream.Generate" for
95 -- @Pre-release@ functions.
97 -- ** Primitives
98 -- | Primitives to construct a stream from pure values or monadic actions.
99 -- All other stream construction and generation combinators described later
100 -- can be expressed in terms of these primitives. However, the special
101 -- versions provided in this module can be much more efficient in most
102 -- cases. Users can create custom combinators using these primitives.
103 , nil
104 , nilM
105 , cons
106 , consM
108 -- ** Unfolding
109 -- | 'unfoldrM' is the most general way of generating a stream efficiently.
110 -- All other generation operations can be expressed using it.
111 , unfoldr
112 , unfoldrM
114 -- ** From Values
115 -- | Generate a monadic stream from a seed value or values.
116 , fromPure
117 , fromEffect
118 , repeat
119 , repeatM
120 , replicate
121 , replicateM
123 -- Note: Using enumeration functions e.g. 'Prelude.enumFromThen' turns out
124 -- to be slightly faster than the idioms like @[from, then..]@.
125 --
126 -- ** Enumeration
127 -- | We can use the 'Enum' type class to enumerate a type producing a list
128 -- and then convert it to a stream:
129 --
130 -- @
131 -- 'fromList' $ 'Prelude.enumFromThen' from then
132 -- @
133 --
134 -- However, this is not particularly efficient.
135 -- The 'Enumerable' type class provides corresponding functions that
136 -- generate a stream instead of a list, efficiently.
138 , Enumerable (..)
139 , enumerate
140 , enumerateTo
142 -- ** Iteration
143 , iterate
144 , iterateM
146 -- ** From Containers
147 -- | Convert an input structure, container or source into a stream. All of
148 -- these can be expressed in terms of primitives.
149 , fromList
151 -- ** From Unfolds
152 -- | Most of the above stream generation operations can also be expressed
153 -- using the corresponding unfolds in the "Streamly.Data.Unfold" module.
154 , unfold -- XXX rename to fromUnfold?
156 -- * Elimination
157 -- | Functions ending in the general shape @Stream m a -> m b@ or @Stream m
158 -- a -> m (b, Stream m a)@
159 --
160 -- See also: "Streamly.Internal.Data.Stream.Eliminate" for @Pre-release@
161 -- functions.
163-- EXPLANATION: In imperative terms a fold can be considered as a loop over the stream
164-- that reduces the stream to a single value.
165-- Left and right folds both use a fold function @f@ and an identity element
166-- @z@ (@zero@) to deconstruct a recursive data structure and reconstruct a
167-- new data structure. The new structure may be a recursive construction (a
168-- container) or a non-recursive single value reduction of the original
169-- structure.
170--
171-- Both right and left folds are mathematical duals of each other, they are
172-- functionally equivalent. Operationally, a left fold on a left associated
173-- structure behaves exactly in the same way as a right fold on a right
174-- associated structure. Similarly, a left fold on a right associated structure
175-- behaves in the same way as a right fold on a left associated structure.
176-- However, the behavior of a right fold on a right associated structure is
177-- operationally different (even though functionally equivalent) than a left
178-- fold on the same structure.
179--
180-- On right associated structures like Haskell @cons@ lists or Streamly
181-- streams, a lazy right fold is naturally suitable for lazy recursive
182-- reconstruction of a new structure, while a strict left fold is naturally
183-- suitable for efficient reduction. In right folds control is in the hand of
184-- the @puller@ whereas in left folds the control is in the hand of the
185-- @pusher@.
186--
187-- The behavior of right and left folds are described in detail in the
188-- individual fold's documentation. To illustrate the two folds for right
189-- associated @cons@ lists:
190--
191-- > foldr :: (a -> b -> b) -> b -> [a] -> b
192-- > foldr f z [] = z
193-- > foldr f z (x:xs) = x `f` foldr f z xs
194-- >
195-- > foldl :: (b -> a -> b) -> b -> [a] -> b
196-- > foldl f z [] = z
197-- > foldl f z (x:xs) = foldl f (z `f` x) xs
198--
199-- @foldr@ is conceptually equivalent to:
200--
201-- > foldr f z [] = z
202-- > foldr f z [x] = f x z
203-- > foldr f z xs = foldr f (foldr f z (tail xs)) [head xs]
204--
205-- @foldl@ is conceptually equivalent to:
206--
207-- > foldl f z [] = z
208-- > foldl f z [x] = f z x
209-- > foldl f z xs = foldl f (foldl f z (init xs)) [last xs]
210--
211-- Left and right folds are duals of each other.
212--
213-- @
214-- foldr f z xs = foldl (flip f) z (reverse xs)
215-- foldl f z xs = foldr (flip f) z (reverse xs)
216-- @
217--
218-- More generally:
219--
220-- @
221-- foldr f z xs = foldl g id xs z where g k x = k . f x
222-- foldl f z xs = foldr g id xs z where g x k = k . flip f x
223-- @
224--
226-- NOTE: Folds are inherently serial as each step needs to use the result of
227-- the previous step. However, it is possible to fold parts of the stream in
228-- parallel and then combine the results using a monoid.
230 -- ** Primitives
231 -- Consuming a part of the stream and returning the rest. Functions
232 -- ending in the general shape @Stream m a -> m (b, Stream m a)@
233 , uncons
235 -- ** Strict Left Folds
236 -- XXX Need to have a general parse operation here which can be used to
237 -- express all others.
238 , fold -- XXX rename to run? We can have a Stream.run and Fold.run.
239 -- XXX fold1 can be achieved using Monoids or Refolds.
240 -- XXX We can call this just "break" and parseBreak as "munch"
241 , foldBreak
243 -- XXX should we have a Fold returning function in stream module?
244 -- , foldAdd
245 -- , buildl
247 -- ** Parsing
248 , parse
249 -- , parseBreak
251 -- ** Lazy Right Folds
252 -- | Consuming a stream to build a right associated expression, suitable
253 -- for lazy evaluation. Evaluation of the input happens when the output of
254 -- the fold is evaluated, the fold output is a lazy thunk.
255 --
256 -- This is suitable for stream transformation operations, for example,
257 -- operations like mapping a function over the stream.
258 , foldrM
259 , foldr
261 -- ** Specific Folds
262 -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
263 -- folds that may be commonly used or may have an edge in performance in
264 -- some cases are provided here.
265 -- , drain
266 , toList
268 -- * Mapping
269 -- | Stateless one-to-one transformations. Use 'fmap' for mapping a pure
270 -- function on a stream.
272 -- EXPLANATION:
273 -- In imperative terms a map operation can be considered as a loop over
274 -- the stream that transforms the stream into another stream by performing
275 -- an operation on each element of the stream.
276 --
277 -- 'map' is the least powerful transformation operation with strictest
278 -- guarantees. A map, (1) is a stateless loop which means that no state is
279 -- allowed to be carried from one iteration to another, therefore,
280 -- operations on different elements are guaranteed to not affect each
281 -- other, (2) is a strictly one-to-one transformation of stream elements
282 -- which means it guarantees that no elements can be added or removed from
283 -- the stream, it can merely transform them.
284 , sequence
285 , mapM
286 , trace
287 , tap
288 , delay
290 -- * Scanning
291 -- | Stateful one-to-one transformations.
292 --
293 -- See also: "Streamly.Internal.Data.Stream.Transform" for
294 -- @Pre-release@ functions.
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 -}
309 -- ** Scanning By 'Fold'
310 , scan
311 , postscan
312 -- XXX postscan1 can be implemented using Monoids or Refolds.
314 -- ** Specific scans
315 -- Indexing can be considered as a special type of zipping where we zip a
316 -- stream with an index stream.
317 , indexed
319 -- * Insertion
320 -- | Add elements to the stream.
322 -- Inserting elements is a special case of interleaving/merging streams.
323 , insertBy
324 , intersperseM
325 , intersperseM_
326 , intersperse
328 -- * Filtering
329 -- | Remove elements from the stream.
331 -- ** Stateless Filters
332 -- | 'mapMaybeM' is the most general stateless filtering operation. All
333 -- other filtering operations can be expressed using it.
335 -- EXPLANATION:
336 -- In imperative terms a filter over a stream corresponds to a loop with a
337 -- @continue@ clause for the cases when the predicate fails.
339 , mapMaybe
340 , mapMaybeM
341 , filter
342 , filterM
344 -- Filter and concat
345 , catMaybes
346 , catLefts
347 , catRights
348 , catEithers
350 -- ** Stateful Filters
351 -- | 'scanMaybe' is the most general stateful filtering operation. The
352 -- filtering folds (folds returning a 'Maybe' type) in
353 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
354 -- perform stateful filtering operations in general.
355 , scanMaybe
356 , take
357 , takeWhile
358 , takeWhileM
359 , drop
360 , dropWhile
361 , dropWhileM
363 -- XXX These are available as scans in folds. We need to check the
364 -- performance though. If these are common and we need convenient stream
365 -- ops then we can expose these.
367 -- , deleteBy
368 -- , uniq
369 -- , uniqBy
371 -- -- ** Sampling
372 -- , strideFromThen
374 -- -- ** Searching
375 -- Finding the presence or location of an element, a sequence of elements
376 -- or another stream within a stream.
378 -- -- ** Searching Elements
379 -- , findIndices
380 -- , elemIndices
382 -- * Combining Two Streams
383 -- ** Appending
384 , append
386 -- ** Interleaving
387 -- | When interleaving more than two streams you may want to interleave
388 -- them pairwise creating a balanced binary merge tree.
389 , interleave
391 -- ** Merging
392 -- | When merging more than two streams you may want to merging them
393 -- pairwise creating a balanced binary merge tree.
394 --
395 -- Merging of @n@ streams can be performed by combining the streams pair
396 -- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used
397 -- with 'concatMapWith' it will have O(n^2) performance.
399 , mergeBy
400 , mergeByM
402 -- ** Zipping
403 -- | When zipping more than two streams you may want to zip them
404 -- pairwise creating a balanced binary tree.
405 --
406 -- Zipping of @n@ streams can be performed by combining the streams pair
407 -- wise using 'mergeMapWith' with O(n * log n) time complexity. If used
408 -- with 'concatMapWith' it will have O(n^2) performance.
409 , zipWith
410 , zipWithM
411 -- , ZipStream (..)
413 -- ** Cross Product
414 -- XXX The argument order in this operation is such that it seems we are
415 -- transforming the first stream using the second stream because the second
416 -- stream is evaluated many times or buffered and better be finite, first
417 -- stream could potentially be infinite. In the tradition of using the
418 -- transformed stream at the end we can have a flipped version called
419 -- "crossMap" or "nestWith".
420 , crossWith
421 -- , cross
422 -- , joinInner
423 -- , CrossStream (..)
425 -- * Unfold Each
426 , unfoldMany
427 , intercalate
428 , intercalateSuffix
430 -- * Stream of streams
431 -- | Stream operations like map and filter represent loop processing in
432 -- imperative programming terms. Similarly, the imperative concept of
433 -- nested loops are represented by streams of streams. The 'concatMap'
434 -- operation represents nested looping.
435 -- A 'concatMap' operation loops over the input stream and then for each
436 -- element of the input stream generates another stream and then loops over
437 -- that inner stream as well producing effects and generating a single
438 -- output stream.
439 --
440 -- One dimension loops are just a special case of nested loops. For
441 -- example, 'concatMap' can degenerate to a simple map operation:
442 --
443 -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
444 --
445 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
446 -- 'nil' stream:
447 --
448 -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
449 --
451 , concatEffect
452 , concatMap
453 , concatMapM
455 -- * Repeated Fold
456 , foldMany -- XXX Rename to foldRepeat
457 , parseMany
458 , Array.chunksOf
460 -- * Buffered Operations
461 -- | Operations that require buffering of the stream.
462 -- Reverse is essentially a left fold followed by an unfold.
463 , reverse
465 -- * Multi-Stream folds
466 -- | Operations that consume multiple streams at the same time.
467 , eqBy
468 , cmpBy
469 , isPrefixOf
470 , isSubsequenceOf
472 -- trimming sequences
473 , stripPrefix
475 -- Exceptions and resource management depend on the "exceptions" package
476 -- XXX We can have IO Stream operations not depending on "exceptions"
477 -- in Exception.Base
479 -- * Exceptions
480 -- | Most of these combinators inhibit stream fusion, therefore, when
481 -- possible, they should be called in an outer loop to mitigate the cost.
482 -- For example, instead of calling them on a stream of chars call them on a
483 -- stream of arrays before flattening it to a stream of chars.
484 --
485 -- See also: "Streamly.Internal.Data.Stream.Exception" for
486 -- @Pre-release@ functions.
488 , onException
489 , handle
491 -- * Resource Management
492 -- | 'bracket' is the most general resource management operation, all other
493 -- operations can be expressed using it. These functions have IO suffix
494 -- because the allocation and cleanup functions are IO actions. For
495 -- generalized allocation and cleanup functions see the functions without
496 -- the IO suffix in the "streamly" package.
497 , before
498 , afterIO
499 , finallyIO
500 , bracketIO
501 , bracketIO3
503 -- * Transforming Inner Monad
505 , morphInner
506 , liftInner
507 , runReaderT
508 , runStateT
510 -- -- * Stream Types
511 -- $serial
512 -- , Interleave
513 -- , Zip
514 )
515where
517import qualified Streamly.Internal.Data.Array.Type as Array
518import Streamly.Internal.Data.Stream.StreamD
519import Prelude
520 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
521 foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
522 notElem, maximum, minimum, head, last, tail, length, null,
523 reverse, iterate, init, and, or, lookup, foldr1, (!!),
524 scanl, scanl1, repeat, replicate, concatMap, span)