on master help sign in
Remove usage of `cons` from Data.Stream docs
commitdd0e41f6e930cca11829e3da5b791118f58576b9 authorHarendra Kumar <harendra@composewell.com> dateFri 17 Mar 2023, 17:37 (4 years ago) parentc3ccb6c
6 files changed+134−67
core/src/DocTestDataStreamK.hs+1 core/src/Streamly/Data/Stream.hs+5−5 core/src/Streamly/Data/StreamK.hs+5 core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+47−62 core/src/Streamly/Internal/Data/Stream/StreamK.hs+40 core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs+36
core/src/DocTestDataStreamK.hs+1
@@ -1,6 +1,7 @@
11{- $setup
22
33>>> :m
4+>>> import Control.Concurrent (threadDelay)
45>>> import Data.Function (fix, (&))
56>>> import Data.Semigroup (cycle1)
67
core/src/Streamly/Data/Stream.hs+5−5
@@ -49,11 +49,11 @@ module Streamly.Data.Stream
4949 -- >>> fromFoldableM = Stream.sequence . fromFoldable
5050
5151 -- ** Primitives
52− -- | Primitives to construct a stream from pure values or monadic actions.
53− -- All other stream construction and generation combinators described later
54− -- can be expressed in terms of these primitives. However, the special
55− -- versions provided in this module can be much more efficient in most
56− -- cases. Users can create custom combinators using these primitives.
52+ -- | A fused 'Stream' is never constructed using 'cons', we always convert
53+ -- other type of containers like list into streams, or generate it using
54+ -- custom functions provided in this module. The 'cons' primitive has a
55+ -- rare use in fusing a small number of elements. On the other hand, it is
56+ -- common to construct 'StreamK' stream using the 'StreamK.cons' primitive.
5757 , nil
5858 , nilM
5959 , cons
core/src/Streamly/Data/StreamK.hs+5
@@ -50,6 +50,11 @@ module Streamly.Data.StreamK
5050
5151 -- * Construction
5252 -- ** Primitives
53+ -- | Primitives to construct a stream from pure values or monadic actions.
54+ -- All other stream construction and generation combinators described later
55+ -- can be expressed in terms of these primitives. However, the special
56+ -- versions provided in this module can be much more efficient in some
57+ -- cases. Users can create custom combinators using these primitives.
5358 , nil
5459 , nilM
5560 , cons
core/src/Streamly/Internal/Data/Stream/StreamD/Generate.hs+47−62
@@ -147,7 +147,7 @@ import Streamly.Internal.Data.Stream.StreamD.Type
147147
148148-- | A stream that terminates without producing any output or side effect.
149149--
150−-- >>> Stream.fold Fold.toList Stream.nil
150+-- >>> Stream.toList Stream.nil
151151-- []
152152--
153153{-# INLINE_NORMAL nil #-}
@@ -157,21 +157,22 @@ nil = Stream (\_ _ -> pure Stop) ()
157157-- XXX implement in terms of consM?
158158-- cons x = consM (return x)
159159
160−-- | Fuse a pure value at the head of an existing stream::
160+-- | WARNING! You almost always need StreamK.'Streamly.Data.StreamK.cons'
161+-- instead.
162+--
163+-- Fuse a pure value at the head of an existing stream::
161164--
162165-- >>> s = 1 `Stream.cons` Stream.fromList [2,3]
163−-- >>> Stream.fold Fold.toList s
166+-- >>> Stream.toList s
164167-- [1,2,3]
165168--
166−-- This function should not be used to dynamically construct a stream. If a
167−-- stream is constructed by successive use of this function it would take
168−-- O(n^2) time to consume the stream.
169−--
170−-- This function should only be used to statically fuse an element with a
171−-- stream. Do not use this recursively or where it cannot be inlined.
172−--
173−-- See "Streamly.Data.StreamK" for a 'cons' that can be used to
174−-- construct a stream recursively.
169+-- From an implementation perspective, StreamK.cons translates into a
170+-- functional call whereas fused cons translates into a conditional branch
171+-- (jump). However, the overhead of the function call in StreamK.cons only
172+-- occurs once, while the overhead of the conditional branch in fused cons is
173+-- incurred for each subsequent element in the stream. As a result,
174+-- StreamK.cons has a time complexity of O(n), while fused cons has a time
175+-- complexity of O(n^2), where @n@ represents the number of 'cons' used.
175176--
176177-- Definition:
177178--
@@ -205,7 +206,7 @@ cons x (Stream step state) = Stream step1 Nothing
205206-- if b > 2
206207-- then return Nothing
207208-- else return (Just (b, b + 1))
208−-- in Stream.fold Fold.toList $ Stream.unfoldrM f 0
209+-- in Stream.toList $ Stream.unfoldrM f 0
209210-- :}
210211-- [0,1,2]
211212--
@@ -224,15 +225,7 @@ unfoldrM next = Stream step
224225 Nothing -> Stop
225226#endif
226227
227−-- |
228−-- >>> :{
229−-- unfoldr step s =
230−-- case step s of
231−-- Nothing -> Stream.nil
232−-- Just (a, b) -> a `Stream.cons` unfoldr step b
233−-- :}
234−--
235−-- Build a stream by unfolding a /pure/ step function @step@ starting from a
228+-- | Build a stream by unfolding a /pure/ step function @step@ starting from a
236229-- seed @s@. The step function returns the next element in the stream and the
237230-- next seed value. When it is done it returns 'Nothing' and the stream ends.
238231-- For example,
@@ -242,7 +235,7 @@ unfoldrM next = Stream step
242235-- if b > 2
243236-- then Nothing
244237-- else Just (b, b + 1)
245−-- in Stream.fold Fold.toList $ Stream.unfoldr f 0
238+-- in Stream.toList $ Stream.unfoldr f 0
246239-- :}
247240-- [0,1,2]
248241--
@@ -256,8 +249,6 @@ unfoldr f = unfoldrM (return . f)
256249
257250-- |
258251-- >>> repeatM = Stream.sequence . Stream.repeat
259−-- >>> repeatM = fix . Stream.consM
260−-- >>> repeatM = cycle1 . Stream.fromEffect
261252--
262253-- Generate a stream by repeatedly executing a monadic action forever.
263254--
@@ -420,10 +411,10 @@ enumerateFromThenToIntegralDn from next to = Stream step EnumInit
420411-- first element is @from@, the second element is @then@ and the successive
421412-- elements are in increments of @then - from@ up to @to@.
422413--
423−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6
414+-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 2 6
424415-- [0,2,4,6]
425416--
426−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
417+-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
427418-- [0,-2,-4,-6]
428419--
429420{-# INLINE_NORMAL enumerateFromThenToIntegral #-}
@@ -444,10 +435,10 @@ enumerateFromThenToIntegral from next to
444435-- is @then@ and the successive elements are in increments of @then - from@.
445436-- The stream is bounded by the size of the 'Integral' type.
446437--
447−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
438+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
448439-- [0,2,4,6]
449440--
450−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
441+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
451442-- [0,-2,-4,-6]
452443--
453444{-# INLINE_NORMAL enumerateFromThenIntegral #-}
@@ -471,10 +462,10 @@ enumerateFromThenIntegral from next =
471462-- CAUTION: This function is not safe for finite integral types. It does not
472463-- check for overflow, underflow or bounds.
473464--
474−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
465+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
475466-- [0,2,4,6]
476467--
477−-- >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
468+-- >>> Stream.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
478469-- [0,-2,-4]
479470--
480471{-# INLINE_NORMAL enumerateFromStepIntegral #-}
@@ -495,7 +486,7 @@ enumerateFromStepIntegral from stride =
495486-- element is @from@ and successive elements are in increments of @1@ up to
496487-- @to@.
497488--
498−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4
489+-- >>> Stream.toList $ Stream.enumerateFromToIntegral 0 4
499490-- [0,1,2,3,4]
500491--
501492{-# INLINE enumerateFromToIntegral #-}
@@ -507,7 +498,7 @@ enumerateFromToIntegral from to =
507498-- stream whose first element is @from@ and the successive elements are in
508499-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
509500--
510−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
501+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
511502-- [0,1,2,3]
512503--
513504{-# INLINE enumerateFromIntegral #-}
@@ -534,7 +525,7 @@ enumerateFromIntegral from = enumerateFromToIntegral from maxBound
534525--
535526-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
536527--
537−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
528+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
538529-- [1.1,2.1,3.1,4.1]
539530--
540531{-# INLINE enumerateFromFractional #-}
@@ -550,10 +541,10 @@ enumerateFromFractional = enumerateFromNum
550541-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
551542-- example:
552543--
553−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
544+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
554545-- [1.1,2.1,3.1,4.1]
555546--
556−-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
547+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
557548-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
558549--
559550{-# INLINE enumerateFromThenFractional #-}
@@ -570,10 +561,10 @@ enumerateFromThenFractional = enumerateFromThenNum
570561-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
571562-- example:
572563--
573−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4
564+-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4
574565-- [1.1,2.1,3.1,4.1]
575566--
576−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6
567+-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4.6
577568-- [1.1,2.1,3.1,4.1,5.1]
578569--
579570-- Notice that the last element is equal to the specified @to@ value after
@@ -594,10 +585,10 @@ enumerateFromToFractional from to =
594585-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
595586-- example:
596587--
597−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
588+-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
598589-- [0.1,2.0,3.9,5.799999999999999]
599590--
600−-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
591+-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
601592-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
602593--
603594{-# INLINE_NORMAL enumerateFromThenToFractional #-}
@@ -672,13 +663,13 @@ class Enum a => Enumerable a where
672663 -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
673664 -- generating an infinite stream when the type is not 'Bounded'.
674665 --
675− -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
666+ -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
676667 -- [0,1,2,3]
677668 --
678669 -- For 'Fractional' types, enumeration is numerically stable. However, no
679670 -- overflow or underflow checks are performed.
680671 --
681− -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
672+ -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
682673 -- [1.1,2.1,3.1,4.1]
683674 --
684675 enumerateFrom :: (Monad m) => a -> Stream m a
@@ -687,16 +678,16 @@ class Enum a => Enumerable a where
687678 -- the type up to the value @to@. If @to@ is smaller than @from@ then an
688679 -- empty stream is returned.
689680 --
690− -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4
681+ -- >>> Stream.toList $ Stream.enumerateFromTo 0 4
691682 -- [0,1,2,3,4]
692683 --
693684 -- For 'Fractional' types, the last element is equal to the specified @to@
694685 -- value after rounding to the nearest integral value.
695686 --
696− -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4
687+ -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4
697688 -- [1.1,2.1,3.1,4.1]
698689 --
699− -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6
690+ -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4.6
700691 -- [1.1,2.1,3.1,4.1,5.1]
701692 --
702693 enumerateFromTo :: (Monad m) => a -> a -> Stream m a
@@ -708,10 +699,10 @@ class Enum a => Enumerable a where
708699 -- 'Bounded' types the stream ends when 'maxBound' is reached, for
709700 -- unbounded types it keeps enumerating infinitely.
710701 --
711− -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
702+ -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
712703 -- [0,2,4,6]
713704 --
714− -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
705+ -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
715706 -- [0,-2,-4,-6]
716707 --
717708 enumerateFromThen :: (Monad m) => a -> a -> Stream m a
@@ -722,10 +713,10 @@ class Enum a => Enumerable a where
722713 -- occur downwards or upwards depending on whether @then@ comes before or
723714 -- after @from@.
724715 --
725− -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6
716+ -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 2 6
726717 -- [0,2,4,6]
727718 --
728− -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
719+ -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
729720 -- [0,-2,-4,-6]
730721 --
731722 enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a
@@ -1075,17 +1066,14 @@ generate n gen = generateM n (return . gen)
10751066-- Iteration
10761067-------------------------------------------------------------------------------
10771068
1078−-- |
1079−-- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)
1080−--
1081−-- Generate an infinite stream with the first element generated by the action
1082−-- @m@ and each successive element derived by applying the monadic function
1083−-- @f@ on the previous element.
1069+-- | Generate an infinite stream with the first element generated by the action
1070+-- @m@ and each successive element derived by applying the monadic function @f@
1071+-- on the previous element.
10841072--
10851073-- >>> :{
10861074-- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)
10871075-- & Stream.take 3
1088−-- & Stream.fold Fold.toList
1076+-- & Stream.toList
10891077-- :}
10901078-- 0
10911079-- 1
@@ -1099,14 +1087,11 @@ iterateM step = unfold (Unfold.iterateM step)
10991087iterateM step = Stream (\_ st -> st >>= \(!x) -> return $ Yield x (step x))
11001088#endif
11011089
1102−-- |
1103−-- >>> iterate f x = x `Stream.cons` iterate f x
1104−--
1105−-- Generate an infinite stream with @x@ as the first element and each
1090+-- | Generate an infinite stream with @x@ as the first element and each
11061091-- successive element derived by applying the function @f@ on the previous
11071092-- element.
11081093--
1109−-- >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1
1094+-- >>> Stream.toList $ Stream.take 5 $ Stream.iterate (+1) 1
11101095-- [1,2,3,4,5]
11111096--
11121097{-# INLINE_NORMAL iterate #-}
@@ -1196,7 +1181,7 @@ fromPtrN n = take n . fromPtr
11961181-- Note that this is completely safe when reading from Haskell string
11971182-- literals because they are guaranteed to be NULL terminated:
11981183--
1199−-- >>> Stream.fold Fold.toList $ Stream.fromByteStr# "\1\2\3\0"#
1184+-- >>> Stream.toList $ Stream.fromByteStr# "\1\2\3\0"#
12001185-- [1,2,3]
12011186--
12021187{-# INLINE fromByteStr# #-}
core/src/Streamly/Internal/Data/Stream/StreamK.hs+40
@@ -255,6 +255,20 @@ unfoldrA = undefined
255255-- Special generation
256256-------------------------------------------------------------------------------
257257
258+-- |
259+-- >>> repeatM = StreamK.sequence . StreamK.repeat
260+-- >>> repeatM = fix . StreamK.consM
261+-- >>> repeatM = cycle1 . StreamK.fromEffect
262+--
263+-- Generate a stream by repeatedly executing a monadic action forever.
264+--
265+-- >>> :{
266+-- repeatAction =
267+-- StreamK.repeatM (threadDelay 1000000 >> print 1)
268+-- & StreamK.take 10
269+-- & StreamK.fold Fold.drain
270+-- :}
271+--
258272repeatM :: Monad m => m a -> StreamK m a
259273repeatM = repeatMWith consM
260274
@@ -276,12 +290,38 @@ fromIndices gen = go 0
276290 where
277291 go n = gen n `cons` go (n + 1)
278292
293+-- |
294+-- >>> iterate f x = x `StreamK.cons` iterate f x
295+--
296+-- Generate an infinite stream with @x@ as the first element and each
297+-- successive element derived by applying the function @f@ on the previous
298+-- element.
299+--
300+-- >>> StreamK.toList $ StreamK.take 5 $ StreamK.iterate (+1) 1
301+-- [1,2,3,4,5]
302+--
279303{-# INLINE iterate #-}
280304iterate :: (a -> a) -> a -> StreamK m a
281305iterate step = go
282306 where
283307 go !s = cons s (go (step s))
284308
309+-- |
310+-- >>> iterateM f m = m >>= \a -> return a `StreamK.consM` iterateM f (f a)
311+--
312+-- Generate an infinite stream with the first element generated by the action
313+-- @m@ and each successive element derived by applying the monadic function
314+-- @f@ on the previous element.
315+--
316+-- >>> :{
317+-- StreamK.iterateM (\x -> print x >> return (x + 1)) (return 0)
318+-- & StreamK.take 3
319+-- & StreamK.toList
320+-- :}
321+-- 0
322+-- 1
323+-- [0,1,2]
324+--
285325{-# INLINE iterateM #-}
286326iterateM :: Monad m => (a -> m a) -> m a -> StreamK m a
287327iterateM = iterateMWith consM
core/src/Streamly/Internal/Data/Stream/StreamK/Type.hs+36
@@ -1688,6 +1688,28 @@ interleaveMin m1 m2 = mkStream $ \st yld _ stp -> do
16881688-- Generation
16891689-------------------------------------------------------------------------------
16901690
1691+-- |
1692+-- >>> :{
1693+-- unfoldr step s =
1694+-- case step s of
1695+-- Nothing -> StreamK.nil
1696+-- Just (a, b) -> a `StreamK.cons` unfoldr step b
1697+-- :}
1698+--
1699+-- Build a stream by unfolding a /pure/ step function @step@ starting from a
1700+-- seed @s@. The step function returns the next element in the stream and the
1701+-- next seed value. When it is done it returns 'Nothing' and the stream ends.
1702+-- For example,
1703+--
1704+-- >>> :{
1705+-- let f b =
1706+-- if b > 2
1707+-- then Nothing
1708+-- else Just (b, b + 1)
1709+-- in StreamK.toList $ StreamK.unfoldr f 0
1710+-- :}
1711+-- [0,1,2]
1712+--
16911713{-# INLINE unfoldr #-}
16921714unfoldr :: (b -> Maybe (a, b)) -> b -> StreamK m a
16931715unfoldr next s0 = build $ \yld stp ->
@@ -1713,6 +1735,20 @@ unfoldrMWith cns step = go
17131735 Just (a, b) -> yld a (go b)
17141736 Nothing -> stp
17151737
1738+-- | Build a stream by unfolding a /monadic/ step function starting from a
1739+-- seed. The step function returns the next element in the stream and the next
1740+-- seed value. When it is done it returns 'Nothing' and the stream ends. For
1741+-- example,
1742+--
1743+-- >>> :{
1744+-- let f b =
1745+-- if b > 2
1746+-- then return Nothing
1747+-- else return (Just (b, b + 1))
1748+-- in StreamK.toList $ StreamK.unfoldrM f 0
1749+-- :}
1750+-- [0,1,2]
1751+--
17161752{-# INLINE unfoldrM #-}
17171753unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> StreamK m a
17181754unfoldrM = unfoldrMWith consM