on master help sign in
Add a Streamly.Internal.Data.Stream module

This is essentially the same as the old SerialT type. It will contain all the Streamly.Prelude operations monomorphized to Serial stream.

commitdae276f7ec0dbad341677a41b1951043e46c7fde authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateTue 24 May 2022, 17:52 (4 years ago) parent7b57bed
7 files changed+563
core/src/Streamly/Internal/Data/Stream.hs+22 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+38 core/src/Streamly/Internal/Data/Stream/Generate.hs+131 core/src/Streamly/Internal/Data/Stream/Transform.hs+110 core/src/Streamly/Internal/Data/Stream/Type.hs+246 core/streamly-core.cabal+6 streamly.cabal+10
core/src/Streamly/Internal/Data/Stream.hsnew file+22
@@ -0,0 +1,22 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+module 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+
17+where
18+
19+import Streamly.Internal.Data.Stream.Eliminate
20+import Streamly.Internal.Data.Stream.Generate
21+import Streamly.Internal.Data.Stream.Transform
22+import Streamly.Internal.Data.Stream.Type
core/src/Streamly/Internal/Data/Stream/Eliminate.hsnew file+38
@@ -0,0 +1,38 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Eliminate
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+module Streamly.Internal.Data.Stream.Eliminate
11+ (
12+ drain
13+ )
14+where
15+
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
23+
24+import Prelude hiding (map, mapM, repeat, filter)
25+
26+#include "inline.hs"
27+
28+-- |
29+-- > drain = mapM_ (\_ -> return ())
30+-- > drain = Stream.fold Fold.drain
31+--
32+-- Run a stream serially, discarding the results.
33+--
34+-- /Pre-release/
35+--
36+{-# INLINE drain #-}
37+drain :: Monad m => Stream m a -> m ()
38+drain (Stream m) = P.drain m
core/src/Streamly/Internal/Data/Stream/Generate.hsnew file+131
@@ -0,0 +1,131 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Generate
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+module Streamly.Internal.Data.Stream.Generate
11+ (
12+ cons
13+ , consM
14+ , repeat
15+ , serial
16+ , unfoldrM
17+ )
18+where
19+
20+#if __GLASGOW_HASKELL__ < 808
21+import Data.Semigroup (Semigroup(..))
22+#endif
23+#if __GLASGOW_HASKELL__ < 808
24+import Data.Semigroup (Semigroup(..))
25+#endif
26+
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"
36+
37+-- $setup
38+-- >>> :m
39+-- >>> import Control.Concurrent (threadDelay)
40+-- >>> import Data.IORef
41+-- >>> import Prelude hiding (zipWith, concatMap, concat)
42+-- >>> import qualified Streamly.Internal.Data.Stream as Stream
43+--
44+
45+-- | Construct a stream by adding a pure value at the head of an existing
46+-- stream. This is the same as @(return a) \`consM` r@ but
47+-- more efficient. For concurrent streams this is not concurrent whereas
48+-- 'consM' is concurrent. For example:
49+--
50+-- @
51+-- > toList $ 1 \`cons` 2 \`cons` 3 \`cons` nil
52+-- [1,2,3]
53+-- @
54+--
55+-- /Pre-release/
56+--
57+{-# INLINE cons #-}
58+cons :: a -> Stream m a -> Stream m a
59+cons x (Stream ms) = Stream $ K.cons x ms
60+
61+-- | Constructs a stream by adding a monadic action at the head of an
62+-- existing stream. For example:
63+--
64+-- @
65+-- > toList $ getLine \`consM` getLine \`consM` nil
66+-- hello
67+-- world
68+-- ["hello","world"]
69+-- @
70+--
71+-- /Pre-release/
72+--
73+{-# INLINE consM #-}
74+{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}
75+consM :: 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
86+
87+------------------------------------------------------------------------------
88+-- Combining
89+------------------------------------------------------------------------------
90+
91+-- | Appends two streams sequentially, yielding all elements from the first
92+-- stream, and then all elements from the second stream.
93+--
94+-- >>> import Streamly.Internal.Data.Stream (serial)
95+-- >>> stream1 = Stream.fromList [1,2]
96+-- >>> stream2 = Stream.fromList [3,4]
97+-- >>> Stream.toList $ stream1 `serial` stream2
98+-- [1,2,3,4]
99+--
100+-- This operation can be used to fold an infinite lazy container of streams.
101+--
102+-- /Pre-release/
103+--
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)
core/src/Streamly/Internal/Data/Stream/Transform.hsnew file+110
@@ -0,0 +1,110 @@
1+-- |
2+-- Module : Streamly.Internal.Data.Stream.Transform
3+-- Copyright : (c) 2017 Composewell Technologies
4+--
5+-- License : BSD-3-Clause
6+-- Maintainer : streamly@composewell.com
7+-- Stability : experimental
8+-- Portability : GHC
9+--
10+module Streamly.Internal.Data.Stream.Transform
11+(
12+ filter
13+, foldFilter
14+, map
15+, mapM
16+)
17+where
18+
19+import Streamly.Internal.Data.Fold.Type (Fold)
20+
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
27+
28+#include "Instances.hs"
29+#include "inline.hs"
30+
31+--
32+-- $setup
33+-- >>> :m
34+-- >>> import Streamly.Internal.Data.Stream as Stream
35+-- >>> import qualified Streamly.Data.Fold as Fold
36+-- >>> import qualified Streamly.Internal.Data.Fold as Fold
37+-- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))
38+--
39+-- >>> hSetBuffering stdout LineBuffering
40+
41+------------------------------------------------------------------------------
42+-- Transformation by Mapping
43+------------------------------------------------------------------------------
44+
45+-- |
46+-- @
47+-- mapM f = sequence . map f
48+-- @
49+--
50+-- Apply a monadic function to each element of the stream and replace it with
51+-- the output of the resulting action.
52+--
53+-- @
54+-- > drain $ Stream.mapM putStr $ Stream.fromList ["a", "b", "c"]
55+-- abc
56+--
57+-- > :{
58+-- drain $ Stream.replicateM 10 (return 1)
59+-- & (fromSerial . Stream.mapM (\x -> threadDelay 1000000 >> print x))
60+-- :}
61+-- 1
62+-- ...
63+-- 1
64+--
65+-- > drain $ Stream.replicateM 10 (return 1)
66+-- & (fromAsync . Stream.mapM (\x -> threadDelay 1000000 >> print x))
67+-- @
68+--
69+-- /Concurrent (do not use with 'fromParallel' on infinite streams)/
70+--
71+-- /Pre-release/
72+--
73+{-# INLINE mapM #-}
74+mapM :: 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
76+
77+-- |
78+-- @
79+-- map = fmap
80+-- @
81+--
82+-- Same as 'fmap'.
83+--
84+-- @
85+-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
86+-- [2,3,4]
87+-- @
88+--
89+-- /Pre-release/
90+--
91+{-# INLINE map #-}
92+map :: Monad m => (a -> b) -> Stream m a -> Stream m b
93+map f = mapM (return . f)
94+
95+-- | Include only those elements that pass a predicate.
96+--
97+{-# INLINE filter #-}
98+filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a
99+filter p = fromStreamD . D.filter p . toStreamD
100+
101+-- | Use a filtering fold on a stream.
102+--
103+-- > Stream.sum $ Stream.foldFilter (Fold.satisfy (> 5)) $ Stream.fromList [1..10]
104+-- 40
105+--
106+-- /Pre-release/
107+--
108+{-# INLINE foldFilter #-}
109+foldFilter :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b
110+foldFilter p = fromStreamD . D.foldFilter p . toStreamD
core/src/Streamly/Internal/Data/Stream/Type.hsnew file+246
@@ -0,0 +1,246 @@
1+{-# LANGUAGE UndecidableInstances #-}
2+{-# OPTIONS_GHC -Wno-orphans #-}
3+
4+-- |
5+-- Module : Streamly.Internal.Data.Stream.Type
6+-- Copyright : (c) 2017 Composewell Technologies
7+--
8+-- License : BSD-3-Clause
9+-- Maintainer : streamly@composewell.com
10+-- Stability : experimental
11+-- Portability : GHC
12+--
13+module Streamly.Internal.Data.Stream.Type
14+ (
15+ Stream(..)
16+ , fromStreamD
17+ , toStreamD
18+ , foldWith
19+
20+ -- * Conversion operations
21+ , fromList
22+ , toList
23+
24+ -- * Transformation
25+ , list
26+ )
27+where
28+
29+import Control.Applicative (liftA2)
30+import Control.DeepSeq (NFData1(..))
31+import Control.Monad.Base (MonadBase(..), liftBaseDefault)
32+import Control.Monad.Catch (MonadThrow, throwM)
33+import Control.Monad.IO.Class (MonadIO(..))
34+import Control.Monad.Reader.Class (MonadReader(..))
35+import Control.Monad.State.Class (MonadState(..))
36+import Control.Monad.Trans.Class (MonadTrans(lift))
37+import Data.Functor.Identity (Identity(..), runIdentity)
38+#if __GLASGOW_HASKELL__ < 808
39+import Data.Semigroup (Semigroup(..))
40+#endif
41+import Streamly.Internal.Data.Fold.Type (Fold)
42+
43+import 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
46+import qualified Streamly.Internal.Data.Stream.StreamD.Type as D
47+import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
48+
49+import Prelude hiding (map, mapM, repeat, filter)
50+
51+#include "Instances.hs"
52+#include "inline.hs"
53+
54+-- $setup
55+-- >>> import qualified Streamly.Prelude as Stream
56+
57+------------------------------------------------------------------------------
58+-- Stream
59+------------------------------------------------------------------------------
60+
61+-- | For 'Stream' streams:
62+--
63+-- @
64+-- (<>) = 'Streamly.Prelude.serial' -- 'Semigroup'
65+-- (>>=) = flip . 'Streamly.Prelude.concatMapWith' 'Streamly.Prelude.serial' -- 'Monad'
66+-- @
67+--
68+-- A single 'Monad' bind behaves like a @for@ loop:
69+--
70+-- >>> :{
71+-- Stream.toList $ do
72+-- x <- Stream.fromList [1,2] -- foreach x in stream
73+-- return x
74+-- :}
75+-- [1,2]
76+--
77+-- Nested monad binds behave like nested @for@ loops:
78+--
79+-- >>> :{
80+-- Stream.toList $ do
81+-- x <- Stream.fromList [1,2] -- foreach x in stream
82+-- y <- Stream.fromList [3,4] -- foreach y in stream
83+-- return (x, y)
84+-- :}
85+-- [(1,3),(1,4),(2,3),(2,4)]
86+--
87+-- /Since: 0.2.0 ("Streamly")/
88+--
89+-- @since 0.8.0
90+newtype Stream m a = Stream {getStream :: K.Stream m a}
91+ -- XXX when deriving do we inherit an INLINE?
92+ deriving (Semigroup, Monoid, MonadTrans)
93+
94+------------------------------------------------------------------------------
95+-- Monad
96+------------------------------------------------------------------------------
97+
98+instance Monad m => Monad (Stream m) where
99+ return = pure
100+
101+ -- Benchmarks better with StreamD bind and pure:
102+ -- toList, filterAllout, *>, *<, >> (~2x)
103+ --
104+ -- pure = Stream . D.fromStreamD . D.fromPure
105+ -- m >>= f = D.fromStreamD $ D.concatMap (D.toStreamD . f) (D.toStreamD m)
106+
107+ -- Benchmarks better with CPS bind and pure:
108+ -- Prime sieve (25x)
109+ -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)
110+ --
111+ {-# INLINE (>>=) #-}
112+ (>>=) (Stream m) f = Stream $ K.bindWith K.serial m (getStream . f)
113+
114+ {-# INLINE (>>) #-}
115+ (>>) = (*>)
116+
117+------------------------------------------------------------------------------
118+-- Other instances
119+------------------------------------------------------------------------------
120+
121+{-# INLINE apSerial #-}
122+apSerial :: Monad m => Stream m (a -> b) -> Stream m a -> Stream m b
123+apSerial (Stream m1) (Stream m2) =
124+ Stream $ D.toStreamK $ D.fromStreamK m1 <*> D.fromStreamK m2
125+
126+{-# INLINE apSequence #-}
127+apSequence :: Monad m => Stream m a -> Stream m b -> Stream m b
128+apSequence (Stream m1) (Stream m2) =
129+ Stream $ D.toStreamK $ D.fromStreamK m1 *> D.fromStreamK m2
130+
131+{-# INLINE apDiscardSnd #-}
132+apDiscardSnd :: Monad m => Stream m a -> Stream m b -> Stream m a
133+apDiscardSnd (Stream m1) (Stream m2) =
134+ Stream $ D.toStreamK $ D.fromStreamK m1 <* D.fromStreamK m2
135+
136+-- Note: we need to define all the typeclass operations because we want to
137+-- INLINE them.
138+instance Monad m => Applicative (Stream m) where
139+ {-# INLINE pure #-}
140+ pure = Stream . K.fromPure
141+
142+ {-# INLINE (<*>) #-}
143+ (<*>) = apSerial
144+ -- (<*>) = K.apSerial
145+
146+ {-# INLINE liftA2 #-}
147+ liftA2 f x = (<*>) (fmap f x)
148+
149+ {-# INLINE (*>) #-}
150+ (*>) = apSequence
151+ -- (*>) = K.apSerialDiscardFst
152+
153+ {-# INLINE (<*) #-}
154+ (<*) = apDiscardSnd
155+ -- (<*) = K.apSerialDiscardSnd
156+
157+MONAD_COMMON_INSTANCES(Stream,)
158+NFDATA1_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
core/streamly-core.cabal+6
@@ -199,6 +199,7 @@ library
199199 include-dirs:
200200 src
201201 , src/Streamly/Internal/Data/Array
202+ , src/Streamly/Internal/Data/Stream
202203
203204 if os(windows)
204205 c-sources: src/Streamly/Internal/Data/Time/Clock/Windows.c
@@ -293,6 +294,11 @@ library
293294 , Streamly.Internal.Data.Stream.WSerial
294295 , Streamly.Internal.Data.Stream.Zip
295296 , 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
296302
297303 -- streamly-core-data-arrays
298304 -- XXX Depends on primitive
streamly.cabal+10
@@ -192,6 +192,11 @@ extra-source-files:
192192 core/src/Streamly/Internal/Data/Parser.hs
193193 core/src/Streamly/Internal/Data/Pipe.hs
194194 core/src/Streamly/Internal/Data/Stream/Serial.hs
195+ core/src/Streamly/Internal/Data/Stream.hs
196+ core/src/Streamly/Internal/Data/Stream/Eliminate.hs
197+ core/src/Streamly/Internal/Data/Stream/Generate.hs
198+ core/src/Streamly/Internal/Data/Stream/Transform.hs
199+ core/src/Streamly/Internal/Data/Stream/Type.hs
195200 core/src/Streamly/Internal/Data/Stream/WSerial.hs
196201 core/src/Streamly/Internal/Data/Stream/Zip.hs
197202 core/src/Streamly/Internal/Data/List.hs
@@ -600,6 +605,11 @@ library
600605 , Streamly.Internal.Data.Stream.Serial
601606 , Streamly.Internal.Data.Stream.Zip
602607 , Streamly.Internal.Data.List
608+ , Streamly.Internal.Data.Stream
609+ , Streamly.Internal.Data.Stream.Eliminate
610+ , Streamly.Internal.Data.Stream.Generate
611+ , Streamly.Internal.Data.Stream.Transform
612+ , Streamly.Internal.Data.Stream.Type
603613
604614 -- streamly-core-data-arrays
605615 -- XXX Depends on primitive