on fa75413 help sign in
streamly/src/Streamly.hs
629 lines 24 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 "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 , (IP.|$)
132 , (IP.|&)
133 , (IP.|$.)
134 , (IP.|&.)
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 -- ** Folding Containers of Streams
184 -- | These are variants of standard 'Foldable' fold functions that use a
185 -- polymorphic stream sum operation (e.g. 'async' or 'wSerial') to fold a
186 -- finite container of streams. Note that these are just special cases of
187 -- the more general 'concatMapWith' operation.
188 --
189 , IP.foldWith
190 , IP.foldMapWith
191 , IP.forEachWith
193 -- * Re-exports
194 , Semigroup (..)
196 -- * Deprecated
197 , Streaming
198 , runStream
199 , runStreaming
200 , runStreamT
201 , runInterleavedT
202 , runAsyncT
203 , runParallelT
204 , runZipStream
205 , runZipAsync
206 , StreamT
207 , InterleavedT
208 , ZipStream
209 , interleaving
210 , zipping
211 , zippingAsync
212 , (<=>)
213 , (<|)
215 {-
216 -- * Deprecated/Moved
217 -- | These APIs have been moved to other modules
218 , foldWith
219 , foldMapWith
220 , forEachWith
221 -}
222 )
223where
225import Data.Semigroup (Semigroup(..))
226import Streamly.Internal.Data.SVar (MonadAsync, Rate(..))
227import Streamly.Internal.Data.Stream.Ahead
228import Streamly.Internal.Data.Stream.Async
229import Streamly.Internal.Data.Stream.Combinators
230import Streamly.Internal.Data.Stream.Parallel
231import Streamly.Internal.Data.Stream.Serial
232import Streamly.Internal.Data.Stream.StreamK hiding (serial)
233import Streamly.Internal.Data.Stream.Zip
235import qualified Streamly.Prelude as P
236import qualified Streamly.Internal.Prelude as IP
237import qualified Streamly.Internal.Data.Stream.StreamK as K
239-- XXX provide good succinct examples of pipelining, merging, splitting etc.
240-- below.
241--
242-- $streams
243--
244-- A program is expressed as a network of streams and folds. A stream is a
245-- source or generator of data elements and a fold is a consumer of data elements that
246-- reduces multiple input elements to a single value.
247--
248-- In the following example, a 'Word8' stream is generated by using
249-- 'Streamly.FileSystem.Handle.read' on a file handle, then the
250-- 'Streamly.Prelude.splitBySuffix' transformation splits the stream on
251-- newlines (ascii value 10); it uses the 'Streamly.Data.Fold.drain' fold to reduce
252-- the resulting lines to unit values (@()@), 'Streamly.Prelude.length' fold
253-- then counts the unit elements in the resulting stream which gives us the
254-- number of lines in the file:
255--
256-- > S.length $ S.splitOnSuffix FL.drain 10 $ FH.read fh
257--
258-- The following example folds the lines to arrays of 'Word8' using the
259-- 'Streamly.Memory.Array.writeF' fold and then wraps the lines in square
260-- brackets before writing them to standard output using
261-- 'Streamly.FileSystem.Handle.write':
262--
263-- > wrapLine ln = S.fromList "[" <> A.read ln <> S.fromList "]\n"
264-- > readLines = S.splitOnSuffix A.writeF 10
265-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1
266--
267-- One stream can be appended after another:
268--
269-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1 <> readLines fh2
270--
271-- The following example reads two files concurrently, merges the lines from
272-- the two streams and writes the resulting stream to another file:
273--
274-- > FH.write stdout $ S.concatMap wrapLine $ readLines fh1 `parallel` readLines fh2
275--
276-- There are many ways to generate, merge, zip, transform and fold data
277-- streams. Many transformations can be chained in a stream pipeline. See
278-- "Streamly.Prelude" module for combinators to manipulate streams.
280-- $folds
281--
282-- The way stream types in this module like 'SerialT' represent data sources,
283-- the same way the 'Fold' type from "Streamly.Data.Fold" represents data sinks or
284-- reducers of streams. Reducers can be combined to consume a stream source in
285-- many ways. The simplest is to reduce a stream source using a fold e.g.:
286--
287-- > S.runFold FL.length $ S.enumerateTo 100
288--
289-- Folds are consumers of streams and can be used to split a stream into
290-- multiple independent flows. Grouping transforms a stream by applying a fold
291-- on segments of a stream, distributing applies multiple folds in parallel on
292-- the same stream and combines them, partitioning sends different elements of
293-- a stream to different folds, unzipping divides the elements of a stream into
294-- parts and sends them through different folds. Parsers are nothing but a
295-- particular type of folds. Transformations can be applied contravariantly on
296-- the input of a fold.
297--
298-- @
299--
300-- |---transform----Fold m a b--------|
301-- ---stream m a-->transform-->| |---f b c ...
302-- |---transform----Fold m a c--------|
303-- | |
304-- ...
305-- @
306--
308-- $arrays
309--
310-- Streamly arrays (See "Streamly.Memory.Array") complement streams to provide an
311-- efficient computing paradigm. Streams are suitable for immutable
312-- transformations of /potentially infinite/ data using /sequential access/ and
313-- pipelined transformations whereas arrays are suitable for in-place
314-- transformations of /necessarily finite/ data using /random access/. Streams
315-- are synonymous with /sequential pipelined processing/ whereas arrays are
316-- synonymous with /efficient buffering and random access/.
317--
318-- In general, a data processing pipeline reads data from some IO device, does
319-- some processing on it and finally writes the output to another IO device.
320-- Streams provide the overall framework of sequential processing pipeline in
321-- which arrays are used as buffering elements in the middle. In addition to
322-- buffering in the middle, arrays can also be used at the boundaries of the
323-- pipeline to efficiently interface with external storage systems like memory,
324-- files and network. If streams are the pipes in a water pipeline network
325-- then arrays are like the storage tanks in the middle. On the input side,
326-- think of arrays as buckets to fetch water to feed the pipeline and on the
327-- output side buckets to remove the processed water.
328--
329-- 'ByteString' data type from the 'bytestring' package and the 'Text' data
330-- type from the 'text' package are special cases of arrays. 'ByteString' is
331-- like @Array Word8@ and 'Text' is like @utf16@ encoded @Array Word8@.
332-- Streamly arrays can be transformed as efficiently as @bytestring@ or @text@
333-- by using stream operations on them.
335-- Streams and arrays are equally important in computing. They are computing
336-- duals of each other.
338-- $streamtypes
339-- The basic stream type is 'Serial', it represents a sequence of IO actions,
340-- and is a 'Monad'. The type 'SerialT' is a monad transformer that can
341-- represent a sequence of actions in an arbitrary monad. The type 'Serial' is
342-- in fact a synonym for @SerialT IO@. There are a few more types similar to
343-- 'SerialT', all of them represent a stream and differ only in the
344-- 'Semigroup', 'Applicative' and 'Monad' compositions of the stream. 'Serial'
345-- and 'WSerial' types compose serially whereas 'Async' and 'WAsync'
346-- types compose concurrently. All these types can be freely inter-converted
347-- using type combinators without any cost. You can freely switch to any type
348-- of composition at any point in the program. When no type annotation or
349-- explicit stream type combinators are used, the default stream type is
350-- inferred as 'Serial'.
351--
352-- This module exports stream types, instances and combinators for:
353--
354-- * converting between different stream types
355-- * appending and concurrently merging streams
356-- * Concurrency control
357-- * Concurrent function application
358-- * Stream rate control
359--
360-- This module is designed to be imported unqualified:
361--
362-- @
363-- import Streamly
364-- @
365--
366-- See the "Streamly.Prelude" module for APIs for construction,
367-- generation, elimination and transformation of streams.
369------------------------------------------------------------------------------
370-- Eliminating a stream
371------------------------------------------------------------------------------
373-- | Same as 'runStream'
374--
375-- @since 0.1.0
376{-# DEPRECATED runStreaming "Please use runStream instead." #-}
377runStreaming :: (Monad m, IsStream t) => t m a -> m ()
378runStreaming = P.drain . K.adapt
380-- | Same as @runStream@.
381--
382-- @since 0.1.0
383{-# DEPRECATED runStreamT "Please use runStream instead." #-}
384runStreamT :: Monad m => SerialT m a -> m ()
385runStreamT = P.drain
387-- | Same as "Streamly.Prelude.runStream".
388--
389{-# DEPRECATED runStream "Please use Streamly.Prelude.drain instead." #-}
390runStream :: Monad m => SerialT m a -> m ()
391runStream = P.drain
393-- | Same as @runStream . wSerially@.
394--
395-- @since 0.1.0
396{-# DEPRECATED runInterleavedT "Please use 'runStream . interleaving' instead." #-}
397runInterleavedT :: Monad m => WSerialT m a -> m ()
398runInterleavedT = P.drain . K.adapt
400-- | Same as @runStream . parallely@.
401--
402-- @since 0.1.0
403{-# DEPRECATED runParallelT "Please use 'runStream . parallely' instead." #-}
404runParallelT :: Monad m => ParallelT m a -> m ()
405runParallelT = P.drain . K.adapt
407-- | Same as @runStream . asyncly@.
408--
409-- @since 0.1.0
410{-# DEPRECATED runAsyncT "Please use 'runStream . asyncly' instead." #-}
411runAsyncT :: Monad m => AsyncT m a -> m ()
412runAsyncT = P.drain . K.adapt
414-- | Same as @runStream . zipping@.
415--
416-- @since 0.1.0
417{-# DEPRECATED runZipStream "Please use 'runStream . zipSerially instead." #-}
418runZipStream :: Monad m => ZipSerialM m a -> m ()
419runZipStream = P.drain . K.adapt
421-- | Same as @runStream . zippingAsync@.
422--
423-- @since 0.1.0
424{-# DEPRECATED runZipAsync "Please use 'runStream . zipAsyncly instead." #-}
425runZipAsync :: Monad m => ZipAsyncM m a -> m ()
426runZipAsync = P.drain . K.adapt
428{-
429-- | Same as "Streamly.Prelude.foldWith".
430--
431{-# DEPRECATED foldWith "Please use Streamly.Prelude.foldWith instead." #-}
432{-# INLINABLE foldWith #-}
433foldWith :: (IsStream t, Foldable f)
434 => (t m a -> t m a -> t m a) -> f (t m a) -> t m a
435foldWith = P.foldWith
437-- | Same as "Streamly.Prelude.foldMapWith".
438--
439{-# DEPRECATED foldMapWith "Please use Streamly.Prelude.foldMapWith instead." #-}
440{-# INLINABLE foldMapWith #-}
441foldMapWith :: (IsStream t, Foldable f)
442 => (t m b -> t m b -> t m b) -> (a -> t m b) -> f a -> t m b
443foldMapWith = P.foldMapWith
445-- | Same as "Streamly.Prelude.forEachWith".
446--
447{-# DEPRECATED forEachWith "Please use Streamly.Prelude.forEachWith instead." #-}
448{-# INLINABLE forEachWith #-}
449forEachWith :: (IsStream t, Foldable f)
450 => (t m b -> t m b -> t m b) -> f a -> (a -> t m b) -> t m b
451forEachWith = P.forEachWith
452-}
454------------------------------------------------------------------------------
455-- Documentation
456------------------------------------------------------------------------------
458-- $serial
459--
460-- When a stream consumer demands an element from a serial stream constructed
461-- as @a \`consM` b \`consM` ... nil@, the action @a@ at the head of the stream
462-- sequence is executed and the result is supplied to the consumer. When the
463-- next element is demanded, the action @b@ is executed and its result is
464-- supplied. Thus, the effects are performed and results are consumed strictly
465-- in a serial order. Serial streams can be considered as /spatially ordered/
466-- streams as the order of execution and consumption is the same as the spatial
467-- order in which the actions are composed by the programmer.
468--
469-- Serial streams enforce the side effects as well as the results of the
470-- actions to be in the same order in which the actions are added to the
471-- stream. Therefore, the semigroup operation for serial streams is not
472-- commutative:
473--
474-- @
475-- a <> b is not the same as b <> a
476-- @
477--
478-- There are two serial stream types 'SerialT' and 'WSerialT'. The stream
479-- evaluation of both the variants works in the same way as described above,
480-- they differ only in the 'Semigroup' and 'Monad' implementaitons.
482-- $ahead
483--
484-- When a stream consumer demands an element from a speculative stream
485-- constructed as @a \`consM` b \`consM` ... nil@, the action @a@ at the head
486-- of the stream is executed and the output of the action is supplied to the
487-- consumer. However, in addition to the action at the head multiple actions
488-- following it may also be executed concurrently and the results buffered.
489-- When the next element is demanded it may be served from the buffer and we
490-- may execute the next action in the sequence to keep the buffer adequately
491-- filled. Thus, the actions are executed concurrently but results consumed in
492-- serial order just like serial streams. `consM` can be used to fold an
493-- infinite lazy container of effects, as the number of concurrent executions
494-- is limited.
495--
496-- Similar to 'consM', the monadic stream generation (e.g. replicateM) and
497-- transformation operations (e.g. mapM) on speculative streams can execute
498-- multiple effects concurrently in a speculative manner.
499--
500-- How many effects can be executed concurrently and how many results can be
501-- buffered are controlled by 'maxThreads' and 'maxBuffer' combinators
502-- respectively. The actual number of concurrent threads is adjusted according
503-- to the rate at which the consumer is consuming the stream. It may even
504-- execute actions serially in a single thread if that is enough to match the
505-- consumer's speed.
506--
507-- Speculative streams enforce ordering of the results of actions in the stream
508-- but the side effects are only partially ordered. Therefore, the semigroup
509-- operation for speculative streams is not commutative from the pure outputs
510-- perspective but commutative from side effects perspective.
512-- $async
513--
514-- When a stream consumer demands an element from an asynchronous stream,
515-- constructed as @a \`consM` b \`consM` ... nil@, the action @a@ along with
516-- multiple following at the head of the stream sequence are executed
517-- concurrently and the output of the one that completes first is supplied to
518-- the consumer. As more actions complete, their results are buffered in the
519-- order of completion. When the next element is demanded it may be served
520-- from the buffer and we may initiate execution of more actions in the
521-- sequence to keep the buffer adequately filled. Thus, the actions are
522-- executed concurrently and their results are consumed in the order of
523-- completion. `consM` can be used to fold an infinite lazy container of
524-- effects, as the number of concurrent executions is limited.
525--
526-- Similar to 'consM', the monadic stream generation (e.g. replicateM) and
527-- transformation operations (e.g. mapM) on asynchronous streams can execute
528-- multiple effects concurrently in an asynchronous manner.
529--
530-- How many effects can be executed concurrently and how many results can be
531-- buffered are controlled by 'maxThreads' and 'maxBuffer' combinators
532-- respectively. The actual number of concurrent threads is adjusted according
533-- to the rate at which the consumer is consuming the stream. It may even
534-- execute actions serially in a single thread if that is enough to match the
535-- consumer's speed.
536--
537-- Asynchronous streams do not enforce any spatial order on the side effects or
538-- on the results of the actions. However there is a partial ordering as the
539-- actions to be executed are picked from the head of stream. The results are
540-- presented to the consumer in the completion time order. Therefore, the
541-- semigroup operation for asynchronous streams is commutative i.e. the stream
542-- is considered unordered.
543--
544-- There are two asynchronous stream types 'AsyncT' and 'WAsyncT'. The stream
545-- evaluation of both the variants works in the same way as described above,
546-- they differ only in the 'Semigroup' and 'Monad' implementaitons.
548-- $zipping
549--
550-- 'ZipSerialM' and 'ZipAsyncM', provide 'Applicative' instances for zipping the
551-- corresponding elements of two streams together. Note that these types are
552-- not monads.
554-- $application
555--
556-- Stream processing functions can be composed in a chain using function
557-- application with or without the '$' operator, or with reverse function
558-- application operator '&'. Streamly provides concurrent versions of these
559-- operators applying stream processing functions such that each stage of the
560-- stream can run in parallel. The operators start with a @|@; we can read '|$'
561-- as "@parallel dollar@" to remember that @|@ comes before '$'.
562--
563-- Imports for the code snippets below:
564--
565-- @
566-- import Streamly
567-- import qualified Streamly.Prelude as S
568-- import Control.Concurrent
569-- @
571-- $sum
572-- The 'Semigroup' operation '<>' of each stream type combines two streams in a
573-- type specific manner. This section provides polymorphic versions of '<>'
574-- which can be used to combine two streams in a predetermined way irrespective
575-- of the type.
577-- XXX An alternative design choice would be to let a control parameter affect
578-- the nearest SVar only and then it gets cleared. The benefit of the current
579-- choice is that it is simply just like global configuration, just like state
580-- behaves, so should be easy to comprehend. But it has the downside of leaking
581-- to undesired actions, that is we can forget to reset it.
582--
583-- $concurrency
584--
585-- These combinators can be used at any point in a stream composition to set
586-- parameters to control the concurrency of the /argument stream/. A control
587-- parameter set at any point remains effective for any concurrent combinators
588-- used in the argument stream until it is reset by using the combinator again.
589-- These control parameters have no effect on non-concurrent combinators in the
590-- stream, or on non-concurrent streams.
591--
592-- /Pitfall:/ Remember that 'maxBuffer' in the following example applies to
593-- 'mapM' and any other combinators that may follow it, and it does not apply
594-- to the combinators before it:
595--
596-- @
597-- ...
598-- $ maxBuffer 10
599-- $ S.mapM ...
600-- ...
601-- @
602--
603-- If we use '&' instead of '$' the situation will reverse, in the following
604-- example, 'maxBuffer' does not apply to 'mapM', it applies to combinators
605-- that come before it, because those are the arguments to 'maxBuffer':
606--
607-- @
608-- ...
609-- & maxBuffer 10
610-- & S.mapM ...
611-- ...
612-- @
614-- $adapters
615--
616-- You may want to use different stream composition styles at different points
617-- in your program. Stream types can be freely converted or adapted from one
618-- type to another. The 'IsStream' type class facilitates type conversion of
619-- one stream type to another. It is not used directly, instead the type
620-- combinators provided below are used for conversions.
621--
622-- To adapt from one monomorphic type (e.g. 'AsyncT') to another monomorphic
623-- type (e.g. 'SerialT') use the 'adapt' combinator. To give a polymorphic code
624-- a specific interpretation or to adapt a specific type to a polymorphic type
625-- use the type specific combinators e.g. 'asyncly' or 'wSerially'. You
626-- cannot adapt polymorphic code to polymorphic code, as the compiler would not know
627-- which specific type you are converting from or to. If you see a an
628-- @ambiguous type variable@ error then most likely you are using 'adapt'
629-- unnecessarily on polymorphic code.