on a00e057 help sign in
455 lines 14 kB history raw
1{-# LANGUAGE CPP #-}
2{-# LANGUAGE ConstraintKinds #-}
3{-# LANGUAGE FlexibleContexts #-}
4{-# LANGUAGE FlexibleInstances #-}
5{-# LANGUAGE GeneralizedNewtypeDeriving#-}
6{-# LANGUAGE InstanceSigs #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE TypeFamilies #-}
9{-# LANGUAGE UndecidableInstances #-} -- XXX
11-- |
12-- Module : Streamly.Internal.Data.Stream.Serial
13-- Copyright : (c) 2017 Harendra Kumar
14--
15-- License : BSD3
16-- Maintainer : streamly@composewell.com
17-- Stability : experimental
18-- Portability : GHC
19--
20--
21module Streamly.Internal.Data.Stream.Serial
22 (
23 -- * Serial appending stream
24 SerialT
25 , Serial
26 , K.serial
27 , serially
29 -- * Serial interleaving stream
30 , WSerialT
31 , WSerial
32 , wSerial
33 , wSerialFst
34 , wSerialMin
35 , wSerially
37 -- * Construction
38 , unfoldrM
40 -- * Transformation
41 , map
42 , mapM
44 -- * Deprecated
45 , StreamT
46 , InterleavedT
47 , (<=>)
48 , interleaving
49 )
50where
52import Control.Applicative (liftA2)
53import Control.DeepSeq (NFData(..))
54#if MIN_VERSION_deepseq(1,4,3)
55import Control.DeepSeq (NFData1(..))
56#endif
57import Control.Monad.Base (MonadBase(..), liftBaseDefault)
58import Control.Monad.Catch (MonadThrow, throwM)
59-- import Control.Monad.Error.Class (MonadError(..))
60import Control.Monad.IO.Class (MonadIO(..))
61import Control.Monad.Reader.Class (MonadReader(..))
62import Control.Monad.State.Class (MonadState(..))
63import Control.Monad.Trans.Class (MonadTrans(lift))
64import Data.Functor.Identity (Identity(..), runIdentity)
65import Data.Foldable (fold)
66#if __GLASGOW_HASKELL__ < 808
67import Data.Semigroup (Semigroup(..))
68#endif
69import GHC.Exts (IsList(..), IsString(..))
70import Text.Read (Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec,
71 readListPrecDefault)
72import Prelude hiding (map, mapM)
74import Streamly.Internal.Data.Stream.StreamK (IsStream(..), adapt, Stream, mkStream,
75 foldStream)
76import qualified Streamly.Internal.Data.Stream.Prelude as P
77import qualified Streamly.Internal.Data.Stream.StreamK as K
78import qualified Streamly.Internal.Data.Stream.StreamD as D
79import qualified Streamly.Internal.Data.Unfold as UF
81#include "Instances.hs"
82#include "inline.hs"
84------------------------------------------------------------------------------
85-- SerialT
86------------------------------------------------------------------------------
88-- | The 'Semigroup' operation for 'SerialT' behaves like a regular append
89-- operation. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
90-- first until it exhausts and then stream @b@ is evaluated. In other words,
91-- the elements of stream @b@ are appended to the elements of stream @a@. This
92-- operation can be used to fold an infinite lazy container of streams.
93--
94-- @
95-- import Streamly
96-- import qualified "Streamly.Prelude" as S
97--
98-- main = (S.toList . 'serially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
99-- @
100-- @
101-- [1,2,3,4]
102-- @
103--
104-- The 'Monad' instance runs the /monadic continuation/ for each
105-- element of the stream, serially.
106--
107-- @
108-- main = S.drain . 'serially' $ do
109-- x <- return 1 \<\> return 2
110-- S.yieldM $ print x
111-- @
112-- @
113-- 1
114-- 2
115-- @
116--
117-- 'SerialT' nests streams serially in a depth first manner.
118--
119-- @
120-- main = S.drain . 'serially' $ do
121-- x <- return 1 \<\> return 2
122-- y <- return 3 \<\> return 4
123-- S.yieldM $ print (x, y)
124-- @
125-- @
126-- (1,3)
127-- (1,4)
128-- (2,3)
129-- (2,4)
130-- @
131--
132-- We call the monadic code being run for each element of the stream a monadic
133-- continuation. In imperative paradigm we can think of this composition as
134-- nested @for@ loops and the monadic continuation is the body of the loop. The
135-- loop iterates for all elements of the stream.
136--
137-- Note that the behavior and semantics of 'SerialT', including 'Semigroup'
138-- and 'Monad' instances are exactly like Haskell lists except that 'SerialT'
139-- can contain effectful actions while lists are pure.
140--
141-- In the code above, the 'serially' combinator can be omitted as the default
142-- stream type is 'SerialT'.
143--
144-- @since 0.2.0
145newtype SerialT m a = SerialT {getSerialT :: Stream m a}
146 deriving (Semigroup, Monoid, MonadTrans)
148-- | A serial IO stream of elements of type @a@. See 'SerialT' documentation
149-- for more details.
150--
151-- @since 0.2.0
152type Serial = SerialT IO
154-- |
155-- @since 0.1.0
156{-# DEPRECATED StreamT "Please use 'SerialT' instead." #-}
157type StreamT = SerialT
159-- | Fix the type of a polymorphic stream as 'SerialT'.
160--
161-- @since 0.1.0
162serially :: IsStream t => SerialT m a -> t m a
163serially = adapt
165{-# INLINE consMSerial #-}
166{-# SPECIALIZE consMSerial :: IO a -> SerialT IO a -> SerialT IO a #-}
167consMSerial :: Monad m => m a -> SerialT m a -> SerialT m a
168consMSerial m ms = fromStream $ K.consMStream m (toStream ms)
170instance IsStream SerialT where
171 toStream = getSerialT
172 fromStream = SerialT
173 consM = consMSerial
174 (|:) = consMSerial
176------------------------------------------------------------------------------
177-- Monad
178------------------------------------------------------------------------------
180instance Monad m => Monad (SerialT m) where
181 return = pure
182 {-# INLINE (>>=) #-}
183 (>>=) = K.bindWith K.serial
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 let f x1 = D.concatMapU (UF.singleton (pure . x1)) (D.toStreamD m2)
218 in D.fromStreamD $ D.concatMap f (D.toStreamD m1)
220instance Monad m => Applicative (SerialT m) where
221 {-# INLINE pure #-}
222 pure = SerialT . K.yield
223 {-# INLINE (<*>) #-}
224 (<*>) = apSerial
226MONAD_COMMON_INSTANCES(SerialT,)
227LIST_INSTANCES(SerialT)
228NFDATA1_INSTANCE(SerialT)
229FOLDABLE_INSTANCE(SerialT)
230TRAVERSABLE_INSTANCE(SerialT)
232------------------------------------------------------------------------------
233-- WSerialT
234------------------------------------------------------------------------------
236-- | The 'Semigroup' operation for 'WSerialT' interleaves the elements from the
237-- two streams. Therefore, when @a <> b@ is evaluated, stream @a@ is evaluated
238-- first to produce the first element of the combined stream and then stream
239-- @b@ is evaluated to produce the next element of the combined stream, and
240-- then we go back to evaluating stream @a@ and so on. In other words, the
241-- elements of stream @a@ are interleaved with the elements of stream @b@.
242--
243-- Note that when multiple actions are combined like @a <> b <> c ... <> z@ we
244-- interleave them in a binary fashion i.e. @a@ and @b@ are interleaved with
245-- each other and the result is interleaved with @c@ and so on. This will not
246-- act as a true round-robin scheduling across all the streams. Note that this
247-- operation cannot be used to fold a container of infinite streams as the
248-- state that it needs to maintain is proportional to the number of streams.
249--
250-- @
251-- import Streamly
252-- import qualified "Streamly.Prelude" as S
253--
254-- main = (S.toList . 'wSerially' $ (S.fromList [1,2]) \<\> (S.fromList [3,4])) >>= print
255-- @
256-- @
257-- [1,3,2,4]
258-- @
259--
260-- Similarly, the 'Monad' instance interleaves the iterations of the
261-- inner and the outer loop, nesting loops in a breadth first manner.
262--
263--
264-- @
265-- main = S.drain . 'wSerially' $ do
266-- x <- return 1 \<\> return 2
267-- y <- return 3 \<\> return 4
268-- S.yieldM $ print (x, y)
269-- @
270-- @
271-- (1,3)
272-- (2,3)
273-- (1,4)
274-- (2,4)
275-- @
276--
277-- @since 0.2.0
278newtype WSerialT m a = WSerialT {getWSerialT :: Stream m a}
279 deriving (MonadTrans)
281-- | An interleaving serial IO stream of elements of type @a@. See 'WSerialT'
282-- documentation for more details.
283--
284-- @since 0.2.0
285type WSerial = WSerialT IO
287-- |
288-- @since 0.1.0
289{-# DEPRECATED InterleavedT "Please use 'WSerialT' instead." #-}
290type InterleavedT = WSerialT
292-- | Fix the type of a polymorphic stream as 'WSerialT'.
293--
294-- @since 0.2.0
295wSerially :: IsStream t => WSerialT m a -> t m a
296wSerially = adapt
298-- | Same as 'wSerially'.
299--
300-- @since 0.1.0
301{-# DEPRECATED interleaving "Please use wSerially instead." #-}
302interleaving :: IsStream t => WSerialT m a -> t m a
303interleaving = wSerially
305consMWSerial :: Monad m => m a -> WSerialT m a -> WSerialT m a
306consMWSerial m ms = fromStream $ K.consMStream m (toStream ms)
308instance IsStream WSerialT where
309 toStream = getWSerialT
310 fromStream = WSerialT
312 {-# INLINE consM #-}
313 {-# SPECIALIZE consM :: IO a -> WSerialT IO a -> WSerialT IO a #-}
314 consM :: Monad m => m a -> WSerialT m a -> WSerialT m a
315 consM = consMWSerial
317 {-# INLINE (|:) #-}
318 {-# SPECIALIZE (|:) :: IO a -> WSerialT IO a -> WSerialT IO a #-}
319 (|:) :: Monad m => m a -> WSerialT m a -> WSerialT m a
320 (|:) = consMWSerial
322------------------------------------------------------------------------------
323-- Semigroup
324------------------------------------------------------------------------------
326-- Additionally we can have m elements yield from the first stream and n
327-- elements yielding from the second stream. We can also have time slicing
328-- variants of positional interleaving, e.g. run first stream for m seconds and
329-- run the second stream for n seconds.
330--
331-- Similar combinators can be implemented using WAhead style.
333-- | Polymorphic version of the 'Semigroup' operation '<>' of 'WSerialT'.
334-- Interleaves two streams, yielding one element from each stream alternately.
335-- When one stream stops the rest of the other stream is used in the output
336-- stream.
337--
338-- @since 0.2.0
339{-# INLINE wSerial #-}
340wSerial :: IsStream t => t m a -> t m a -> t m a
341wSerial m1 m2 = mkStream $ \st yld sng stp -> do
342 let stop = foldStream st yld sng stp m2
343 single a = yld a m2
344 yieldk a r = yld a (wSerial m2 r)
345 foldStream st yieldk single stop m1
347-- | Like `wSerial` but stops interleaving as soon as the first stream stops.
348--
349-- @since 0.7.0
350{-# INLINE wSerialFst #-}
351wSerialFst :: IsStream t => t m a -> t m a -> t m a
352wSerialFst m1 m2 = mkStream $ \st yld sng stp -> do
353 let yieldFirst a r = yld a (yieldSecond r m2)
354 in foldStream st yieldFirst sng stp m1
356 where
358 yieldSecond s1 s2 = mkStream $ \st yld sng stp -> do
359 let stop = foldStream st yld sng stp s1
360 single a = yld a s1
361 yieldk a r = yld a (wSerial s1 r)
362 in foldStream st yieldk single stop s2
364-- | Like `wSerial` but stops interleaving as soon as any of the two streams
365-- stops.
366--
367-- @since 0.7.0
368{-# INLINE wSerialMin #-}
369wSerialMin :: IsStream t => t m a -> t m a -> t m a
370wSerialMin m1 m2 = mkStream $ \st yld sng stp -> do
371 let stop = stp
372 single a = sng a
373 yieldk a r = yld a (wSerial m2 r)
374 foldStream st yieldk single stop m1
376instance Semigroup (WSerialT m a) where
377 (<>) = wSerial
379infixr 5 <=>
381-- | Same as 'wSerial'.
382--
383-- @since 0.1.0
384{-# DEPRECATED (<=>) "Please use 'wSerial' instead." #-}
385{-# INLINE (<=>) #-}
386(<=>) :: IsStream t => t m a -> t m a -> t m a
387(<=>) = wSerial
389------------------------------------------------------------------------------
390-- Monoid
391------------------------------------------------------------------------------
393instance Monoid (WSerialT m a) where
394 mempty = K.nil
395 mappend = (<>)
397{-# INLINE apWSerial #-}
398apWSerial :: Monad m => WSerialT m (a -> b) -> WSerialT m a -> WSerialT m b
399apWSerial (WSerialT m1) (WSerialT m2) =
400 let f x1 = K.concatMapBy wSerial (pure . x1) m2
401 in WSerialT $ K.concatMapBy wSerial f m1
403instance Monad m => Applicative (WSerialT m) where
404 {-# INLINE pure #-}
405 pure = WSerialT . K.yield
406 {-# INLINE (<*>) #-}
407 (<*>) = apWSerial
409------------------------------------------------------------------------------
410-- Monad
411------------------------------------------------------------------------------
413instance Monad m => Monad (WSerialT m) where
414 return = pure
415 {-# INLINE (>>=) #-}
416 (>>=) = K.bindWith wSerial
418------------------------------------------------------------------------------
419-- Other instances
420------------------------------------------------------------------------------
422MONAD_COMMON_INSTANCES(WSerialT,)
423LIST_INSTANCES(WSerialT)
424NFDATA1_INSTANCE(WSerialT)
425FOLDABLE_INSTANCE(WSerialT)
426TRAVERSABLE_INSTANCE(WSerialT)
428------------------------------------------------------------------------------
429-- Construction
430------------------------------------------------------------------------------
432-- | Build a stream by unfolding a /monadic/ step function starting from a
433-- seed. The step function returns the next element in the stream and the next
434-- seed value. When it is done it returns 'Nothing' and the stream ends. For
435-- example,
436--
437-- @
438-- let f b =
439-- if b > 3
440-- then return Nothing
441-- else print b >> return (Just (b, b + 1))
442-- in drain $ unfoldrM f 0
443-- @
444-- @
445-- 0
446-- 1
447-- 2
448-- 3
449-- @
450--
451-- /Internal/
452--
453{-# INLINE unfoldrM #-}
454unfoldrM :: (IsStream t, Monad m) => (b -> m (Maybe (a, b))) -> b -> t m a
455unfoldrM step seed = D.fromStreamD (D.unfoldrM step seed)