on master help sign in
refactor stream level combinators in another module
commitbb56f1757e682d1918dd58b2a8920e4a72b3c6ba authorHarendra Kumar <harendra.kumar@gmail.com> dateFri 2 Nov 2018, 18:44 (8 years ago) parentc8ae480
7 files changed+233−228
src/Streamly.hs+7−7 src/Streamly/Internal.hs+1−1 src/Streamly/Prelude.hs+1−5 src/Streamly/Streams/Combinators.hs+216 src/Streamly/Streams/Prelude.hs−9 src/Streamly/Streams/SVar.hs+1−204 streamly.cabal+7−2
src/Streamly.hs+7−7
@@ -173,16 +173,16 @@ module Streamly
173173 )
174174where
175175
176−import Streamly.Streams.StreamK hiding (runStream, serial)
177−import Streamly.Streams.Serial
178−import Streamly.Streams.Async
176+import Data.Semigroup (Semigroup(..))
177+import Streamly.SVar (MonadAsync, Rate(..))
179178import Streamly.Streams.Ahead
179+import Streamly.Streams.Async
180+import Streamly.Streams.Combinators
180181import Streamly.Streams.Parallel
181−import Streamly.Streams.Zip
182182import Streamly.Streams.Prelude
183−import Streamly.Streams.SVar
184−import Streamly.SVar (MonadAsync, Rate (..))
185−import Data.Semigroup (Semigroup(..))
183+import Streamly.Streams.Serial
184+import Streamly.Streams.StreamK hiding (runStream, serial)
185+import Streamly.Streams.Zip
186186
187187import qualified Streamly.Streams.StreamD as D
188188import qualified Streamly.Streams.StreamK as K
src/Streamly/Internal.hs+1−1
@@ -16,4 +16,4 @@ module Streamly.Internal
1616 )
1717where
1818
19−import Streamly.Streams.SVar
19+import Streamly.Streams.Combinators (inspectMode)
src/Streamly/Prelude.hs+1−5
@@ -1,9 +1,5 @@
11{-# LANGUAGE CPP #-}
22{-# LANGUAGE FlexibleContexts #-}
3−{-# LANGUAGE FlexibleInstances #-}
4−{-# LANGUAGE MultiParamTypeClasses #-}
5−{-# LANGUAGE RankNTypes #-}
6−{-# LANGUAGE UndecidableInstances #-} -- XXX
73
84#if __GLASGOW_HASKELL__ >= 800
95{-# OPTIONS_GHC -Wno-orphans #-}
@@ -236,7 +232,7 @@ import qualified Prelude
236232import qualified System.IO as IO
237233
238234import Streamly.SVar (MonadAsync, defState, rstState)
239−import Streamly.Streams.SVar (maxYields)
235+import Streamly.Streams.Combinators (maxYields)
240236import Streamly.Streams.StreamK (IsStream(..))
241237import Streamly.Streams.Serial (SerialT)
242238
src/Streamly/Streams/Combinators.hsnew file+216
@@ -0,0 +1,216 @@
1+{-# LANGUAGE CPP #-}
2+
3+#include "inline.hs"
4+
5+-- |
6+-- Module : Streamly.Streams.Combinators
7+-- Copyright : (c) 2017 Harendra Kumar
8+--
9+-- License : BSD3
10+-- Maintainer : harendra.kumar@gmail.com
11+-- Stability : experimental
12+-- Portability : GHC
13+--
14+--
15+module Streamly.Streams.Combinators
16+ ( maxThreads
17+ , maxBuffer
18+ , maxYields
19+ , rate
20+ , avgRate
21+ , minRate
22+ , maxRate
23+ , constRate
24+ , inspectMode
25+ , printState
26+ )
27+where
28+
29+import Control.Monad.IO.Class (MonadIO(liftIO))
30+import Data.Int (Int64)
31+
32+import Streamly.SVar
33+import Streamly.Streams.StreamK
34+import Streamly.Streams.Serial (SerialT)
35+
36+-------------------------------------------------------------------------------
37+-- Concurrency control
38+-------------------------------------------------------------------------------
39+--
40+-- XXX need to write these in direct style otherwise they will break fusion.
41+--
42+-- | Specify the maximum number of threads that can be spawned concurrently for
43+-- any concurrent combinator in a stream.
44+-- A value of 0 resets the thread limit to default, a negative value means
45+-- there is no limit. The default value is 1500.
46+--
47+-- When the actions in a stream are IO bound, having blocking IO calls, this
48+-- option can be used to control the maximum number of in-flight IO requests.
49+-- When the actions are CPU bound this option can be used to
50+-- control the amount of CPU used by the stream.
51+--
52+-- @since 0.4.0
53+{-# INLINE_NORMAL maxThreads #-}
54+maxThreads :: IsStream t => Int -> t m a -> t m a
55+maxThreads n m = fromStream $ Stream $ \st stp sng yld ->
56+ unStream (toStream m) (setMaxThreads n st) stp sng yld
57+
58+{-
59+{-# RULES "maxThreadsSerial serial" maxThreads = maxThreadsSerial #-}
60+maxThreadsSerial :: Int -> SerialT m a -> SerialT m a
61+maxThreadsSerial _ = id
62+-}
63+
64+-- | Specify the maximum size of the buffer for storing the results from
65+-- concurrent computations. If the buffer becomes full we stop spawning more
66+-- concurrent tasks until there is space in the buffer.
67+-- A value of 0 resets the buffer size to default, a negative value means
68+-- there is no limit. The default value is 1500.
69+--
70+-- CAUTION! using an unbounded 'maxBuffer' value (i.e. a negative value)
71+-- coupled with an unbounded 'maxThreads' value is a recipe for disaster in
72+-- presence of infinite streams, or very large streams. Especially, it must
73+-- not be used when 'pure' is used in 'ZipAsyncM' streams as 'pure' in
74+-- applicative zip streams generates an infinite stream causing unbounded
75+-- concurrent generation with no limit on the buffer or threads.
76+--
77+-- @since 0.4.0
78+{-# INLINE_NORMAL maxBuffer #-}
79+maxBuffer :: IsStream t => Int -> t m a -> t m a
80+maxBuffer n m = fromStream $ Stream $ \st stp sng yld ->
81+ unStream (toStream m) (setMaxBuffer n st) stp sng yld
82+
83+{-
84+{-# RULES "maxBuffer serial" maxBuffer = maxBufferSerial #-}
85+maxBufferSerial :: Int -> SerialT m a -> SerialT m a
86+maxBufferSerial _ = id
87+-}
88+
89+-- | Specify the pull rate of a stream.
90+-- A 'Nothing' value resets the rate to default which is unlimited. When the
91+-- rate is specified, concurrent production may be ramped up or down
92+-- automatically to achieve the specified yield rate. The specific behavior for
93+-- different styles of 'Rate' specifications is documented under 'Rate'. The
94+-- effective maximum production rate achieved by a stream is governed by:
95+--
96+-- * The 'maxThreads' limit
97+-- * The 'maxBuffer' limit
98+-- * The maximum rate that the stream producer can achieve
99+-- * The maximum rate that the stream consumer can achieve
100+--
101+-- @since 0.5.0
102+{-# INLINE_NORMAL rate #-}
103+rate :: IsStream t => Maybe Rate -> t m a -> t m a
104+rate r m = fromStream $ Stream $ \st stp sng yld ->
105+ case r of
106+ Just (Rate low goal _ _) | goal < low ->
107+ error "rate: Target rate cannot be lower than minimum rate."
108+ Just (Rate _ goal high _) | goal > high ->
109+ error "rate: Target rate cannot be greater than maximum rate."
110+ Just (Rate low _ high _) | low > high ->
111+ error "rate: Minimum rate cannot be greater than maximum rate."
112+ _ -> unStream (toStream m) (setStreamRate r st) stp sng yld
113+
114+-- XXX implement for serial streams as well, as a simple delay
115+
116+{-
117+{-# RULES "rate serial" rate = yieldRateSerial #-}
118+yieldRateSerial :: Double -> SerialT m a -> SerialT m a
119+yieldRateSerial _ = id
120+-}
121+
122+-- | Same as @rate (Just $ Rate (r/2) r (2*r) maxBound)@
123+--
124+-- Specifies the average production rate of a stream in number of yields
125+-- per second (i.e. @Hertz@). Concurrent production is ramped up or down
126+-- automatically to achieve the specified average yield rate. The rate can
127+-- go down to half of the specified rate on the lower side and double of
128+-- the specified rate on the higher side.
129+--
130+-- @since 0.5.0
131+avgRate :: IsStream t => Double -> t m a -> t m a
132+avgRate r = rate (Just $ Rate (r/2) r (2*r) maxBound)
133+
134+-- | Same as @rate (Just $ Rate r r (2*r) maxBound)@
135+--
136+-- Specifies the minimum rate at which the stream should yield values. As
137+-- far as possible the yield rate would never be allowed to go below the
138+-- specified rate, even though it may possibly go above it at times, the
139+-- upper limit is double of the specified rate.
140+--
141+-- @since 0.5.0
142+minRate :: IsStream t => Double -> t m a -> t m a
143+minRate r = rate (Just $ Rate r r (2*r) maxBound)
144+
145+-- | Same as @rate (Just $ Rate (r/2) r r maxBound)@
146+--
147+-- Specifies the maximum rate at which the stream should yield values. As
148+-- far as possible the yield rate would never be allowed to go above the
149+-- specified rate, even though it may possibly go below it at times, the
150+-- lower limit is half of the specified rate. This can be useful in
151+-- applications where certain resource usage must not be allowed to go
152+-- beyond certain limits.
153+--
154+-- @since 0.5.0
155+maxRate :: IsStream t => Double -> t m a -> t m a
156+maxRate r = rate (Just $ Rate (r/2) r r maxBound)
157+
158+-- | Same as @rate (Just $ Rate r r r 0)@
159+--
160+-- Specifies a constant yield rate. If for some reason the actual rate
161+-- goes above or below the specified rate we do not try to recover it by
162+-- increasing or decreasing the rate in future. This can be useful in
163+-- applications like graphics frame refresh where we need to maintain a
164+-- constant refresh rate.
165+--
166+-- @since 0.5.0
167+constRate :: IsStream t => Double -> t m a -> t m a
168+constRate r = rate (Just $ Rate r r r 0)
169+
170+-- | Specify the average latency, in nanoseconds, of a single threaded action
171+-- in a concurrent composition. Streamly can measure the latencies, but that is
172+-- possible only after at least one task has completed. This combinator can be
173+-- used to provide a latency hint so that rate control using 'rate' can take
174+-- that into account right from the beginning. When not specified then a
175+-- default behavior is chosen which could be too slow or too fast, and would be
176+-- restricted by any other control parameters configured.
177+-- A value of 0 indicates default behavior, a negative value means there is no
178+-- limit i.e. zero latency.
179+-- This would normally be useful only in high latency and high throughput
180+-- cases.
181+--
182+{-# INLINE_NORMAL _serialLatency #-}
183+_serialLatency :: IsStream t => Int -> t m a -> t m a
184+_serialLatency n m = fromStream $ Stream $ \st stp sng yld ->
185+ unStream (toStream m) (setStreamLatency n st) stp sng yld
186+
187+{-
188+{-# RULES "serialLatency serial" _serialLatency = serialLatencySerial #-}
189+serialLatencySerial :: Int -> SerialT m a -> SerialT m a
190+serialLatencySerial _ = id
191+-}
192+
193+-- Stop concurrent dispatches after this limit. This is useful in API's like
194+-- "take" where we want to dispatch only upto the number of elements "take"
195+-- needs. This value applies only to the immediate next level and is not
196+-- inherited by everything in enclosed scope.
197+{-# INLINE_NORMAL maxYields #-}
198+maxYields :: IsStream t => Maybe Int64 -> t m a -> t m a
199+maxYields n m = fromStream $ Stream $ \st stp sng yld ->
200+ unStream (toStream m) (setYieldLimit n st) stp sng yld
201+
202+{-# RULES "maxYields serial" maxYields = maxYieldsSerial #-}
203+maxYieldsSerial :: Maybe Int64 -> SerialT m a -> SerialT m a
204+maxYieldsSerial _ = id
205+
206+printState :: MonadIO m => State Stream m a -> m ()
207+printState st = liftIO $ do
208+ let msv = streamVar st
209+ case msv of
210+ Just sv -> dumpSVar sv >>= putStrLn
211+ Nothing -> putStrLn "No SVar"
212+
213+-- | Print debug information about an SVar when the stream ends
214+inspectMode :: IsStream t => t m a -> t m a
215+inspectMode m = fromStream $ Stream $ \st stp sng yld ->
216+ unStream (toStream m) (setInspectMode st) stp sng yld
src/Streamly/Streams/Prelude.hs−9
@@ -1,12 +1,3 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ConstraintKinds #-}
3−{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE InstanceSigs #-}
6−{-# LANGUAGE MultiParamTypeClasses #-}
7−{-# LANGUAGE RankNTypes #-}
8−{-# LANGUAGE UndecidableInstances #-} -- XXX
9−
101-- |
112-- Module : Streamly.Streams.Prelude
123-- Copyright : (c) 2017 Harendra Kumar
src/Streamly/Streams/SVar.hs+1−204
@@ -1,12 +1,4 @@
1−{-# LANGUAGE CPP #-}
2−{-# LANGUAGE ConstraintKinds #-}
31{-# LANGUAGE FlexibleContexts #-}
4−{-# LANGUAGE FlexibleInstances #-}
5−{-# LANGUAGE MultiParamTypeClasses #-}
6−{-# LANGUAGE RankNTypes #-}
7−{-# LANGUAGE UndecidableInstances #-} -- XXX
8−
9−#include "inline.hs"
102
113-- |
124-- Module : Streamly.Streams.SVar
@@ -19,19 +11,8 @@
1911--
2012--
2113module Streamly.Streams.SVar
22− (
23− fromSVar
14+ ( fromSVar
2415 , toSVar
25− , maxThreads
26− , maxBuffer
27− , maxYields
28− , rate
29− , avgRate
30− , minRate
31− , maxRate
32− , constRate
33− , inspectMode
34− , printState
3516 )
3617where
3718
@@ -39,7 +20,6 @@ import Control.Exception (fromException)
3920import Control.Monad (when)
4021import Control.Monad.Catch (throwM)
4122import Control.Monad.IO.Class (MonadIO(liftIO))
42−import Data.Int (Int64)
4323import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
4424import Data.Maybe (isNothing)
4525import Data.Semigroup ((<>))
@@ -49,20 +29,12 @@ import System.Mem (performMajorGC)
4929
5030import Streamly.SVar
5131import Streamly.Streams.StreamK
52−import Streamly.Streams.Serial (SerialT)
5332
5433printSVar :: SVar t m a -> String -> IO ()
5534printSVar sv how = do
5635 svInfo <- dumpSVar sv
5736 hPutStrLn stderr $ "\n" <> how <> "\n" <> svInfo
5837
59−printState :: MonadIO m => State Stream m a -> m ()
60−printState st = liftIO $ do
61− let msv = streamVar st
62− case msv of
63− Just sv -> dumpSVar sv >>= putStrLn
64− Nothing -> putStrLn "No SVar"
65−
6638-- | Pull a stream from an SVar.
6739{-# NOINLINE fromStreamVar #-}
6840fromStreamVar :: MonadAsync m => SVar Stream m a -> Stream m a
@@ -129,178 +101,3 @@ fromSVar sv =
129101-- be read back from the SVar using 'fromSVar'.
130102toSVar :: (IsStream t, MonadAsync m) => SVar Stream m a -> t m a -> m ()
131103toSVar sv m = toStreamVar sv (toStream m)
132−
133−-------------------------------------------------------------------------------
134−-- Concurrency control
135−-------------------------------------------------------------------------------
136−--
137−-- XXX need to write these in direct style otherwise they will break fusion.
138−--
139−-- | Specify the maximum number of threads that can be spawned concurrently for
140−-- any concurrent combinator in a stream.
141−-- A value of 0 resets the thread limit to default, a negative value means
142−-- there is no limit. The default value is 1500.
143−--
144−-- When the actions in a stream are IO bound, having blocking IO calls, this
145−-- option can be used to control the maximum number of in-flight IO requests.
146−-- When the actions are CPU bound this option can be used to
147−-- control the amount of CPU used by the stream.
148−--
149−-- @since 0.4.0
150−{-# INLINE_NORMAL maxThreads #-}
151−maxThreads :: IsStream t => Int -> t m a -> t m a
152−maxThreads n m = fromStream $ Stream $ \st stp sng yld ->
153− unStream (toStream m) (setMaxThreads n st) stp sng yld
154−
155−{-
156−{-# RULES "maxThreadsSerial serial" maxThreads = maxThreadsSerial #-}
157−maxThreadsSerial :: Int -> SerialT m a -> SerialT m a
158−maxThreadsSerial _ = id
159−-}
160−
161−-- | Specify the maximum size of the buffer for storing the results from
162−-- concurrent computations. If the buffer becomes full we stop spawning more
163−-- concurrent tasks until there is space in the buffer.
164−-- A value of 0 resets the buffer size to default, a negative value means
165−-- there is no limit. The default value is 1500.
166−--
167−-- CAUTION! using an unbounded 'maxBuffer' value (i.e. a negative value)
168−-- coupled with an unbounded 'maxThreads' value is a recipe for disaster in
169−-- presence of infinite streams, or very large streams. Especially, it must
170−-- not be used when 'pure' is used in 'ZipAsyncM' streams as 'pure' in
171−-- applicative zip streams generates an infinite stream causing unbounded
172−-- concurrent generation with no limit on the buffer or threads.
173−--
174−-- @since 0.4.0
175−{-# INLINE_NORMAL maxBuffer #-}
176−maxBuffer :: IsStream t => Int -> t m a -> t m a
177−maxBuffer n m = fromStream $ Stream $ \st stp sng yld ->
178− unStream (toStream m) (setMaxBuffer n st) stp sng yld
179−
180−{-
181−{-# RULES "maxBuffer serial" maxBuffer = maxBufferSerial #-}
182−maxBufferSerial :: Int -> SerialT m a -> SerialT m a
183−maxBufferSerial _ = id
184−-}
185−
186−-- | Specify the pull rate of a stream.
187−-- A 'Nothing' value resets the rate to default which is unlimited. When the
188−-- rate is specified, concurrent production may be ramped up or down
189−-- automatically to achieve the specified yield rate. The specific behavior for
190−-- different styles of 'Rate' specifications is documented under 'Rate'. The
191−-- effective maximum production rate achieved by a stream is governed by:
192−--
193−-- * The 'maxThreads' limit
194−-- * The 'maxBuffer' limit
195−-- * The maximum rate that the stream producer can achieve
196−-- * The maximum rate that the stream consumer can achieve
197−--
198−-- @since 0.5.0
199−{-# INLINE_NORMAL rate #-}
200−rate :: IsStream t => Maybe Rate -> t m a -> t m a
201−rate r m = fromStream $ Stream $ \st stp sng yld ->
202− case r of
203− Just (Rate low goal _ _) | goal < low ->
204− error "rate: Target rate cannot be lower than minimum rate."
205− Just (Rate _ goal high _) | goal > high ->
206− error "rate: Target rate cannot be greater than maximum rate."
207− Just (Rate low _ high _) | low > high ->
208− error "rate: Minimum rate cannot be greater than maximum rate."
209− _ -> unStream (toStream m) (setStreamRate r st) stp sng yld
210−
211−-- XXX implement for serial streams as well, as a simple delay
212−
213−{-
214−{-# RULES "rate serial" rate = yieldRateSerial #-}
215−yieldRateSerial :: Double -> SerialT m a -> SerialT m a
216−yieldRateSerial _ = id
217−-}
218−
219−-- | Same as @rate (Just $ Rate (r/2) r (2*r) maxBound)@
220−--
221−-- Specifies the average production rate of a stream in number of yields
222−-- per second (i.e. @Hertz@). Concurrent production is ramped up or down
223−-- automatically to achieve the specified average yield rate. The rate can
224−-- go down to half of the specified rate on the lower side and double of
225−-- the specified rate on the higher side.
226−--
227−-- @since 0.5.0
228−avgRate :: IsStream t => Double -> t m a -> t m a
229−avgRate r = rate (Just $ Rate (r/2) r (2*r) maxBound)
230−
231−-- | Same as @rate (Just $ Rate r r (2*r) maxBound)@
232−--
233−-- Specifies the minimum rate at which the stream should yield values. As
234−-- far as possible the yield rate would never be allowed to go below the
235−-- specified rate, even though it may possibly go above it at times, the
236−-- upper limit is double of the specified rate.
237−--
238−-- @since 0.5.0
239−minRate :: IsStream t => Double -> t m a -> t m a
240−minRate r = rate (Just $ Rate r r (2*r) maxBound)
241−
242−-- | Same as @rate (Just $ Rate (r/2) r r maxBound)@
243−--
244−-- Specifies the maximum rate at which the stream should yield values. As
245−-- far as possible the yield rate would never be allowed to go above the
246−-- specified rate, even though it may possibly go below it at times, the
247−-- lower limit is half of the specified rate. This can be useful in
248−-- applications where certain resource usage must not be allowed to go
249−-- beyond certain limits.
250−--
251−-- @since 0.5.0
252−maxRate :: IsStream t => Double -> t m a -> t m a
253−maxRate r = rate (Just $ Rate (r/2) r r maxBound)
254−
255−-- | Same as @rate (Just $ Rate r r r 0)@
256−--
257−-- Specifies a constant yield rate. If for some reason the actual rate
258−-- goes above or below the specified rate we do not try to recover it by
259−-- increasing or decreasing the rate in future. This can be useful in
260−-- applications like graphics frame refresh where we need to maintain a
261−-- constant refresh rate.
262−--
263−-- @since 0.5.0
264−constRate :: IsStream t => Double -> t m a -> t m a
265−constRate r = rate (Just $ Rate r r r 0)
266−
267−-- | Specify the average latency, in nanoseconds, of a single threaded action
268−-- in a concurrent composition. Streamly can measure the latencies, but that is
269−-- possible only after at least one task has completed. This combinator can be
270−-- used to provide a latency hint so that rate control using 'rate' can take
271−-- that into account right from the beginning. When not specified then a
272−-- default behavior is chosen which could be too slow or too fast, and would be
273−-- restricted by any other control parameters configured.
274−-- A value of 0 indicates default behavior, a negative value means there is no
275−-- limit i.e. zero latency.
276−-- This would normally be useful only in high latency and high throughput
277−-- cases.
278−--
279−{-# INLINE_NORMAL _serialLatency #-}
280−_serialLatency :: IsStream t => Int -> t m a -> t m a
281−_serialLatency n m = fromStream $ Stream $ \st stp sng yld ->
282− unStream (toStream m) (setStreamLatency n st) stp sng yld
283−
284−{-
285−{-# RULES "serialLatency serial" _serialLatency = serialLatencySerial #-}
286−serialLatencySerial :: Int -> SerialT m a -> SerialT m a
287−serialLatencySerial _ = id
288−-}
289−
290−-- Stop concurrent dispatches after this limit. This is useful in API's like
291−-- "take" where we want to dispatch only upto the number of elements "take"
292−-- needs. This value applies only to the immediate next level and is not
293−-- inherited by everything in enclosed scope.
294−{-# INLINE_NORMAL maxYields #-}
295−maxYields :: IsStream t => Maybe Int64 -> t m a -> t m a
296−maxYields n m = fromStream $ Stream $ \st stp sng yld ->
297− unStream (toStream m) (setYieldLimit n st) stp sng yld
298−
299−{-# RULES "maxYields serial" maxYields = maxYieldsSerial #-}
300−maxYieldsSerial :: Maybe Int64 -> SerialT m a -> SerialT m a
301−maxYieldsSerial _ = id
302−
303−-- | Print debug information about an SVar when the stream ends
304−inspectMode :: IsStream t => t m a -> t m a
305−inspectMode m = fromStream $ Stream $ \st stp sng yld ->
306− unStream (toStream m) (setInspectMode st) stp sng yld
streamly.cabal+7−2
@@ -126,15 +126,20 @@ flag examples-sdl
126126library
127127 hs-source-dirs: src
128128 other-modules: Streamly.SVar
129+
130+ -- Base streams
129131 , Streamly.Streams.StreamK
130132 , Streamly.Streams.StreamD
131− , Streamly.Streams.Serial
133+ , Streamly.Streams.Prelude
134+
135+ -- Higher level streams
132136 , Streamly.Streams.SVar
137+ , Streamly.Streams.Serial
133138 , Streamly.Streams.Async
134139 , Streamly.Streams.Parallel
135140 , Streamly.Streams.Ahead
136141 , Streamly.Streams.Zip
137− , Streamly.Streams.Prelude
142+ , Streamly.Streams.Combinators
138143
139144 exposed-modules: Streamly.Prelude
140145 , Streamly.Time