on c004bd7 help sign in
506 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)
51import Control.Monad.IO.Class (MonadIO(..))
52import Control.Monad.Reader.Class (MonadReader(..))
53import Control.Monad.State.Class (MonadState(..))
54import Control.Monad.Trans.Class (MonadTrans(lift))
55import Data.Foldable (Foldable(foldl'), fold)
56import Data.Functor.Identity (Identity(..), runIdentity)
57import Data.Maybe (fromMaybe)
58import Data.Semigroup (Endo(..))
59#if __GLASGOW_HASKELL__ < 808
60import Data.Semigroup (Semigroup(..))
61#endif
62import GHC.Exts (IsList(..), IsString(..))
63import Text.Read
64 ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec
65 , readListPrecDefault)
66import Streamly.Internal.BaseCompat ((#.), errorWithoutStackTrace)
67import Streamly.Internal.Data.Stream.StreamK
68 (IsStream(..), adapt, Stream, mkStream, foldStream)
69import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
71import qualified Streamly.Internal.Data.Stream.Prelude as P
72import qualified Streamly.Internal.Data.Stream.StreamK as K
73import qualified Streamly.Internal.Data.Stream.StreamD as D
75import Prelude hiding (map, mapM, errorWithoutStackTrace)
77#include "Instances.hs"
78#include "inline.hs"
80------------------------------------------------------------------------------
81-- SerialT
82------------------------------------------------------------------------------
84-- | The 'Semigroup' operation for 'SerialT' behaves like a regular append
85-- operation. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
86-- first until it exhausts and then stream @b@ is evaluated. In other words,
87-- the elements of stream @b@ are appended to the elements of stream @a@. This
88-- operation can be used to fold an infinite lazy container of streams.
89--
90-- @
91-- import Streamly
92-- import qualified "Streamly.Prelude" as S
93--
94-- main = (S.toList . 'serially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
95-- @
96-- @
97-- [1,2,3,4]
98-- @
99--
100-- The 'Monad' instance runs the /monadic continuation/ for each
101-- element of the stream, serially.
102--
103-- @
104-- main = S.drain . 'serially' $ do
105-- x <- return 1 \<\> return 2
106-- S.yieldM $ print x
107-- @
108-- @
109-- 1
110-- 2
111-- @
112--
113-- 'SerialT' nests streams serially in a depth first manner.
114--
115-- @
116-- main = S.drain . 'serially' $ do
117-- x <- return 1 \<\> return 2
118-- y <- return 3 \<\> return 4
119-- S.yieldM $ print (x, y)
120-- @
121-- @
122-- (1,3)
123-- (1,4)
124-- (2,3)
125-- (2,4)
126-- @
127--
128-- We call the monadic code being run for each element of the stream a monadic
129-- continuation. In imperative paradigm we can think of this composition as
130-- nested @for@ loops and the monadic continuation is the body of the loop. The
131-- loop iterates for all elements of the stream.
132--
133-- Note that the behavior and semantics of 'SerialT', including 'Semigroup'
134-- and 'Monad' instances are exactly like Haskell lists except that 'SerialT'
135-- can contain effectful actions while lists are pure.
136--
137-- In the code above, the 'serially' combinator can be omitted as the default
138-- stream type is 'SerialT'.
139--
140-- /Since: 0.2.0 ("Streamly")/
141--
142-- @since 0.8.0
143newtype SerialT m a = SerialT {getSerialT :: Stream m a}
144 -- XXX when deriving do we inherit an INLINE?
145 deriving (Semigroup, Monoid, MonadTrans)
147-- | A serial IO stream of elements of type @a@. See 'SerialT' documentation
148-- for more details.
149--
150-- /Since: 0.2.0 ("Streamly")/
151--
152-- @since 0.8.0
153type Serial = SerialT IO
155-- |
156-- @since 0.1.0
157{-# DEPRECATED StreamT "Please use 'SerialT' instead." #-}
158type StreamT = SerialT
160-- | Fix the type of a polymorphic stream as 'SerialT'.
161--
162-- /Since: 0.1.0 ("Streamly")/
163--
164-- @since 0.8.0
165serially :: IsStream t => SerialT m a -> t m a
166serially = adapt
168{-# INLINE consMSerial #-}
169{-# SPECIALIZE consMSerial :: IO a -> SerialT IO a -> SerialT IO a #-}
170consMSerial :: Monad m => m a -> SerialT m a -> SerialT m a
171consMSerial m ms = fromStream $ K.consMStream m (toStream ms)
173instance IsStream SerialT where
174 toStream = getSerialT
175 fromStream = SerialT
176 consM = consMSerial
177 (|:) = consMSerial
179------------------------------------------------------------------------------
180-- Monad
181------------------------------------------------------------------------------
183instance Monad m => Monad (SerialT m) where
184 return = pure
186 {-# INLINE (>>=) #-}
187 (>>=) = K.bindWith K.serial
189 {-# INLINE (>>) #-}
190 (>>) = (*>)
192 -- StreamD based implementation
193 -- return = SerialT . D.fromStreamD . D.yield
194 -- m >>= f = D.fromStreamD $ D.concatMap (\a -> D.toStreamD (f a)) (D.toStreamD m)
196------------------------------------------------------------------------------
197-- Other instances
198------------------------------------------------------------------------------
200{-# INLINE mapM #-}
201mapM :: (IsStream t, Monad m) => (a -> m b) -> t m a -> t m b
202mapM f m = D.fromStreamD $ D.mapM f $ D.toStreamD m
204-- |
205-- @
206-- map = fmap
207-- @
208--
209-- Same as 'fmap'.
210--
211-- @
212-- > S.toList $ S.map (+1) $ S.fromList [1,2,3]
213-- [2,3,4]
214-- @
215--
216-- @since 0.4.0
217{-# INLINE map #-}
218map :: (IsStream t, Monad m) => (a -> b) -> t m a -> t m b
219map f = mapM (return . f)
221{-# INLINE apSerial #-}
222apSerial :: Monad m => SerialT m (a -> b) -> SerialT m a -> SerialT m b
223apSerial (SerialT m1) (SerialT m2) =
224 D.fromStreamD $ D.toStreamD m1 <*> D.toStreamD m2
226{-# INLINE apSequence #-}
227apSequence :: Monad m => SerialT m a -> SerialT m b -> SerialT m b
228apSequence (SerialT m1) (SerialT m2) =
229 D.fromStreamD $ D.toStreamD m1 *> D.toStreamD m2
231{-# INLINE apDiscardSnd #-}
232apDiscardSnd :: Monad m => SerialT m a -> SerialT m b -> SerialT m a
233apDiscardSnd (SerialT m1) (SerialT m2) =
234 D.fromStreamD $ D.toStreamD m1 <* D.toStreamD m2
236-- Note: we need to define all the typeclass operations because we want to
237-- INLINE them.
238instance Monad m => Applicative (SerialT m) where
239 {-# INLINE pure #-}
240 pure = SerialT . K.yield
242 {-# INLINE (<*>) #-}
243 (<*>) = apSerial
244 -- (<*>) = K.apSerial
246#if MIN_VERSION_base(4,10,0)
247 {-# INLINE liftA2 #-}
248 liftA2 f x = (<*>) (fmap f x)
249#endif
251 {-# INLINE (*>) #-}
252 (*>) = apSequence
253 -- (*>) = K.apSerialDiscardFst
255 {-# INLINE (<*) #-}
256 (<*) = apDiscardSnd
257 -- (<*) = K.apSerialDiscardSnd
259MONAD_COMMON_INSTANCES(SerialT,)
260LIST_INSTANCES(SerialT)
261NFDATA1_INSTANCE(SerialT)
262FOLDABLE_INSTANCE(SerialT)
263TRAVERSABLE_INSTANCE(SerialT)
265------------------------------------------------------------------------------
266-- WSerialT
267------------------------------------------------------------------------------
269-- | The 'Semigroup' operation for 'WSerialT' interleaves the elements from the
270-- two streams. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
271-- first to produce the first element of the combined stream and then stream
272-- @b@ is evaluated to produce the next element of the combined stream, and
273-- then we go back to evaluating stream @a@ and so on. In other words, the
274-- elements of stream @a@ are interleaved with the elements of stream @b@.
275--
276-- Note that evaluation of @a <> b <> c@ does not schedule @a@, @b@ and @c@
277-- with equal priority. This expression is equivalent to @a <> (b <> c)@,
278-- therefore, it fairly interleaves @a@ with the result of @b <> c@. For
279-- example, @S.fromList [1,2] <> S.fromList [3,4] <> S.fromList [5,6] ::
280-- WSerialT Identity Int@ would result in [1,3,2,5,4,6]. In other words, the
281-- leftmost stream gets the same scheduling priority as the rest of the
282-- streams taken together. The same is true for each subexpression on the right.
283--
284-- Note that this operation cannot be used to fold a container of infinite
285-- streams as the state that it needs to maintain is proportional to the number
286-- of streams.
287--
288-- The @W@ in the name stands for @wide@ or breadth wise scheduling in
289-- contrast to the depth wise scheduling behavior of 'SerialT'.
290--
291-- @
292-- import Streamly
293-- import qualified "Streamly.Prelude" as S
294--
295-- main = (S.toList . 'wSerially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
296-- @
297-- @
298-- [1,3,2,4]
299-- @
300--
301-- Similarly, the 'Monad' instance interleaves the iterations of the
302-- inner and the outer loop, nesting loops in a breadth first manner.
303--
304--
305-- @
306-- main = S.drain . 'wSerially' $ do
307-- x <- return 1 \<\> return 2
308-- y <- return 3 \<\> return 4
309-- S.yieldM $ print (x, y)
310-- @
311-- @
312-- (1,3)
313-- (2,3)
314-- (1,4)
315-- (2,4)
316-- @
317--
318-- /Since: 0.2.0 ("Streamly")/
319--
320-- @since 0.8.0
321newtype WSerialT m a = WSerialT {getWSerialT :: Stream m a}
322 deriving (MonadTrans)
324-- | An interleaving serial IO stream of elements of type @a@. See 'WSerialT'
325-- documentation for more details.
326--
327-- /Since: 0.2.0 ("Streamly")/
328--
329-- @since 0.8.0
330type WSerial = WSerialT IO
332-- |
333-- @since 0.1.0
334{-# DEPRECATED InterleavedT "Please use 'WSerialT' instead." #-}
335type InterleavedT = WSerialT
337-- | Fix the type of a polymorphic stream as 'WSerialT'.
338--
339-- /Since: 0.2.0 ("Streamly")/
340--
341-- @since 0.8.0
342wSerially :: IsStream t => WSerialT m a -> t m a
343wSerially = adapt
345-- | Same as 'wSerially'.
346--
347-- @since 0.1.0
348{-# DEPRECATED interleaving "Please use wSerially instead." #-}
349interleaving :: IsStream t => WSerialT m a -> t m a
350interleaving = wSerially
352consMWSerial :: Monad m => m a -> WSerialT m a -> WSerialT m a
353consMWSerial m ms = fromStream $ K.consMStream m (toStream ms)
355instance IsStream WSerialT where
356 toStream = getWSerialT
357 fromStream = WSerialT
359 {-# INLINE consM #-}
360 {-# SPECIALIZE consM :: IO a -> WSerialT IO a -> WSerialT IO a #-}
361 consM :: Monad m => m a -> WSerialT m a -> WSerialT m a
362 consM = consMWSerial
364 {-# INLINE (|:) #-}
365 {-# SPECIALIZE (|:) :: IO a -> WSerialT IO a -> WSerialT IO a #-}
366 (|:) :: Monad m => m a -> WSerialT m a -> WSerialT m a
367 (|:) = consMWSerial
369------------------------------------------------------------------------------
370-- Semigroup
371------------------------------------------------------------------------------
373infixr 6 `wSerial`
375-- Additionally we can have m elements yield from the first stream and n
376-- elements yielding from the second stream. We can also have time slicing
377-- variants of positional interleaving, e.g. run first stream for m seconds and
378-- run the second stream for n seconds.
379--
380-- Similar combinators can be implemented using WAhead style.
382-- | Polymorphic version of the 'Semigroup' operation '<>' of 'WSerialT'.
383-- Interleaves two streams, yielding one element from each stream alternately.
384-- When one stream stops the rest of the other stream is used in the output
385-- stream.
386--
387-- @since 0.8.0
388--
389-- /Since: 0.2.0 ("Streamly")/
390{-# INLINE wSerial #-}
391wSerial :: IsStream t => t m a -> t m a -> t m a
392wSerial m1 m2 = mkStream $ \st yld sng stp -> do
393 let stop = foldStream st yld sng stp m2
394 single a = yld a m2
395 yieldk a r = yld a (wSerial m2 r)
396 foldStream st yieldk single stop m1
398-- | Like `wSerial` but stops interleaving as soon as the first stream stops.
399--
400-- @since 0.7.0
401{-# INLINE wSerialFst #-}
402wSerialFst :: IsStream t => t m a -> t m a -> t m a
403wSerialFst m1 m2 = mkStream $ \st yld sng stp -> do
404 let yieldFirst a r = yld a (yieldSecond r m2)
405 in foldStream st yieldFirst sng stp m1
407 where
409 yieldSecond s1 s2 = mkStream $ \st yld sng stp -> do
410 let stop = foldStream st yld sng stp s1
411 single a = yld a s1
412 yieldk a r = yld a (wSerial s1 r)
413 in foldStream st yieldk single stop s2
415-- | Like `wSerial` but stops interleaving as soon as any of the two streams
416-- stops.
417--
418-- @since 0.7.0
419{-# INLINE wSerialMin #-}
420wSerialMin :: IsStream t => t m a -> t m a -> t m a
421wSerialMin m1 m2 = mkStream $ \st yld sng stp -> do
422 let stop = stp
423 single a = sng a
424 yieldk a r = yld a (wSerial m2 r)
425 foldStream st yieldk single stop m1
427instance Semigroup (WSerialT m a) where
428 (<>) = wSerial
430infixr 5 <=>
432-- | Same as 'wSerial'.
433--
434-- @since 0.1.0
435{-# DEPRECATED (<=>) "Please use 'wSerial' instead." #-}
436{-# INLINE (<=>) #-}
437(<=>) :: IsStream t => t m a -> t m a -> t m a
438(<=>) = wSerial
440------------------------------------------------------------------------------
441-- Monoid
442------------------------------------------------------------------------------
444instance Monoid (WSerialT m a) where
445 mempty = K.nil
446 mappend = (<>)
448{-# INLINE apWSerial #-}
449apWSerial :: Monad m => WSerialT m (a -> b) -> WSerialT m a -> WSerialT m b
450apWSerial (WSerialT m1) (WSerialT m2) =
451 let f x1 = K.concatMapBy wSerial (pure . x1) m2
452 in WSerialT $ K.concatMapBy wSerial f m1
454instance Monad m => Applicative (WSerialT m) where
455 {-# INLINE pure #-}
456 pure = WSerialT . K.yield
457 {-# INLINE (<*>) #-}
458 (<*>) = apWSerial
460------------------------------------------------------------------------------
461-- Monad
462------------------------------------------------------------------------------
464instance Monad m => Monad (WSerialT m) where
465 return = pure
466 {-# INLINE (>>=) #-}
467 (>>=) = K.bindWith wSerial
469------------------------------------------------------------------------------
470-- Other instances
471------------------------------------------------------------------------------
473MONAD_COMMON_INSTANCES(WSerialT,)
474LIST_INSTANCES(WSerialT)
475NFDATA1_INSTANCE(WSerialT)
476FOLDABLE_INSTANCE(WSerialT)
477TRAVERSABLE_INSTANCE(WSerialT)
479------------------------------------------------------------------------------
480-- Construction
481------------------------------------------------------------------------------
483-- | Build a stream by unfolding a /monadic/ step function starting from a
484-- seed. The step function returns the next element in the stream and the next
485-- seed value. When it is done it returns 'Nothing' and the stream ends. For
486-- example,
487--
488-- @
489-- let f b =
490-- if b > 3
491-- then return Nothing
492-- else print b >> return (Just (b, b + 1))
493-- in drain $ unfoldrM f 0
494-- @
495-- @
496-- 0
497-- 1
498-- 2
499-- 3
500-- @
501--
502-- /Internal/
503--
504{-# INLINE unfoldrM #-}
505unfoldrM :: (IsStream t, Monad m) => (b -> m (Maybe (a, b))) -> b -> t m a
506unfoldrM step seed = D.fromStreamD (D.unfoldrM step seed)