on e7d2a73 help sign in
streamly/src/Streamly.hs
614 lines 23 kB history raw
1-- |
2-- Module : Streamly
3-- Copyright : (c) 2017 Harendra Kumar
4--
5-- License : BSD3
6-- Maintainer : streamly@composewell.com
7-- Stability : experimental
8-- Portability : GHC
9--
10-- Streamly is a general purpose programming framework using cocnurrent data
11-- flow programming paradigm. It can be considered as a generalization of
12-- Haskell lists to monadic streaming with concurrent composition capability.
13-- The serial stream type in streamly @SerialT m a@ is like the list type @[a]@
14-- parameterized by the monad @m@. For example, @SerialT IO a@ is a moral
15-- equivalent of @[a]@ in the IO monad. Streams are constructed very much like
16-- lists, except that they use 'nil' and 'cons' instead of '[]' and ':'.
17--
18-- @
19-- > import "Streamly"
20-- > import "Streamly.Prelude" (cons, consM)
21-- > import qualified "Streamly.Prelude" as S
22-- >
23-- > S.toList $ 1 \`cons` 2 \`cons` 3 \`cons` nil
24-- [1,2,3]
25-- @
26--
27-- Unlike lists, streams can be constructed from monadic effects:
28--
29-- @
30-- > S.'toList' $ 'getLine' \`consM` 'getLine' \`consM` S.'nil'
31-- hello
32-- world
33-- ["hello","world"]
34-- @
35--
36-- Streams are processed just like lists, with list like combinators, except
37-- that they are monadic and work in a streaming fashion. Here is a simple
38-- console echo program example:
39--
40-- @
41-- > S.drain $ S.repeatM getLine & S.mapM putStrLn
42-- @
43--
44-- @SerialT Identity a@ is a moral equivalent of pure lists. Streamly utilizes
45-- fusion for high performance, therefore, we can represent and process strings
46-- as streams of 'Char', encode and decode the streams to/from UTF8 and
47-- serialize them to @Array Word8@ obviating the need for special purpose
48-- libraries like @bytestring@ and @text@.
49--
50-- For more details
51-- please see the "Streamly.Tutorial" module and the examples directory in this
52-- package.
54{-# LANGUAGE CPP #-}
55{-# LANGUAGE MultiParamTypeClasses #-}
57#if __GLASGOW_HASKELL__ >= 800
58{-# OPTIONS_GHC -Wno-orphans #-}
59#endif
61#include "Streamly/Streams/inline.hs"
63module Streamly
64 (
65 -- * Concepts Overview
66 -- ** Streams
67 -- $streams
69 -- ** Folds
70 -- $folds
72 -- ** Arrays
73 -- $arrays
75 -- * Module Overview
76 -- $streamtypes
78 -- * Type Synonyms
79 MonadAsync
81 -- * Stream transformers
82 -- | A stream represents a sequence of pure or effectful actions. The
83 -- `cons` and `consM` operations and the corresponding operators '.:' and
84 -- '|:' can be used to join pure values or effectful actions in a sequence.
85 -- The effects in the stream can be executed in many different ways
86 -- depending on the type of stream. In other words, the behavior of 'consM'
87 -- depends on the type of the stream.
88 --
89 -- There are three high level categories of streams, /spatially ordered/
90 -- streams, /speculative/ streams and /time ordered/ streams. Spatially
91 -- ordered streams, 'SerialT' and 'WSerialT', execute the effects in serial
92 -- order i.e. one at a time and present the outputs of those effects to the
93 -- consumer in the same order. Speculative streams, 'AheadT', may execute
94 -- many effects concurrently but present the outputs to the consumer in the
95 -- specified spatial order. Time ordered streams, 'AsyncT', 'WAsyncT' and
96 -- 'ParallelT', may execute many effects concurrently and present the
97 -- outputs of those effects to the consumer in time order i.e. as soon as
98 -- the output is generated.
99 --
100 -- We described above how the effects in a sequence are executed for
101 -- different types of streams. The behvavior of the 'Semigroup' and 'Monad'
102 -- instances follow the behavior of 'consM'. Stream generation operations
103 -- like 'repeatM' also execute the effects differently for different
104 -- streams, providing a concurrent generation capability when used with
105 -- stream types that execute effects concurrently. Similarly, effectful
106 -- transformation operations like 'mapM' also execute the transforming
107 -- effects differently for different types of streams.
109 -- ** Serial Streams
110 -- $serial
111 , SerialT
112 , WSerialT
114 -- ** Speculative Streams
115 -- $ahead
116 , AheadT
118 -- ** Asynchronous Streams
119 -- $async
120 , AsyncT
121 , WAsyncT
122 , ParallelT
124 -- ** Zipping Streams
125 -- $zipping
126 , ZipSerialM
127 , ZipAsyncM
129 -- * Parallel Function Application
130 -- $application
131 , (|$)
132 , (|&)
133 , (|$.)
134 , (|&.)
135 , mkAsync
137 -- * Merging Streams
138 -- $sum
139 , serial
140 , wSerial
141 , ahead
142 , async
143 , wAsync
144 , parallel
146 -- * Concurrency Control
147 -- $concurrency
148 , maxThreads
149 , maxBuffer
151 -- * Rate Limiting
152 , Rate (..)
153 , rate
154 , avgRate
155 , minRate
156 , maxRate
157 , constRate
159 -- * Stream Type Adapters
160 -- $adapters
161 , IsStream ()
163 , serially
164 , wSerially
165 , asyncly
166 , aheadly
167 , wAsyncly
168 , parallely
169 , zipSerially
170 , zipAsyncly
171 , adapt
173 -- * IO Streams
174 , Serial
175 , WSerial
176 , Ahead
177 , Async
178 , WAsync
179 , Parallel
180 , ZipSerial
181 , ZipAsync
183 -- * Re-exports
184 , Semigroup (..)
186 -- * Deprecated
187 , Streaming
188 , runStream
189 , runStreaming
190 , runStreamT
191 , runInterleavedT
192 , runAsyncT
193 , runParallelT
194 , runZipStream
195 , runZipAsync
196 , StreamT
197 , InterleavedT
198 , ZipStream
199 , interleaving
200 , zipping
201 , zippingAsync
202 , (<=>)
203 , (<|)
205 -- * Deprecated/Moved
206 -- | These APIs have been moved to other modules
207 , foldWith
208 , foldMapWith
209 , forEachWith
210 )
211where
213import Data.Semigroup (Semigroup(..))
214import Streamly.Internal.Data.SVar (MonadAsync, Rate(..))
215import Streamly.Streams.Ahead
216import Streamly.Streams.Async
217import Streamly.Streams.Combinators
218import Streamly.Streams.Parallel
219import Streamly.Streams.Serial
220import Streamly.Streams.StreamK hiding (serial)
221import Streamly.Streams.Zip
223import qualified Streamly.Prelude as P
224import qualified Streamly.Streams.StreamK as K
226-- XXX provide good succinct examples of pipelining, merging, splitting ect.
227-- below.
228--
229-- $streams
230--
231-- A program is expressed as a network of streams and folds. A stream is a
232-- source or generator of data elements and a fold is a consumer of data elements that
233-- reduces multiple input elements to a single value.
234--
235-- In the following example, a 'Word8' stream is generated by using
236-- 'Streamly.FileSystem.Handle.read' on a file handle, then the
237-- 'Streamly.Prelude.splitBySuffix' transformation splits the stream on
238-- newlines (ascii value 10); it uses the 'Streamly.Data.Fold.drain' fold to reduce
239-- the resulting lines to unit values (@()@), 'Streamly.Prelude.length' fold
240-- then counts the unit elements in the resulting stream which gives us the
241-- number of lines in the file:
242--
243-- > S.length $ S.splitOnSuffix FL.drain 10 $ FH.read fh
244--
245-- The following example folds the lines to arrays of 'Word8' using the
246-- 'Streamly.Memory.Array.writeF' fold and then wraps the lines in square
247-- brackets before writing them to standard output using
248-- 'Streamly.FileSystem.Handle.write':
249--
250-- > wrapLine ln = S.fromList "[" <> A.read ln <> S.fromList "]\n"
251-- > readLines = S.splitOnSuffix A.writeF 10
252-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1
253--
254-- One stream can be appended after another:
255--
256-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1 <> readLines fh2
257--
258-- The following example reads two files concurrently, merges the lines from
259-- the two streams and writes the resulting stream to another file:
260--
261-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1 `parallel` readLines fh2
262--
263-- There are many ways to generate, merge, zip, transform and fold data
264-- streams. Many transformations can be chained in a stream pipeline. See
265-- "Streamly.Prelude" module for combinators to manipulate streams.
267-- $folds
268--
269-- The way stream types in this module like 'SerialT' represent data sources,
270-- the same way the 'Fold' type from "Streamly.Data.Fold" represents data sinks or
271-- reducers of streams. Reducers can be combined to consume a stream source in
272-- many ways. The simplest is to reduce a stream source using a fold e.g.:
273--
274-- > S.runFold FL.length $ S.enumerateTo 100
275--
276-- Folds are consumers of streams and can be used to split a stream into
277-- multiple independent flows. Grouping transforms a stream by applying a fold
278-- on segments of a stream, distributing applies multiple folds in parallel on
279-- the same stream and combines them, partitioning sends different elements of
280-- a stream to different folds, unzipping divides the elements of a stream into
281-- parts and sends them through different folds. Parsers are nothing but a
282-- particular type of folds. Transformations can be applied contravariantly on
283-- the input of a fold.
284--
285-- @
286--
287-- |---transform----Fold m a b--------|
288-- ---stream m a-->transform-->| |---f b c ...
289-- |---transform----Fold m a c--------|
290-- | |
291-- ...
292-- @
293--
295-- $arrays
296--
297-- Streamly arrays (See "Streamly.Memory.Array") complement streams to provide an
298-- efficient computing paradigm. Streams are suitable for immutable
299-- transformations of /potentially infinite/ data using /sequential access/ and
300-- pipelined transformations whereas arrays are suitable for in-place
301-- transformations of /necessarily finite/ data using /random access/. Streams
302-- are synonymous with /sequential pipelined processing/ whereas arrays are
303-- synonymous with /efficient buffering and random access/.
304--
305-- In general, a data processing pipeline reads data from some IO device, does
306-- some processing on it and finally writes the output to another IO device.
307-- Streams provide the overall framework of sequential processing pipeline in
308-- which arrays are used as buffering elements in the middle. In addition to
309-- buffering in the middle, arrays can also be used at the boundaries of the
310-- pipeline to efficiently interface with external storage systems like memory,
311-- files and network. If streams are the pipes in a water pipeline network
312-- then arrays are like the storage tanks in the middle. On the input side,
313-- think of arrays as buckets to fetch water to feed the pipeline and on the
314-- output side buckets to remove the processed water.
315--
316-- 'ByteString' data type from the 'bytestring' package and the 'Text' data
317-- type from the 'text' package are special cases of arrays. 'ByteString' is
318-- like @Array Word8@ and 'Text' is like @utf16@ encoded @Array Word8@.
319-- Streamly arrays can be transformed as efficiently as @bytestring@ or @text@
320-- by using stream operations on them.
322-- Streams and arrays are equally important in computing. They are computing
323-- duals of each other.
325-- $streamtypes
326-- The basic stream type is 'Serial', it represents a sequence of IO actions,
327-- and is a 'Monad'. The type 'SerialT' is a monad transformer that can
328-- represent a sequence of actions in an arbitrary monad. The type 'Serial' is
329-- in fact a synonym for @SerialT IO@. There are a few more types similar to
330-- 'SerialT', all of them represent a stream and differ only in the
331-- 'Semigroup', 'Applicative' and 'Monad' compositions of the stream. 'Serial'
332-- and 'WSerial' types compose serially whereas 'Async' and 'WAsync'
333-- types compose concurrently. All these types can be freely inter-converted
334-- using type combinators without any cost. You can freely switch to any type
335-- of composition at any point in the program. When no type annotation or
336-- explicit stream type combinators are used, the default stream type is
337-- inferred as 'Serial'.
338--
339-- This module exports stream types, instances and combinators for:
340--
341-- * converting between different stream types
342-- * appending and concurrently merging streams
343-- * Concurrency control
344-- * Concurrent function application
345-- * Stream rate control
346--
347-- This module is designed to be imported unqualified:
348--
349-- @
350-- import Streamly
351-- @
352--
353-- See the "Streamly.Prelude" module for APIs for construction,
354-- generation, elimination and transformation of streams.
356------------------------------------------------------------------------------
357-- Eliminating a stream
358------------------------------------------------------------------------------
360-- | Same as 'runStream'
361--
362-- @since 0.1.0
363{-# DEPRECATED runStreaming "Please use runStream instead." #-}
364runStreaming :: (Monad m, IsStream t) => t m a -> m ()
365runStreaming = P.drain . K.adapt
367-- | Same as @runStream@.
368--
369-- @since 0.1.0
370{-# DEPRECATED runStreamT "Please use runStream instead." #-}
371runStreamT :: Monad m => SerialT m a -> m ()
372runStreamT = P.drain
374-- | Same as "Streamly.Prelude.runStream".
375--
376{-# DEPRECATED runStream "Please use Streamly.Prelude.drain instead." #-}
377runStream :: Monad m => SerialT m a -> m ()
378runStream = P.drain
380-- | Same as @runStream . wSerially@.
381--
382-- @since 0.1.0
383{-# DEPRECATED runInterleavedT "Please use 'runStream . interleaving' instead." #-}
384runInterleavedT :: Monad m => WSerialT m a -> m ()
385runInterleavedT = P.drain . K.adapt
387-- | Same as @runStream . parallely@.
388--
389-- @since 0.1.0
390{-# DEPRECATED runParallelT "Please use 'runStream . parallely' instead." #-}
391runParallelT :: Monad m => ParallelT m a -> m ()
392runParallelT = P.drain . K.adapt
394-- | Same as @runStream . asyncly@.
395--
396-- @since 0.1.0
397{-# DEPRECATED runAsyncT "Please use 'runStream . asyncly' instead." #-}
398runAsyncT :: Monad m => AsyncT m a -> m ()
399runAsyncT = P.drain . K.adapt
401-- | Same as @runStream . zipping@.
402--
403-- @since 0.1.0
404{-# DEPRECATED runZipStream "Please use 'runStream . zipSerially instead." #-}
405runZipStream :: Monad m => ZipSerialM m a -> m ()
406runZipStream = P.drain . K.adapt
408-- | Same as @runStream . zippingAsync@.
409--
410-- @since 0.1.0
411{-# DEPRECATED runZipAsync "Please use 'runStream . zipAsyncly instead." #-}
412runZipAsync :: Monad m => ZipAsyncM m a -> m ()
413runZipAsync = P.drain . K.adapt
415-- | Same as "Streamly.Prelude.foldWith".
416--
417{-# DEPRECATED foldWith "Please use Streamly.Prelude.foldWith instead." #-}
418{-# INLINABLE foldWith #-}
419foldWith :: (IsStream t, Foldable f)
420 => (t m a -> t m a -> t m a) -> f (t m a) -> t m a
421foldWith = P.foldWith
423-- | Same as "Streamly.Prelude.foldMapWith".
424--
425{-# DEPRECATED foldMapWith "Please use Streamly.Prelude.foldMapWith instead." #-}
426{-# INLINABLE foldMapWith #-}
427foldMapWith :: (IsStream t, Foldable f)
428 => (t m b -> t m b -> t m b) -> (a -> t m b) -> f a -> t m b
429foldMapWith = P.foldMapWith
431-- | Same as "Streamly.Prelude.forEachWith".
432--
433{-# DEPRECATED forEachWith "Please use Streamly.Prelude.forEachWith instead." #-}
434{-# INLINABLE forEachWith #-}
435forEachWith :: (IsStream t, Foldable f)
436 => (t m b -> t m b -> t m b) -> f a -> (a -> t m b) -> t m b
437forEachWith = P.forEachWith
439------------------------------------------------------------------------------
440-- Documentation
441------------------------------------------------------------------------------
443-- $serial
444--
445-- When a stream consumer demands an element from a serial stream constructed
446-- as @a \`consM` b \`consM` ... nil@, the action @a@ at the head of the stream
447-- sequence is executed and the result is supplied to the consumer. When the
448-- next element is demanded, the action @b@ is executed and its result is
449-- supplied. Thus, the effects are performed and results are consumed strictly
450-- in a serial order. Serial streams can be considered as /spatially ordered/
451-- streams as the order of execution and consumption is the same as the spatial
452-- order in which the actions are composed by the programmer.
453--
454-- Serial streams enforce the side effects as well as the results of the
455-- actions to be in the same order in which the actions are added to the
456-- stream. Therefore, the semigroup operation for serial streams is not
457-- commutative:
458--
459-- @
460-- a <> b is not the same as b <> a
461-- @
462--
463-- There are two serial stream types 'SerialT' and 'WSerialT'. The stream
464-- evaluation of both the variants works in the same way as described above,
465-- they differ only in the 'Semigroup' and 'Monad' implementaitons.
467-- $ahead
468--
469-- When a stream consumer demands an element from a speculative stream
470-- constructed as @a \`consM` b \`consM` ... nil@, the action @a@ at the head
471-- of the stream is executed and the output of the action is supplied to the
472-- consumer. However, in addition to the action at the head multiple actions
473-- following it may also be executed concurrently and the results buffered.
474-- When the next element is demanded it may be served from the buffer and we
475-- may execute the next action in the sequence to keep the buffer adequately
476-- filled. Thus, the actions are executed concurrently but results consumed in
477-- serial order just like serial streams. `consM` can be used to fold an
478-- infinite lazy container of effects, as the number of concurrent executions
479-- is limited.
480--
481-- Similar to 'consM', the monadic stream generation (e.g. replicateM) and
482-- transformation operations (e.g. mapM) on speculative streams can execute
483-- multiple effects concurrently in a speculative manner.
484--
485-- How many effects can be executed concurrently and how many results can be
486-- buffered are controlled by 'maxThreads' and 'maxBuffer' combinators
487-- respectively. The actual number of concurrent threads is adjusted according
488-- to the rate at which the consumer is consuming the stream. It may even
489-- execute actions serially in a single thread if that is enough to match the
490-- consumer's speed.
491--
492-- Speculative streams enforce ordering of the results of actions in the stream
493-- but the side effects are only partially ordered. Therefore, the semigroup
494-- operation for speculative streams is not commutative from the pure outputs
495-- perspective but commutative from side effects perspective.
497-- $async
498--
499-- When a stream consumer demands an element from an asynchronous stream,
500-- constructed as @a \`consM` b \`consM` ... nil@, the action @a@ along with
501-- multiple following at the head of the stream sequence are executed
502-- concurrently and the output of the one that completes first is supplied to
503-- the consumer. As more actions complete, their results are buffered in the
504-- order of completion. When the next element is demanded it may be served
505-- from the buffer and we may initiate execution of more actions in the
506-- sequence to keep the buffer adequately filled. Thus, the actions are
507-- executed concurrently and their results are consumed in the order of
508-- completion. `consM` can be used to fold an infinite lazy container of
509-- effects, as the number of concurrent executions is limited.
510--
511-- Similar to 'consM', the monadic stream generation (e.g. replicateM) and
512-- transformation operations (e.g. mapM) on asynchronous streams can execute
513-- multiple effects concurrently in an asynchronous manner.
514--
515-- How many effects can be executed concurrently and how many results can be
516-- buffered are controlled by 'maxThreads' and 'maxBuffer' combinators
517-- respectively. The actual number of concurrent threads is adjusted according
518-- to the rate at which the consumer is consuming the stream. It may even
519-- execute actions serially in a single thread if that is enough to match the
520-- consumer's speed.
521--
522-- Asynchronous streams do not enforce any spatial order on the side effects or
523-- on the results of the actions. However there is a partial ordering as the
524-- actions to be executed are picked from the head of stream. The results are
525-- presented to the consumer in the completion time order. Therefore, the
526-- semigroup operation for asynchronous streams is commutative i.e. the stream
527-- is considered unordered.
528--
529-- There are two asynchronous stream types 'AsyncT' and 'WAsyncT'. The stream
530-- evaluation of both the variants works in the same way as described above,
531-- they differ only in the 'Semigroup' and 'Monad' implementaitons.
533-- $zipping
534--
535-- 'ZipSerialM' and 'ZipAsyncM', provide 'Applicative' instances for zipping the
536-- corresponding elements of two streams together. Note that these types are
537-- not monads.
539-- $application
540--
541-- Stream processing functions can be composed in a chain using function
542-- application with or without the '$' operator, or with reverse function
543-- application operator '&'. Streamly provides concurrent versions of these
544-- operators applying stream processing functions such that each stage of the
545-- stream can run in parallel. The operators start with a @|@; we can read '|$'
546-- as "@parallel dollar@" to remember that @|@ comes before '$'.
547--
548-- Imports for the code snippets below:
549--
550-- @
551-- import Streamly
552-- import qualified Streamly.Prelude as S
553-- import Control.Concurrent
554-- @
556-- $sum
557-- The 'Semigroup' operation '<>' of each stream type combines two streams in a
558-- type specific manner. This section provides polymorphic versions of '<>'
559-- which can be used to combine two streams in a predetermined way irrespective
560-- of the type.
562-- XXX An alternative design choice would be to let a control parameter affect
563-- the nearest SVar only and then it gets cleared. The benefit of the current
564-- choice is that it is simply just like global configuration, just like state
565-- behaves, so should be easy to comprehend. But it has the downside of leaking
566-- to undesired actions, that is we can forget to reset it.
567--
568-- $concurrency
569--
570-- These combinators can be used at any point in a stream composition to set
571-- parameters to control the concurrency of the /argument stream/. A control
572-- parameter set at any point remains effective for any concurrent combinators
573-- used in the argument stream until it is reset by using the combinator again.
574-- These control parameters have no effect on non-concurrent combinators in the
575-- stream, or on non-concurrent streams.
576--
577-- /Pitfall:/ Remember that 'maxBuffer' in the following example applies to
578-- 'mapM' and any other combinators that may follow it, and it does not apply
579-- to the combinators before it:
580--
581-- @
582-- ...
583-- $ maxBuffer 10
584-- $ S.mapM ...
585-- ...
586-- @
587--
588-- If we use '&' instead of '$' the situation will reverse, in the following
589-- example, 'maxBuffer' does not apply to 'mapM', it applies to combinators
590-- that come before it, because those are the arguments to 'maxBuffer':
591--
592-- @
593-- ...
594-- & maxBuffer 10
595-- & S.mapM ...
596-- ...
597-- @
599-- $adapters
600--
601-- You may want to use different stream composition styles at different points
602-- in your program. Stream types can be freely converted or adapted from one
603-- type to another. The 'IsStream' type class facilitates type conversion of
604-- one stream type to another. It is not used directly, instead the type
605-- combinators provided below are used for conversions.
606--
607-- To adapt from one monomorphic type (e.g. 'AsyncT') to another monomorphic
608-- type (e.g. 'SerialT') use the 'adapt' combinator. To give a polymorphic code
609-- a specific interpretation or to adapt a specific type to a polymorphic type
610-- use the type specific combinators e.g. 'asyncly' or 'wSerially'. You
611-- cannot adapt polymorphic code to polymorphic code, as the compiler would not know
612-- which specific type you are converting from or to. If you see a an
613-- @ambiguous type variable@ error then most likely you are using 'adapt'
614-- unnecessarily on polymorphic code.