on master help sign in
Refactor, reformat Stream module

Remove redundant operations, operations that can be expressed by unfolds (generatio opsn) and folds (elimination ops).

commit187295832e137694a4d8c986613e948257932632 authorHarendra Kumar <harendra@composewell.com> dateThu 2 Jun 2022, 17:25 (4 years ago) parentdae276f
8 files changed+108−218
core/src/Streamly/Internal/Data/Stream.hs+5−7 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+27−20 core/src/Streamly/Internal/Data/Stream/Generate.hs+13−54 core/src/Streamly/Internal/Data/Stream/Instances.hs+2−2 core/src/Streamly/Internal/Data/Stream/Transform.hs+13−18 core/src/Streamly/Internal/Data/Stream/Type.hs+38−107 core/streamly-core.cabal+5−5 streamly.cabal+5−5
core/src/Streamly/Internal/Data/Stream.hs+5−7
@@ -1,19 +1,17 @@
11-- |
22-- Module : Streamly.Internal.Data.Stream
33-- Copyright : (c) 2017 Composewell Technologies
4−--
54-- License : BSD-3-Clause
65-- Maintainer : streamly@composewell.com
76-- Stability : experimental
87-- Portability : GHC
98--
109module Streamly.Internal.Data.Stream
11−( module Streamly.Internal.Data.Stream.Type
12−, module Streamly.Internal.Data.Stream.Eliminate
13−, module Streamly.Internal.Data.Stream.Generate
14−, module Streamly.Internal.Data.Stream.Transform
15−)
16−
10+ ( module Streamly.Internal.Data.Stream.Type
11+ , module Streamly.Internal.Data.Stream.Eliminate
12+ , module Streamly.Internal.Data.Stream.Generate
13+ , module Streamly.Internal.Data.Stream.Transform
14+ )
1715where
1816
1917import Streamly.Internal.Data.Stream.Eliminate
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+27−20
@@ -1,7 +1,6 @@
11-- |
22-- Module : Streamly.Internal.Data.Stream.Eliminate
33-- Copyright : (c) 2017 Composewell Technologies
4−--
54-- License : BSD-3-Clause
65-- Maintainer : streamly@composewell.com
76-- Stability : experimental
@@ -9,30 +8,38 @@
98--
109module Streamly.Internal.Data.Stream.Eliminate
1110 (
12− drain
11+ fold
1312 )
1413where
1514
16−#if __GLASGOW_HASKELL__ < 808
17−import Data.Semigroup (Semigroup(..))
18−#endif
19−
20−import qualified Streamly.Internal.Data.Stream.Common as P
21−
22−import Streamly.Internal.Data.Stream.Type
15+import Streamly.Internal.Data.Fold.Type (Fold)
16+import Streamly.Internal.Data.Stream.Type (Stream)
2317
24−import Prelude hiding (map, mapM, repeat, filter)
18+import qualified Streamly.Internal.Data.Stream.Type as Stream
19+import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD
2520
26−#include "inline.hs"
27−
28−-- |
29−-- > drain = mapM_ (\_ -> return ())
30−-- > drain = Stream.fold Fold.drain
21+-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
22+-- expression strictly at each step. The behavior is similar to 'foldl''. A
23+-- 'Fold' can terminate early without consuming the full stream. See the
24+-- documentation of individual 'Fold's for termination behavior.
3125--
32−-- Run a stream serially, discarding the results.
26+-- >>> Stream.foldWith Fold.sum (Stream.enumerateFromTo 1 100)
27+-- 5050
3328--
34−-- /Pre-release/
29+-- Folds never fail, therefore, they produce a default value even when no input
30+-- is provided. It means we can always fold an empty stream and get a valid
31+-- result. For example:
32+--
33+-- >>> Stream.fold Fold.sum Stream.nil
34+-- 0
35+--
36+-- However, 'foldMany' on an empty stream results in an empty stream.
37+-- Therefore, @Stream.foldWith f@ is not the same as @Stream.head . Stream.foldMany
38+-- f@.
3539--
36−{-# INLINE drain #-}
37−drain :: Monad m => Stream m a -> m ()
38−drain (Stream m) = P.drain m
40+-- @fold f = Stream.parse (Parser.fromFold f)@
41+--
42+-- /Pre-release/
43+{-# INLINE fold #-}
44+fold :: Monad m => Fold m a b -> Stream m a -> m b
45+fold fld = StreamD.fold fld . Stream.toStreamD
core/src/Streamly/Internal/Data/Stream/Generate.hs+13−54
@@ -1,7 +1,6 @@
11-- |
22-- Module : Streamly.Internal.Data.Stream.Generate
33-- Copyright : (c) 2017 Composewell Technologies
4−--
54-- License : BSD-3-Clause
65-- Maintainer : streamly@composewell.com
76-- Stability : experimental
@@ -11,9 +10,7 @@ module Streamly.Internal.Data.Stream.Generate
1110 (
1211 cons
1312 , consM
14− , repeat
15− , serial
16− , unfoldrM
13+ , append
1714 )
1815where
1916
@@ -23,16 +20,10 @@ import Data.Semigroup (Semigroup(..))
2320#if __GLASGOW_HASKELL__ < 808
2421import Data.Semigroup (Semigroup(..))
2522#endif
23+import Streamly.Internal.Data.Stream.Type (Stream)
2624
27−import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
28−import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
29−import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
30−
31−import Prelude hiding (map, mapM, repeat, filter)
32−
33−import Streamly.Internal.Data.Stream.Type
34−
35−#include "inline.hs"
25+import qualified Streamly.Internal.Data.Stream.Type as Stream
26+import qualified Streamly.Internal.Data.Stream.StreamK.Type as StreamK
3627
3728-- $setup
3829-- >>> :m
@@ -56,7 +47,7 @@ import Streamly.Internal.Data.Stream.Type
5647--
5748{-# INLINE cons #-}
5849cons :: a -> Stream m a -> Stream m a
59−cons x (Stream ms) = Stream $ K.cons x ms
50+cons x = Stream.fromStreamK . StreamK.cons x . Stream.toStreamK
6051
6152-- | Constructs a stream by adding a monadic action at the head of an
6253-- existing stream. For example:
@@ -68,21 +59,14 @@ cons x (Stream ms) = Stream $ K.cons x ms
6859-- ["hello","world"]
6960-- @
7061--
62+-- > consM x xs = fromEffect x `append` xs
63+--
7164-- /Pre-release/
7265--
7366{-# INLINE consM #-}
7467{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
7568consM :: Monad m => m a -> Stream m a -> Stream m a
76−consM m (Stream ms) = Stream $ K.consM m ms
77−
78−-- |
79−-- Generate an infinite stream by repeating a pure value.
80−--
81−-- /Pre-release/
82−--
83−{-# INLINE_NORMAL repeat #-}
84−repeat :: Monad m => a -> Stream m a
85−repeat = Stream . D.toStreamK . D.repeat
69+consM m = Stream.fromStreamK . StreamK.consM m . Stream.toStreamK
8670
8771------------------------------------------------------------------------------
8872-- Combining
@@ -91,41 +75,16 @@ repeat = Stream . D.toStreamK . D.repeat
9175-- | Appends two streams sequentially, yielding all elements from the first
9276-- stream, and then all elements from the second stream.
9377--
94−-- >>> import Streamly.Internal.Data.Stream (serial)
78+-- >>> import Streamly.Internal.Data.Stream (append)
9579-- >>> stream1 = Stream.fromList [1,2]
9680-- >>> stream2 = Stream.fromList [3,4]
97−-- >>> Stream.toList $ stream1 `serial` stream2
81+-- >>> Stream.toList $ stream1 `append` stream2
9882-- [1,2,3,4]
9983--
10084-- This operation can be used to fold an infinite lazy container of streams.
10185--
10286-- /Pre-release/
10387--
104−{-# INLINE serial #-}
105−serial :: Stream m a -> Stream m a -> Stream m a
106−serial = (<>)
107−
108−-- | Build a stream by unfolding a /monadic/ step function starting from a
109−-- seed. The step function returns the next element in the stream and the next
110−-- seed value. When it is done it returns 'Nothing' and the stream ends. For
111−-- example,
112−--
113−-- @
114−-- let f b =
115−-- if b > 3
116−-- then return Nothing
117−-- else print b >> return (Just (b, b + 1))
118−-- in drain $ unfoldrM f 0
119−-- @
120−-- @
121−-- 0
122−-- 1
123−-- 2
124−-- 3
125−-- @
126−--
127−-- /Pre-release/
128−--
129−{-# INLINE unfoldrM #-}
130−unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a
131−unfoldrM step seed = Stream $ D.toStreamK (D.unfoldrM step seed)
88+{-# INLINE append #-}
89+append :: Stream m a -> Stream m a -> Stream m a
90+append = (<>)
core/src/Streamly/Internal/Data/Stream/Instances.hs+2−2
@@ -139,7 +139,7 @@ instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
139139 \
140140 {-# INLINE maximum #-}; \
141141 maximum = \
142− fromMaybe (errorWithoutStackTrace $ "maximum: empty stream") \
142+ fromMaybe (errorWithoutStackTrace "maximum: empty stream") \
143143 . toMaybe \
144144 . foldl' getMax Nothing' where { \
145145 getMax Nothing' x = Just' x; \
@@ -147,7 +147,7 @@ instance (Foldable m, Monad m) => Foldable (STREAM m) where { \
147147 \
148148 {-# INLINE minimum #-}; \
149149 minimum = \
150− fromMaybe (errorWithoutStackTrace $ "minimum: empty stream") \
150+ fromMaybe (errorWithoutStackTrace "minimum: empty stream") \
151151 . toMaybe \
152152 . foldl' getMin Nothing' where { \
153153 getMin Nothing' x = Just' x; \
core/src/Streamly/Internal/Data/Stream/Transform.hs+13−18
@@ -1,32 +1,27 @@
11-- |
22-- Module : Streamly.Internal.Data.Stream.Transform
33-- Copyright : (c) 2017 Composewell Technologies
4−--
54-- License : BSD-3-Clause
65-- Maintainer : streamly@composewell.com
76-- Stability : experimental
87-- Portability : GHC
98--
109module Streamly.Internal.Data.Stream.Transform
11−(
12− filter
13−, foldFilter
14−, map
15−, mapM
16−)
10+ (
11+ filter
12+ , foldFilter
13+ , map
14+ , mapM
15+ )
1716where
1817
1918import Streamly.Internal.Data.Fold.Type (Fold)
19+import Streamly.Internal.Data.Stream.Type (Stream)
2020
21−import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
22−import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
23−
24−import Prelude hiding (map, mapM, repeat, filter)
25−
26−import Streamly.Internal.Data.Stream.Type
21+import qualified Streamly.Internal.Data.Stream.Type as Stream
22+import qualified Streamly.Internal.Data.Stream.StreamD.Transform as StreamD
2723
28−#include "Instances.hs"
29−#include "inline.hs"
24+import Prelude hiding (map, mapM, filter)
3025
3126--
3227-- $setup
@@ -72,7 +67,7 @@ import Streamly.Internal.Data.Stream.Type
7267--
7368{-# INLINE mapM #-}
7469mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
75−mapM f (Stream m) = Stream $ D.toStreamK $ D.mapM f $ D.fromStreamK m
70+mapM f = Stream.fromStreamD . StreamD.mapM f . Stream.toStreamD
7671
7772-- |
7873-- @
@@ -96,7 +91,7 @@ map f = mapM (return . f)
9691--
9792{-# INLINE filter #-}
9893filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
99−filter p = fromStreamD . D.filter p . toStreamD
94+filter p = Stream.fromStreamD . StreamD.filter p . Stream.toStreamD
10095
10196-- | Use a filtering fold on a stream.
10297--
@@ -107,4 +102,4 @@ filter p = fromStreamD . D.filter p . toStreamD
107102--
108103{-# INLINE foldFilter #-}
109104foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
110−foldFilter p = fromStreamD . D.foldFilter p . toStreamD
105+foldFilter p = Stream.fromStreamD . StreamD.foldFilter p . Stream.toStreamD
core/src/Streamly/Internal/Data/Stream/Type.hs+38−107
@@ -1,10 +1,8 @@
11{-# LANGUAGE UndecidableInstances #-}
2−{-# OPTIONS_GHC -Wno-orphans #-}
32
43-- |
54-- Module : Streamly.Internal.Data.Stream.Type
65-- Copyright : (c) 2017 Composewell Technologies
7−--
86-- License : BSD-3-Clause
97-- Maintainer : streamly@composewell.com
108-- Stability : experimental
@@ -12,42 +10,40 @@
1210--
1311module Streamly.Internal.Data.Stream.Type
1412 (
15− Stream(..)
13+ Stream
14+ , fromStreamK
15+ , toStreamK
1616 , fromStreamD
1717 , toStreamD
18− , foldWith
19−
20− -- * Conversion operations
21− , fromList
22− , toList
23−
24− -- * Transformation
25− , list
2618 )
2719where
2820
2921import Control.Applicative (liftA2)
30−import Control.DeepSeq (NFData1(..))
22+import Control.DeepSeq (NFData(..), NFData1(..))
3123import Control.Monad.Base (MonadBase(..), liftBaseDefault)
3224import Control.Monad.Catch (MonadThrow, throwM)
3325import Control.Monad.IO.Class (MonadIO(..))
3426import Control.Monad.Reader.Class (MonadReader(..))
3527import Control.Monad.State.Class (MonadState(..))
3628import Control.Monad.Trans.Class (MonadTrans(lift))
29+import Data.Foldable (Foldable(foldl'), fold)
3730import Data.Functor.Identity (Identity(..), runIdentity)
31+import Data.Maybe (fromMaybe)
32+import Data.Semigroup (Endo(..))
3833#if __GLASGOW_HASKELL__ < 808
3934import Data.Semigroup (Semigroup(..))
4035#endif
41−import Streamly.Internal.Data.Fold.Type (Fold)
36+import GHC.Exts (IsList(..), IsString(..), oneShot)
37+import Streamly.Internal.BaseCompat ((#.))
38+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
39+import Text.Read
40+ ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
41+ , readListPrecDefault)
4242
4343import qualified Streamly.Internal.Data.Stream.Common as P
44−import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
45−import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D
4644import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
4745import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
4846
49−import Prelude hiding (map, mapM, repeat, filter)
50−
5147#include "Instances.hs"
5248#include "inline.hs"
5349
@@ -87,10 +83,30 @@ import Prelude hiding (map, mapM, repeat, filter)
8783-- /Since: 0.2.0 ("Streamly")/
8884--
8985-- @since 0.8.0
90−newtype Stream m a = Stream {getStream :: K.Stream m a}
86+newtype Stream m a = Stream (K.Stream m a)
9187 -- XXX when deriving do we inherit an INLINE?
9288 deriving (Semigroup, Monoid, MonadTrans)
9389
90+------------------------------------------------------------------------------
91+-- Conversions
92+------------------------------------------------------------------------------
93+
94+{-# INLINE fromStreamK #-}
95+fromStreamK :: K.Stream m a -> Stream m a
96+fromStreamK = Stream
97+
98+{-# INLINE toStreamK #-}
99+toStreamK :: Stream m a -> K.Stream m a
100+toStreamK (Stream k) = k
101+
102+{-# INLINE toStreamD #-}
103+toStreamD :: Applicative m => Stream m a -> D.Stream m a
104+toStreamD = D.fromStreamK . toStreamK
105+
106+{-# INLINE fromStreamD #-}
107+fromStreamD :: Monad m => D.Stream m a -> Stream m a
108+fromStreamD = fromStreamK . D.toStreamK
109+
94110------------------------------------------------------------------------------
95111-- Monad
96112------------------------------------------------------------------------------
@@ -109,7 +125,7 @@ instance Monad m => Monad (Stream m) where
109125 -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
110126 --
111127 {-# INLINE (>>=) #-}
112− (>>=) (Stream m) f = Stream $ K.bindWith K.serial m (getStream . f)
128+ (>>=) (Stream m) f = Stream $ K.bindWith K.serial m (toStreamK . f)
113129
114130 {-# INLINE (>>) #-}
115131 (>>) = (*>)
@@ -155,92 +171,7 @@ instance Monad m => Applicative (Stream m) where
155171 -- (<*) = K.apSerialDiscardSnd
156172
157173MONAD_COMMON_INSTANCES(Stream,)
174+LIST_INSTANCES(Stream)
158175NFDATA1_INSTANCE(Stream)
159−
160−-- XXX Renamed to foldWith because Stream has a Foldable instance having
161−-- method fold.
162−--
163−-- | Fold a stream using the supplied left 'Fold' and reducing the resulting
164−-- expression strictly at each step. The behavior is similar to 'foldl''. A
165−-- 'Fold' can terminate early without consuming the full stream. See the
166−-- documentation of individual 'Fold's for termination behavior.
167−--
168−-- >>> Stream.foldWith Fold.sum (Stream.enumerateFromTo 1 100)
169−-- 5050
170−--
171−-- Folds never fail, therefore, they produce a default value even when no input
172−-- is provided. It means we can always fold an empty stream and get a valid
173−-- result. For example:
174−--
175−-- >>> Stream.foldWith Fold.sum Stream.nil
176−-- 0
177−--
178−-- However, 'foldMany' on an empty stream results in an empty stream.
179−-- Therefore, @Stream.foldWith f@ is not the same as @Stream.head . Stream.foldMany
180−-- f@.
181−--
182−-- @foldWith f = Stream.parse (Parser.fromFold f)@
183−--
184−-- /Pre-release/
185−{-# INLINE foldWith #-}
186−foldWith :: Monad m => Fold m a b -> Stream m a -> m b
187−foldWith fld m = D.fold fld $ toStreamD m
188−
189−-- XXX Renamed to "list" because fromList is present in IsList instance.
190−--
191−-- |
192−-- @
193−-- fromList = 'Prelude.foldr' 'K.cons' 'K.nil'
194−-- @
195−--
196−-- Construct a stream from a list of pure values. This is more efficient than
197−-- 'K.fromFoldable' for serial streams.
198−--
199−-- @since 0.4.0
200−{-# INLINE_EARLY list #-}
201−list :: Monad m => [a] -> Stream m a
202−list = fromStreamD . D.fromList
203−
204−------------------------------------------------------------------------------
205−-- Construction
206−------------------------------------------------------------------------------
207−
208−-- | Build a stream by unfolding a /monadic/ step function starting from a
209−-- seed. The step function returns the next element in the stream and the next
210−-- seed value. When it is done it returns 'Nothing' and the stream ends. For
211−-- example,
212−--
213−-- @
214−-- let f b =
215−-- if b > 3
216−-- then return Nothing
217−-- else print b >> return (Just (b, b + 1))
218−-- in drain $ unfoldrM f 0
219−-- @
220−-- @
221−-- 0
222−-- 1
223−-- 2
224−-- 3
225−-- @
226−--
227−-- /Pre-release/
228−--
229−{-# INLINE toStreamD #-}
230−toStreamD :: Applicative m => Stream m a -> D.Stream m a
231−toStreamD (Stream m) = D.fromStreamK m
232−
233−{-# INLINE fromStreamD #-}
234−fromStreamD :: Monad m => D.Stream m a -> Stream m a
235−fromStreamD m = Stream $ D.toStreamK m
236−
237−{-# INLINE_EARLY fromList #-}
238−fromList :: Monad m => [a] -> Stream m a
239−fromList = fromStreamD . D.fromList
240−
241−-- | Convert a stream into a list in the underlying monad.
242−--
243−-- @since 0.1.0
244−{-# INLINE toList #-}
245−toList :: Monad m => Stream m a -> m [a]
246−toList m = D.toList $ toStreamD m
176+FOLDABLE_INSTANCE(Stream)
177+TRAVERSABLE_INSTANCE(Stream)
core/streamly-core.cabal+5−5
@@ -290,15 +290,15 @@ library
290290 , Streamly.Internal.Data.Fold.Window
291291 , Streamly.Internal.Data.Parser
292292 , Streamly.Internal.Data.Pipe
293+ , Streamly.Internal.Data.Stream.Type
294+ , Streamly.Internal.Data.Stream.Eliminate
295+ , Streamly.Internal.Data.Stream.Generate
296+ , Streamly.Internal.Data.Stream.Transform
297+ , Streamly.Internal.Data.Stream
293298 , Streamly.Internal.Data.Stream.Serial
294299 , Streamly.Internal.Data.Stream.WSerial
295300 , Streamly.Internal.Data.Stream.Zip
296301 , Streamly.Internal.Data.List
297− , Streamly.Internal.Data.Stream
298− , Streamly.Internal.Data.Stream.Eliminate
299− , Streamly.Internal.Data.Stream.Generate
300− , Streamly.Internal.Data.Stream.Transform
301− , Streamly.Internal.Data.Stream.Type
302302
303303 -- streamly-core-data-arrays
304304 -- XXX Depends on primitive
streamly.cabal+5−5
@@ -602,14 +602,14 @@ library
602602 , Streamly.Internal.Data.Fold.Window
603603 , Streamly.Internal.Data.Parser
604604 , Streamly.Internal.Data.Pipe
605− , Streamly.Internal.Data.Stream.Serial
606− , Streamly.Internal.Data.Stream.Zip
607− , Streamly.Internal.Data.List
608− , Streamly.Internal.Data.Stream
605+ , Streamly.Internal.Data.Stream.Type
609606 , Streamly.Internal.Data.Stream.Eliminate
610607 , Streamly.Internal.Data.Stream.Generate
611608 , Streamly.Internal.Data.Stream.Transform
612− , Streamly.Internal.Data.Stream.Type
609+ , Streamly.Internal.Data.Stream
610+ , Streamly.Internal.Data.Stream.Serial
611+ , Streamly.Internal.Data.Stream.Zip
612+ , Streamly.Internal.Data.List
613613
614614 -- streamly-core-data-arrays
615615 -- XXX Depends on primitive