on master help sign in
Data.Stream: Update docs, expose doctest setup
commitfe3609bb1b8996dad4b236b9442e57bb9281eff1 authorHarendra Kumar <harendra@composewell.com> dateSun 12 Mar 2023, 13:37 (4 years ago) parent789e7d2
13 files changed+144−155
core/src/DocTestDataStream.hs+37 core/src/Streamly/Data/Stream.hs+83−75 core/src/Streamly/Internal/Data/Stream/StreamD/Container.hs+2−3 core/src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs+2−7 core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+2−3 core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+2−9 core/src/Streamly/Internal/Data/Stream/StreamD/Lift.hs+2−4 core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+2−13 core/src/Streamly/Internal/Data/Stream/StreamD/Top.hs+2−5 core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+2−19 core/src/Streamly/Internal/Data/Stream/StreamD/Transformer.hs+2−5 core/src/Streamly/Internal/Data/Stream/StreamD/Type.hs+5−12 core/streamly-core.cabal+1
core/src/DocTestDataStream.hsnew file+37
@@ -0,0 +1,37 @@
1+{- $setup
2+
3+>>> :m
4+>>> import Control.Concurrent (threadDelay)
5+>>> import Control.Monad (void)
6+>>> import Control.Monad.IO.Class (MonadIO (liftIO))
7+>>> import Control.Monad.Trans.Class (lift)
8+>>> import Control.Monad.Trans.Identity (runIdentityT)
9+>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
10+>>> import Data.Maybe (fromJust, isJust)
11+>>> import Data.Function (fix, (&))
12+>>> import Data.Functor.Identity (runIdentity)
13+>>> import Data.IORef
14+>>> import Data.Semigroup (cycle1)
15+>>> import GHC.Exts (Ptr (Ptr))
16+>>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
17+
18+>>> hSetBuffering stdout LineBuffering
19+>>> effect n = print n >> return n
20+
21+>>> import Streamly.Data.Stream (Stream)
22+>>> import qualified Streamly.Data.Array as Array
23+>>> import qualified Streamly.Data.Fold as Fold
24+>>> import qualified Streamly.Data.Stream as Stream
25+>>> import qualified Streamly.Data.Unfold as Unfold
26+>>> import qualified Streamly.Data.Parser as Parser
27+>>> import qualified Streamly.FileSystem.Dir as Dir
28+
29+For APIs that have not been released yet.
30+
31+>>> import qualified Streamly.Internal.Data.Fold as Fold
32+>>> import qualified Streamly.Internal.Data.Fold.Window as Window
33+>>> import qualified Streamly.Internal.Data.Parser as Parser
34+>>> import qualified Streamly.Internal.Data.Stream as Stream
35+>>> import qualified Streamly.Internal.Data.Unfold as Unfold
36+>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
37+-}
core/src/Streamly/Data/Stream.hs+83−75
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Data.Stream
34-- Copyright : (c) 2017 Composewell Technologies
@@ -7,87 +8,29 @@
78-- Stability : released
89-- Portability : GHC
910--
10−-- To run examples in this module:
11+-- Fast, composable stream producers with ability to terminate, supporting
12+-- stream fusion.
1113--
12−-- >>> import qualified Streamly.Data.Fold as Fold
13−-- >>> import qualified Streamly.Data.Stream as Stream
14+-- Please refer to "Streamly.Internal.Data.Stream" for more functions that have
15+-- not yet been released.
1416--
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>
17+-- Checkout the <https://github.com/composewell/streamly-examples>
8618-- repository for many more real world examples of stream programming.
87−--
19+
8820module Streamly.Data.Stream
8921 (
22+ -- * Setup
23+ -- | To execute the code examples provided in this module in ghci, please
24+ -- run the following commands first.
25+ --
26+ -- $setup
27+
28+ -- * Overview
29+ -- $overview
30+
31+ -- * The Stream Type
9032 Stream
33+
9134 -- * Construction
9235 -- | Functions ending in the general shape @b -> Stream m a@.
9336 --
@@ -522,3 +465,68 @@ import Prelude
522465 notElem, maximum, minimum, head, last, tail, length, null,
523466 reverse, iterate, init, and, or, lookup, foldr1, (!!),
524467 scanl, scanl1, repeat, replicate, concatMap, span)
468+
469+#include "DocTestDataStream.hs"
470+
471+-- $overview
472+--
473+-- Streamly is a framework for modular data flow based programming and
474+-- declarative concurrency. Powerful stream fusion framework in streamly
475+-- allows high performance combinatorial programming even when using byte level
476+-- streams. Streamly API is similar to Haskell lists.
477+--
478+-- == Console Echo Example
479+--
480+-- In the following example, 'repeatM' generates an infinite stream of 'String'
481+-- by repeatedly performing the 'getLine' IO action. 'mapM' then applies
482+-- 'putStrLn' on each element in the stream converting it to stream of '()'.
483+-- Finally, 'drain' folds the stream to IO discarding the () values, thus
484+-- producing only effects.
485+--
486+-- >>> import Data.Function ((&))
487+--
488+-- >>> :{
489+-- echo =
490+-- Stream.repeatM getLine -- Stream IO String
491+-- & Stream.mapM putStrLn -- Stream IO ()
492+-- & Stream.fold Fold.drain -- IO ()
493+-- :}
494+--
495+-- This is a console echo program. It is an example of a declarative loop
496+-- written using streaming combinators. Compare it with an imperative @while@
497+-- loop.
498+--
499+-- Hopefully, this gives you an idea how we can program declaratively by
500+-- representing loops using streams. In this module, you can find all
501+-- "Data.List" like functions and many more powerful combinators to perform
502+-- common programming tasks.
503+--
504+-- == Performance Notes
505+--
506+-- The 'Stream' type represents a stream by composing state as data which
507+-- enables stream fusion. Stream fusion generates a tight loop without any
508+-- constructor allocations between the stages, providing C like performance for
509+-- the loop. Stream fusion works when multiple functions are combined in a
510+-- pipeline statically. Therefore, the operations in this module must be
511+-- inlined and must not be used recursively to allow for stream fusion.
512+--
513+-- Using the 'Stream' type binary stream construction operations like 'cons',
514+-- 'append' etc. degrade quadratically (O(n^2)) when combined many times. If
515+-- you need to combine these operations, say more than 50 times in a single
516+-- loop, then you should use the continuation style stream type 'StreamK'
517+-- instead. Also, if you need to use these operations in a recursive loop you
518+-- should use 'StreamK' instead.
519+--
520+-- The 'StreamK' type represents a stream by composing function calls,
521+-- therefore, a function call overhead is incurred at each composition. It is
522+-- quite fast in general but may be a few times slower than a fused stream.
523+-- However, it allows for scalable dynamic composition and control flow
524+-- manipulation. Using the 'StreamK' type binary operations like 'cons' and
525+-- 'append' provide linear (O(n)) performance.
526+--
527+-- 'Stream' and 'StreamK' types can be interconverted.
528+--
529+-- == Useful Idioms
530+--
531+-- >>> fromListM = Stream.sequence . Stream.fromList
532+-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
core/src/Streamly/Internal/Data/Stream/StreamD/Container.hs+2−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Container
34-- Copyright : (c) 2019 Composewell Technologies
@@ -44,9 +45,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Generate as Stream
4445import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream
4546import qualified Streamly.Internal.Data.Stream.StreamD.Transformer as Stream
4647
47−-- $setup
48−-- >>> :m
49−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
48+#include "DocTestDataStream.hs"
5049
5150-- | The memory used is proportional to the number of unique elements in the
5251-- stream. If we want to limit the memory we can just use "take" to limit the
core/src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs+2−7
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Eliminate
34-- Copyright : (c) 2018 Composewell Technologies
@@ -107,13 +108,7 @@ import Prelude hiding
107108 , maximum, minimum, notElem, null, splitAt, tail, (!!))
108109import Streamly.Internal.Data.Stream.StreamD.Type
109110
110−-- $setup
111−-- >>> :m
112−-- >>> import Streamly.Internal.Data.Stream (Stream)
113−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
114−-- >>> import qualified Streamly.Internal.Data.Parser as Parser
115−-- >>> import qualified Streamly.Internal.Data.Fold as Fold
116−-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
111+#include "DocTestDataStream.hs"
117112
118113------------------------------------------------------------------------------
119114-- Elimination by Folds
core/src/Streamly/Internal/Data/Stream/StreamD/Exception.hs+2−3
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Exception
34-- Copyright : (c) 2020 Composewell Technologies and Contributors
@@ -37,9 +38,7 @@ import qualified Control.Monad.Catch as MC
3738
3839import Streamly.Internal.Data.Stream.StreamD.Type
3940
40−-- $setup
41−-- >>> :m
42−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
41+#include "DocTestDataStream.hs"
4342
4443data GbracketState s1 s2 v
4544 = GBracketInit
core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+2−9
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Generate
34-- Copyright : (c) 2020 Composewell Technologies and Contributors
@@ -136,15 +137,7 @@ import Numeric.Natural
136137import Prelude hiding (iterate, repeat, replicate, take, takeWhile)
137138import Streamly.Internal.Data.Stream.StreamD.Type
138139
139−-- $setup
140−-- >>> :m
141−-- >>> import Control.Concurrent (threadDelay)
142−-- >>> import Data.Function (fix, (&))
143−-- >>> import Data.Semigroup (cycle1)
144−-- >>> import qualified Streamly.Data.Fold as Fold
145−-- >>> import qualified Streamly.Data.Unfold as Unfold
146−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
147−-- >>> import GHC.Exts (Ptr (Ptr))
140+#include "DocTestDataStream.hs"
148141
149142------------------------------------------------------------------------------
150143-- Primitives
core/src/Streamly/Internal/Data/Stream/StreamD/Lift.hs+2−4
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Lift
34-- Copyright : (c) 2018 Composewell Technologies
@@ -28,10 +29,7 @@ import Streamly.Internal.Data.SVar.Type (adaptState)
2829
2930import Streamly.Internal.Data.Stream.StreamD.Type
3031
31−-- $setup
32−-- >>> :m
33−-- >>> import Data.Functor.Identity (runIdentity)
34−-- >>> import Streamly.Internal.Data.Stream as Stream
32+#include "DocTestDataStream.hs"
3533
3634-------------------------------------------------------------------------------
3735-- Generalize Inner Monad
core/src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs+2−13
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Nesting
34-- Copyright : (c) 2018 Composewell Technologies
@@ -201,19 +202,7 @@ import Streamly.Internal.Data.Stream.StreamD.Type
201202
202203import Prelude hiding (concatMap, mapM, zipWith)
203204
204−-- $setup
205−-- >>> :m
206−-- >>> import Data.Either (either)
207−-- >>> import Data.IORef
208−-- >>> import Streamly.Internal.Data.Stream (Stream)
209−-- >>> import Prelude hiding (zipWith, concatMap, concat)
210−-- >>> import qualified Streamly.Data.Array as Array
211−-- >>> import qualified Streamly.Internal.Data.Fold as Fold
212−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
213−-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
214−-- >>> import qualified Streamly.Internal.Data.Parser as Parser
215−-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
216−--
205+#include "DocTestDataStream.hs"
217206
218207------------------------------------------------------------------------------
219208-- Appending
core/src/Streamly/Internal/Data/Stream/StreamD/Top.hs+2−5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Top
34-- Copyright : (c) 2020 Composewell Technologies
@@ -58,11 +59,7 @@ import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream
5859
5960import Prelude hiding (filter, zipWith, concatMap, concat)
6061
61−-- $setup
62−-- >>> :m
63−-- >>> import Prelude hiding (filter, zipWith, concatMap, concat)
64−-- >>> import qualified Streamly.Internal.Data.Fold as Fold
65−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
62+#include "DocTestDataStream.hs"
6663
6764------------------------------------------------------------------------------
6865-- Sampling
core/src/Streamly/Internal/Data/Stream/StreamD/Transform.hs+2−19
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Transform
34-- Copyright : (c) 2018 Composewell Technologies
@@ -182,25 +183,7 @@ import Streamly.Internal.Data.Stream.StreamD.Generate
182183 (absTimesWith, relTimesWith)
183184import Streamly.Internal.Data.Stream.StreamD.Type
184185
185−--
186−-- $setup
187−-- >>> :m
188−-- >>> import Control.Concurrent (threadDelay)
189−-- >>> import Control.Monad (void)
190−-- >>> import Control.Monad.IO.Class (MonadIO (liftIO))
191−-- >>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
192−-- >>> import Data.Function ((&))
193−-- >>> import Data.Maybe (fromJust, isJust)
194−-- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)
195−-- >>> import Streamly.Internal.Data.Stream (Stream)
196−-- >>> import qualified Streamly.Data.Fold as Fold
197−-- >>> import qualified Streamly.Data.Unfold as Unfold
198−-- >>> import qualified Streamly.Internal.Data.Fold as Fold (filtering)
199−-- >>> import qualified Streamly.Internal.Data.Fold.Window as Window
200−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
201−-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
202−--
203−-- >>> hSetBuffering stdout LineBuffering
186+#include "DocTestDataStream.hs"
204187
205188------------------------------------------------------------------------------
206189-- Piping
core/src/Streamly/Internal/Data/Stream/StreamD/Transformer.hs+2−5
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12-- |
23-- Module : Streamly.Internal.Data.Stream.StreamD.Transformer
34-- Copyright : (c) 2018 Composewell Technologies
@@ -36,11 +37,7 @@ import qualified Control.Monad.Trans.State.Strict as State
3637
3738import Streamly.Internal.Data.Stream.StreamD.Type
3839
39−-- $setup
40−-- >>> :m
41−-- >>> import Control.Monad.Trans.Class (lift)
42−-- >>> import Control.Monad.Trans.Identity (runIdentityT)
43−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
40+#include "DocTestDataStream.hs"
4441
4542-- | Lazy left fold to a transformer monad.
4643--
core/src/Streamly/Internal/Data/Stream/StreamD/Type.hs+5−12
@@ -1,3 +1,4 @@
1+{-# LANGUAGE CPP #-}
12{-# LANGUAGE UndecidableInstances #-}
23
34-- |
@@ -167,15 +168,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
167168import qualified Streamly.Internal.Data.Unfold.Type as Unfold
168169#endif
169170
170−-- $setup
171−-- >>> import Streamly.Internal.Data.Stream (CrossStream(..))
172−-- >>> import qualified Streamly.Data.Fold as Fold
173−-- >>> import qualified Streamly.Data.Parser as Parser
174−-- >>> import qualified Streamly.Data.Stream as Stream
175−-- >>> import qualified Streamly.Data.Unfold as Unfold
176−-- >>> import qualified Streamly.Internal.Data.Stream as Stream
177−-- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir
178−-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
171+#include "DocTestDataStream.hs"
179172
180173------------------------------------------------------------------------------
181174-- The direct style stream type
@@ -308,7 +301,7 @@ unfold (Unfold ustep inject) seed = Stream step UnfoldNothing
308301--
309302-- >>> fromPure a = a `Stream.cons` Stream.nil
310303-- >>> fromPure = pure
311−-- >>> fromPure = fromEffect . pure
304+-- >>> fromPure = Stream.fromEffect . pure
312305--
313306{-# INLINE_NORMAL fromPure #-}
314307fromPure :: Applicative m => a -> Stream m a
@@ -320,7 +313,7 @@ fromPure x = Stream (\_ s -> pure $ step undefined s) True
320313
321314-- | Create a singleton stream from a monadic action.
322315--
323−-- >>> fromEffect m = m `consM` Stream.nil
316+-- >>> fromEffect m = m `Stream.consM` Stream.nil
324317-- >>> fromEffect = Stream.sequence . Stream.fromPure
325318--
326319-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")
@@ -1383,7 +1376,7 @@ concat = concatMap id
13831376-- monad with the stream monad.
13841377--
13851378-- >>> concatEffect = Stream.concat . Stream.fromEffect
1386−-- >>> concatEffect eff = Stream.concatMapM (\() -> eff) (fromPure ())
1379+-- >>> concatEffect eff = Stream.concatMapM (\() -> eff) (Stream.fromPure ())
13871380--
13881381-- See also: 'concat', 'sequence'
13891382--
core/streamly-core.cabal+1
@@ -43,6 +43,7 @@ extra-source-files:
4343 src/DocTestDataMutArray.hs
4444 src/DocTestDataMutArrayGeneric.hs
4545 src/DocTestDataParser.hs
46+ src/DocTestDataStream.hs
4647
4748 -- This is duplicated
4849 src/Streamly/Internal/Data/Array/ArrayMacros.h