on 8a37cd8 help sign in
489 lines 15 kB history raw
1{-# LANGUAGE UndecidableInstances #-}
3-- |
4-- Module : Streamly.Internal.Data.Stream.Serial
5-- Copyright : (c) 2017 Harendra Kumar
6--
7-- License : BSD3
8-- Maintainer : streamly@composewell.com
9-- Stability : experimental
10-- Portability : GHC
11--
12--
13module Streamly.Internal.Data.Stream.Serial
14 (
15 -- * Serial appending stream
16 SerialT
17 , Serial
18 , K.serial
19 , serially
21 -- * Serial interleaving stream
22 , WSerialT
23 , WSerial
24 , wSerial
25 , wSerialFst
26 , wSerialMin
27 , wSerially
29 -- * Construction
30 , unfoldrM
32 -- * Transformation
33 , map
34 , mapM
36 -- * Deprecated
37 , StreamT
38 , InterleavedT
39 , (<=>)
40 , interleaving
41 )
42where
44import Control.Applicative (liftA2)
45import Control.DeepSeq (NFData(..))
46#if MIN_VERSION_deepseq(1,4,3)
47import Control.DeepSeq (NFData1(..))
48#endif
49import Control.Monad.Base (MonadBase(..), liftBaseDefault)
50import Control.Monad.Catch (MonadThrow, throwM)
51-- import Control.Monad.Error.Class (MonadError(..))
52import Control.Monad.IO.Class (MonadIO(..))
53import Control.Monad.Reader.Class (MonadReader(..))
54import Control.Monad.State.Class (MonadState(..))
55import Control.Monad.Trans.Class (MonadTrans(lift))
56import Data.Foldable (Foldable(foldl'), fold)
57import Data.Functor.Identity (Identity(..), runIdentity)
58import Data.Maybe (fromMaybe)
59import Data.Semigroup (Endo(..))
60#if __GLASGOW_HASKELL__ < 808
61import Data.Semigroup (Semigroup(..))
62#endif
63import GHC.Exts (IsList(..), IsString(..))
64import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
65 readListPrecDefault)
66import Prelude hiding (map, mapM, errorWithoutStackTrace)
68import Streamly.Internal.BaseCompat ((#.), errorWithoutStackTrace)
69import Streamly.Internal.Data.Stream.StreamK (IsStream(..), adapt, Stream, mkStream,
70 foldStream)
71import Streamly.Internal.Data.Strict (Maybe'(..), toMaybe)
72import qualified Streamly.Internal.Data.Stream.Prelude as P
73import qualified Streamly.Internal.Data.Stream.StreamK as K
74import qualified Streamly.Internal.Data.Stream.StreamD as D
76#include "Instances.hs"
77#include "inline.hs"
79------------------------------------------------------------------------------
80-- SerialT
81------------------------------------------------------------------------------
83-- | The 'Semigroup' operation for 'SerialT' behaves like a regular append
84-- operation. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
85-- first until it exhausts and then stream @b@ is evaluated. In other words,
86-- the elements of stream @b@ are appended to the elements of stream @a@. This
87-- operation can be used to fold an infinite lazy container of streams.
88--
89-- @
90-- import Streamly
91-- import qualified "Streamly.Prelude" as S
92--
93-- main = (S.toList . 'serially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
94-- @
95-- @
96-- [1,2,3,4]
97-- @
98--
99-- The 'Monad' instance runs the /monadic continuation/ for each
100-- element of the stream, serially.
101--
102-- @
103-- main = S.drain . 'serially' $ do
104-- x <- return 1 \<\> return 2
105-- S.yieldM $ print x
106-- @
107-- @
108-- 1
109-- 2
110-- @
111--
112-- 'SerialT' nests streams serially in a depth first manner.
113--
114-- @
115-- main = S.drain . 'serially' $ do
116-- x <- return 1 \<\> return 2
117-- y <- return 3 \<\> return 4
118-- S.yieldM $ print (x, y)
119-- @
120-- @
121-- (1,3)
122-- (1,4)
123-- (2,3)
124-- (2,4)
125-- @
126--
127-- We call the monadic code being run for each element of the stream a monadic
128-- continuation. In imperative paradigm we can think of this composition as
129-- nested @for@ loops and the monadic continuation is the body of the loop. The
130-- loop iterates for all elements of the stream.
131--
132-- Note that the behavior and semantics of 'SerialT', including 'Semigroup'
133-- and 'Monad' instances are exactly like Haskell lists except that 'SerialT'
134-- can contain effectful actions while lists are pure.
135--
136-- In the code above, the 'serially' combinator can be omitted as the default
137-- stream type is 'SerialT'.
138--
139-- @since 0.2.0
140newtype SerialT m a = SerialT {getSerialT :: Stream m a}
141 -- XXX when deriving do we inherit an INLINE?
142 deriving (Semigroup, Monoid, MonadTrans)
144-- | A serial IO stream of elements of type @a@. See 'SerialT' documentation
145-- for more details.
146--
147-- @since 0.2.0
148type Serial = SerialT IO
150-- |
151-- @since 0.1.0
152{-# DEPRECATED StreamT "Please use 'SerialT' instead." #-}
153type StreamT = SerialT
155-- | Fix the type of a polymorphic stream as 'SerialT'.
156--
157-- @since 0.1.0
158serially :: IsStream t => SerialT m a -> t m a
159serially = adapt
161{-# INLINE consMSerial #-}
162{-# SPECIALIZE consMSerial :: IO a -> SerialT IO a -> SerialT IO a #-}
163consMSerial :: Monad m => m a -> SerialT m a -> SerialT m a
164consMSerial m ms = fromStream $ K.consMStream m (toStream ms)
166instance IsStream SerialT where
167 toStream = getSerialT
168 fromStream = SerialT
169 consM = consMSerial
170 (|:) = consMSerial
172------------------------------------------------------------------------------
173-- Monad
174------------------------------------------------------------------------------
176instance Monad m => Monad (SerialT m) where
177 return = pure
179 {-# INLINE (>>=) #-}
180 (>>=) = K.bindWith K.serial
182 {-# INLINE (>>) #-}
183 (>>) = (*>)
185 -- StreamD based implementation
186 -- return = SerialT . D.fromStreamD . D.yield
187 -- m >>= f = D.fromStreamD $ D.concatMap (\a -> D.toStreamD (f a)) (D.toStreamD m)
189------------------------------------------------------------------------------
190-- Other instances
191------------------------------------------------------------------------------
193{-# INLINE mapM #-}
194mapM :: (IsStream t, Monad m) => (a -> m b) -> t m a -> t m b
195mapM f m = D.fromStreamD $ D.mapM f $ D.toStreamD m
197-- |
198-- @
199-- map = fmap
200-- @
201--
202-- Same as 'fmap'.
203--
204-- @
205-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
206-- [2,3,4]
207-- @
208--
209-- @since 0.4.0
210{-# INLINE map #-}
211map :: (IsStream t, Monad m) => (a -> b) -> t m a -> t m b
212map f = mapM (return . f)
214{-# INLINE apSerial #-}
215apSerial :: Monad m => SerialT m (a -> b) -> SerialT m a -> SerialT m b
216apSerial (SerialT m1) (SerialT m2) =
217 D.fromStreamD $ D.toStreamD m1 <*> D.toStreamD m2
219{-# INLINE apSequence #-}
220apSequence :: Monad m => SerialT m a -> SerialT m b -> SerialT m b
221apSequence (SerialT m1) (SerialT m2) =
222 D.fromStreamD $ D.toStreamD m1 *> D.toStreamD m2
224{-# INLINE apDiscardSnd #-}
225apDiscardSnd :: Monad m => SerialT m a -> SerialT m b -> SerialT m a
226apDiscardSnd (SerialT m1) (SerialT m2) =
227 D.fromStreamD $ D.toStreamD m1 <* D.toStreamD m2
229-- Note: we need to define all the typeclass operations because we want to
230-- INLINE them.
231instance Monad m => Applicative (SerialT m) where
232 {-# INLINE pure #-}
233 pure = SerialT . K.yield
235 {-# INLINE (<*>) #-}
236 (<*>) = apSerial
237 -- (<*>) = K.apSerial
239#if MIN_VERSION_base(4,10,0)
240 {-# INLINE liftA2 #-}
241 liftA2 f x = (<*>) (fmap f x)
242#endif
244 {-# INLINE (*>) #-}
245 (*>) = apSequence
246 -- (*>) = K.apSerialDiscardFst
248 {-# INLINE (<*) #-}
249 (<*) = apDiscardSnd
250 -- (<*) = K.apSerialDiscardSnd
252MONAD_COMMON_INSTANCES(SerialT,)
253LIST_INSTANCES(SerialT)
254NFDATA1_INSTANCE(SerialT)
255FOLDABLE_INSTANCE(SerialT)
256TRAVERSABLE_INSTANCE(SerialT)
258------------------------------------------------------------------------------
259-- WSerialT
260------------------------------------------------------------------------------
262-- | The 'Semigroup' operation for 'WSerialT' interleaves the elements from the
263-- two streams. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
264-- first to produce the first element of the combined stream and then stream
265-- @b@ is evaluated to produce the next element of the combined stream, and
266-- then we go back to evaluating stream @a@ and so on. In other words, the
267-- elements of stream @a@ are interleaved with the elements of stream @b@.
268--
269-- Note that evaluation of @a <> b <> c@ does not schedule @a@, @b@ and @c@
270-- with equal priority. This expression is equivalent to @a <> (b <> c)@,
271-- therefore, it fairly interleaves @a@ with the result of @b <> c@. For
272-- example, @S.fromList [1,2] <> S.fromList [3,4] <> S.fromList [5,6] ::
273-- WSerialT Identity Int@ would result in [1,3,2,5,4,6]. In other words, the
274-- leftmost stream gets the same scheduling priority as the rest of the
275-- streams taken together. The same is true for each subexpression on the right.
276--
277-- Note that this operation cannot be used to fold a container of infinite
278-- streams as the state that it needs to maintain is proportional to the number
279-- of streams.
280--
281-- The @W@ in the name stands for @wide@ or breadth wise scheduling in
282-- contrast to the depth wise scheduling behavior of 'SerialT'.
283--
284-- @
285-- import Streamly
286-- import qualified "Streamly.Prelude" as S
287--
288-- main = (S.toList . 'wSerially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
289-- @
290-- @
291-- [1,3,2,4]
292-- @
293--
294-- Similarly, the 'Monad' instance interleaves the iterations of the
295-- inner and the outer loop, nesting loops in a breadth first manner.
296--
297--
298-- @
299-- main = S.drain . 'wSerially' $ do
300-- x <- return 1 \<\> return 2
301-- y <- return 3 \<\> return 4
302-- S.yieldM $ print (x, y)
303-- @
304-- @
305-- (1,3)
306-- (2,3)
307-- (1,4)
308-- (2,4)
309-- @
310--
311-- @since 0.2.0
312newtype WSerialT m a = WSerialT {getWSerialT :: Stream m a}
313 deriving (MonadTrans)
315-- | An interleaving serial IO stream of elements of type @a@. See 'WSerialT'
316-- documentation for more details.
317--
318-- @since 0.2.0
319type WSerial = WSerialT IO
321-- |
322-- @since 0.1.0
323{-# DEPRECATED InterleavedT "Please use 'WSerialT' instead." #-}
324type InterleavedT = WSerialT
326-- | Fix the type of a polymorphic stream as 'WSerialT'.
327--
328-- @since 0.2.0
329wSerially :: IsStream t => WSerialT m a -> t m a
330wSerially = adapt
332-- | Same as 'wSerially'.
333--
334-- @since 0.1.0
335{-# DEPRECATED interleaving "Please use wSerially instead." #-}
336interleaving :: IsStream t => WSerialT m a -> t m a
337interleaving = wSerially
339consMWSerial :: Monad m => m a -> WSerialT m a -> WSerialT m a
340consMWSerial m ms = fromStream $ K.consMStream m (toStream ms)
342instance IsStream WSerialT where
343 toStream = getWSerialT
344 fromStream = WSerialT
346 {-# INLINE consM #-}
347 {-# SPECIALIZE consM :: IO a -> WSerialT IO a -> WSerialT IO a #-}
348 consM :: Monad m => m a -> WSerialT m a -> WSerialT m a
349 consM = consMWSerial
351 {-# INLINE (|:) #-}
352 {-# SPECIALIZE (|:) :: IO a -> WSerialT IO a -> WSerialT IO a #-}
353 (|:) :: Monad m => m a -> WSerialT m a -> WSerialT m a
354 (|:) = consMWSerial
356------------------------------------------------------------------------------
357-- Semigroup
358------------------------------------------------------------------------------
360-- Additionally we can have m elements yield from the first stream and n
361-- elements yielding from the second stream. We can also have time slicing
362-- variants of positional interleaving, e.g. run first stream for m seconds and
363-- run the second stream for n seconds.
364--
365-- Similar combinators can be implemented using WAhead style.
367-- | Polymorphic version of the 'Semigroup' operation '<>' of 'WSerialT'.
368-- Interleaves two streams, yielding one element from each stream alternately.
369-- When one stream stops the rest of the other stream is used in the output
370-- stream.
371--
372-- @since 0.2.0
373{-# INLINE wSerial #-}
374wSerial :: IsStream t => t m a -> t m a -> t m a
375wSerial m1 m2 = mkStream $ \st yld sng stp -> do
376 let stop = foldStream st yld sng stp m2
377 single a = yld a m2
378 yieldk a r = yld a (wSerial m2 r)
379 foldStream st yieldk single stop m1
381-- | Like `wSerial` but stops interleaving as soon as the first stream stops.
382--
383-- @since 0.7.0
384{-# INLINE wSerialFst #-}
385wSerialFst :: IsStream t => t m a -> t m a -> t m a
386wSerialFst m1 m2 = mkStream $ \st yld sng stp -> do
387 let yieldFirst a r = yld a (yieldSecond r m2)
388 in foldStream st yieldFirst sng stp m1
390 where
392 yieldSecond s1 s2 = mkStream $ \st yld sng stp -> do
393 let stop = foldStream st yld sng stp s1
394 single a = yld a s1
395 yieldk a r = yld a (wSerial s1 r)
396 in foldStream st yieldk single stop s2
398-- | Like `wSerial` but stops interleaving as soon as any of the two streams
399-- stops.
400--
401-- @since 0.7.0
402{-# INLINE wSerialMin #-}
403wSerialMin :: IsStream t => t m a -> t m a -> t m a
404wSerialMin m1 m2 = mkStream $ \st yld sng stp -> do
405 let stop = stp
406 single a = sng a
407 yieldk a r = yld a (wSerial m2 r)
408 foldStream st yieldk single stop m1
410instance Semigroup (WSerialT m a) where
411 (<>) = wSerial
413infixr 5 <=>
415-- | Same as 'wSerial'.
416--
417-- @since 0.1.0
418{-# DEPRECATED (<=>) "Please use 'wSerial' instead." #-}
419{-# INLINE (<=>) #-}
420(<=>) :: IsStream t => t m a -> t m a -> t m a
421(<=>) = wSerial
423------------------------------------------------------------------------------
424-- Monoid
425------------------------------------------------------------------------------
427instance Monoid (WSerialT m a) where
428 mempty = K.nil
429 mappend = (<>)
431{-# INLINE apWSerial #-}
432apWSerial :: Monad m => WSerialT m (a -> b) -> WSerialT m a -> WSerialT m b
433apWSerial (WSerialT m1) (WSerialT m2) =
434 let f x1 = K.concatMapBy wSerial (pure . x1) m2
435 in WSerialT $ K.concatMapBy wSerial f m1
437instance Monad m => Applicative (WSerialT m) where
438 {-# INLINE pure #-}
439 pure = WSerialT . K.yield
440 {-# INLINE (<*>) #-}
441 (<*>) = apWSerial
443------------------------------------------------------------------------------
444-- Monad
445------------------------------------------------------------------------------
447instance Monad m => Monad (WSerialT m) where
448 return = pure
449 {-# INLINE (>>=) #-}
450 (>>=) = K.bindWith wSerial
452------------------------------------------------------------------------------
453-- Other instances
454------------------------------------------------------------------------------
456MONAD_COMMON_INSTANCES(WSerialT,)
457LIST_INSTANCES(WSerialT)
458NFDATA1_INSTANCE(WSerialT)
459FOLDABLE_INSTANCE(WSerialT)
460TRAVERSABLE_INSTANCE(WSerialT)
462------------------------------------------------------------------------------
463-- Construction
464------------------------------------------------------------------------------
466-- | Build a stream by unfolding a /monadic/ step function starting from a
467-- seed. The step function returns the next element in the stream and the next
468-- seed value. When it is done it returns 'Nothing' and the stream ends. For
469-- example,
470--
471-- @
472-- let f b =
473-- if b > 3
474-- then return Nothing
475-- else print b >> return (Just (b, b + 1))
476-- in drain $ unfoldrM f 0
477-- @
478-- @
479-- 0
480-- 1
481-- 2
482-- 3
483-- @
484--
485-- /Internal/
486--
487{-# INLINE unfoldrM #-}
488unfoldrM :: (IsStream t, Monad m) => (b -> m (Maybe (a, b))) -> b -> t m a
489unfoldrM step seed = D.fromStreamD (D.unfoldrM step seed)