1{-# LANGUAGE BangPatterns #-}
3{-# LANGUAGE ConstraintKinds #-}
4{-# LANGUAGE ExistentialQuantification #-}
5{-# LANGUAGE FlexibleContexts #-}
6{-# LANGUAGE FlexibleInstances #-}
7{-# LANGUAGE MultiParamTypeClasses #-}
8{-# LANGUAGE PatternSynonyms #-}
9{-# LANGUAGE ViewPatterns #-}
10{-# LANGUAGE RankNTypes #-}
12#include "../inline.hs"
15-- Module : Streamly.Streams.StreamD.Type
16-- Copyright : (c) 2018 Harendra Kumar
17-- Copyright : (c) Roman Leshchinskiy 2008-2010
20-- Maintainer : harendra.kumar@gmail.com
21-- Stability : experimental
24module Streamly.Streams.StreamD.Type
28 -- XXX UnStream is exported to avoid a performance issue in concatMap if we
29 -- use the pattern synonym "Stream".
30#if __GLASGOW_HASKELL__ >= 800
31 , Stream (Stream, UnStream)
66import Control.Applicative (liftA2)
67import Control.Monad (ap)
68import Control.Monad.Trans (lift, MonadTrans)
69import GHC.Types (SPEC(..))
70import Prelude hiding (map, mapM, foldr, take, concatMap)
72import Streamly.SVar (State(..), adaptState, defState)
73import Streamly.Fold.Types (Fold(..))
75import qualified Streamly.Streams.StreamK as K
77------------------------------------------------------------------------------
78-- The direct style stream type
79------------------------------------------------------------------------------
81-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
82-- the next state of the stream. 'Stop' indicates there are no more values in
84data Step s a = Yield a s | Skip s | Stop
86instance Functor (Step s) where
88 fmap f (Yield x s) = Yield (f x) s
89 fmap _ (Skip s) = Skip s
93-- | A stream consists of a step function that generates the next step given a
94-- current state, and the current state.
96 forall s. UnStream (State K.Stream m a -> s -> m (Step s a)) s
98unShare :: Stream m a -> Stream m a
99unShare (UnStream step state) = UnStream step' state
100 where step' gst = step (adaptState gst)
102pattern Stream :: (State K.Stream m a -> s -> m (Step s a)) -> s -> Stream m a
103pattern Stream step state <- (unShare -> UnStream step state)
104 where Stream = UnStream
106#if __GLASGOW_HASKELL__ >= 802
107{-# COMPLETE Stream #-}
110{-# INLINE_LATE fromStreamK #-}
111fromStreamK :: Monad m => K.Stream m a -> Stream m a
112fromStreamK = Stream step
115 let stop = return Stop
116 single a = return $ Yield a K.nil
117 yieldk a r = return $ Yield a r
118 in K.foldStreamShared gst yieldk single stop m1
120-- Convert a direct stream to and from CPS encoded stream
121{-# INLINE_LATE toStreamK #-}
122toStreamK :: Monad m => Stream m a -> K.Stream m a
123toStreamK (Stream step state) = go state
125 go st = K.mkStream $ \gst yld sng stp -> do
128 Yield x s -> yld x (go s)
129 Skip s -> K.foldStreamShared gst yld sng stp $ go s
132#ifndef DISABLE_FUSION
133{-# RULES "fromStreamK/toStreamK fusion"
134 forall s. toStreamK (fromStreamK s) = s #-}
135{-# RULES "toStreamK/fromStreamK fusion"
136 forall s. fromStreamK (toStreamK s) = s #-}
139------------------------------------------------------------------------------
141------------------------------------------------------------------------------
143-- | Map a monadic function over a 'Stream'
144{-# INLINE_NORMAL mapM #-}
145mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
146mapM f (Stream step state) = Stream step' state
148 {-# INLINE_LATE step' #-}
150 r <- step (adaptState gst) st
152 Yield x s -> f x >>= \a -> return $ Yield a s
153 Skip s -> return $ Skip s
157map :: Monad m => (a -> b) -> Stream m a -> Stream m b
158map f = mapM (return . f)
160instance Monad m => Functor (Stream m) where
164------------------------------------------------------------------------------
166------------------------------------------------------------------------------
168{-# INLINE_NORMAL concatMapM #-}
169concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
170concatMapM f (Stream step state) = Stream step' (Left state)
172 {-# INLINE_LATE step' #-}
173 step' gst (Left st) = do
174 r <- step (adaptState gst) st
178 return $ Skip (Right (b_stream, s))
179 Skip s -> return $ Skip (Left s)
182 -- XXX flattenArrays is 5x faster than "concatMap fromArray". if somehow we
183 -- can get inner_step to inline and fuse here we can perhaps get the same
184 -- performance using "concatMap fromArray".
186 -- XXX using the pattern synonym "Stream" causes a major performance issue
187 -- here even if the synonym does not include an adaptState call. Need to
188 -- find out why. Is that something to be fixed in GHC?
189 step' gst (Right (UnStream inner_step inner_st, st)) = do
190 r <- inner_step (adaptState gst) inner_st
193 return $ Yield b (Right (Stream inner_step inner_s, st))
195 return $ Skip (Right (Stream inner_step inner_s, st))
196 Stop -> return $ Skip (Left st)
198{-# INLINE concatMap #-}
199concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
200concatMap f = concatMapM (return . f)
202-- XXX The idea behind this rule is to rewrite any calls to "concatMap
203-- fromArray" automatically to flattenArrays which is much faster. However, we
204-- need an INLINE_EARLY on concatMap for this rule to fire. But if we use
205-- INLINE_EARLY on concatMap or fromArray then direct uses of
206-- "concatMap fromArray" (without the RULE) become much slower, this means
207-- "concatMap f" in general would become slower. Need to find a solution to
210-- {-# RULES "concatMap Array.toStreamD"
211-- concatMap Array.toStreamD = Array.flattenArray #-}
213-- | Create a singleton 'Stream' from a pure value.
214{-# INLINE_NORMAL yield #-}
215yield :: Monad m => a -> Stream m a
216yield x = Stream (\_ s -> return $ step undefined s) True
218 {-# INLINE_LATE step #-}
219 step _ True = Yield x False
222instance Monad m => Applicative (Stream m) where
228-- NOTE: even though concatMap for StreamD is 4x faster compared to StreamK,
229-- the monad instance does not seem to be significantly faster.
230instance Monad m => Monad (Stream m) where
231 {-# INLINE return #-}
234 (>>=) = flip concatMap
236-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
237-- one shot operations, but helps immensely in operations composed of multiple
238-- combinators or the same combinator many times. There seems to be an
239-- opportunity to optimize here, can we get both, better perf for single ops
240-- as well as composed ops? Without SPEC, all single operation benchmarks
243-- The way we want a left fold to be strict, dually we want the right fold to
244-- be lazy. The correct signature of the fold function to keep it lazy must be
245-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
246-- earlier, which is incorrect. In the latter signature we have to feed the
247-- value to the fold function after evaluating the monadic action, depending on
248-- the bind behavior of the monad, the action may get evaluated immediately
249-- introducing unnecessary strictness to the fold. If the implementation is
250-- lazy the following example, must work:
252-- S.foldrM (\x t -> if x then return t else return False) (return True)
253-- (S.fromList [False,undefined] :: SerialT IO Bool)
255{-# INLINE_NORMAL foldrM #-}
256foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
257foldrM f z (Stream step state) = go SPEC state
260 r <- step defState st
262 Yield x s -> f x (go SPEC s)
266{-# INLINE_NORMAL foldrMx #-}
268 => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
269foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
272 r <- step defState st
274 Yield x s -> fstep x (go SPEC s)
278-- Note that foldr works on pure values, therefore it becomes necessarily
279-- strict when the monad m is strict. In that case it cannot terminate early,
280-- it would evaluate all of its input. Though, this should work fine with lazy
281-- monads. For example, if "any" is implemented using "foldr" instead of
282-- "foldrM" it performs the same with Identity monad but performs 1000x slower
285{-# INLINE_NORMAL foldr #-}
286foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
287foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
289-- | Create a singleton 'Stream' from a monadic action.
290{-# INLINE_NORMAL yieldM #-}
291yieldM :: Monad m => m a -> Stream m a
292yieldM m = Stream step True
294 {-# INLINE_LATE step #-}
295 step _ True = m >>= \x -> return $ Yield x False
296 step _ False = return Stop
298-- this performs horribly, should not be used
299{-# INLINE_NORMAL foldrS #-}
302 => (a -> Stream m b -> Stream m b)
306foldrS f final (Stream step state) = go SPEC state
310 r <- yieldM $ step defState st
312 Yield x s -> f x (go SPEC s)
316-- Right fold to some transformer (T) monad. This can be useful to implement
317-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
319{-# INLINE_NORMAL foldrT #-}
320foldrT :: (Monad m, Monad (t m), MonadTrans t)
321 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
322foldrT f final (Stream step state) = go SPEC state
325 r <- lift $ step defState st
327 Yield x s -> f x (go SPEC s)
332toList :: Monad m => Stream m a -> m [a]
335-- XXX run begin action only if the stream is not empty.
336{-# INLINE_NORMAL foldlMx' #-}
337foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
338foldlMx' fstep begin done (Stream step state) =
339 begin >>= \x -> go SPEC x state
342 go !_ acc st = acc `seq` do
343 r <- step defState st
348 Skip s -> go SPEC acc s
351{-# INLINE foldlx' #-}
352foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
353foldlx' fstep begin done m =
354 foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
356-- XXX implement in terms of foldlMx'?
357{-# INLINE_NORMAL foldlM' #-}
358foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
359foldlM' fstep begin (Stream step state) = go SPEC begin state
361 go !_ acc st = acc `seq` do
362 r <- step defState st
367 Skip s -> go SPEC acc s
371foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
372foldl' fstep = foldlM' (\b a -> return (fstep b a))
374-- | Convert a list of pure values to a 'Stream'
375{-# INLINE_LATE fromList #-}
376fromList :: Monad m => [a] -> Stream m a
377fromList = Stream step
379 {-# INLINE_LATE step #-}
380 step _ (x:xs) = return $ Yield x xs
381 step _ [] = return Stop
383------------------------------------------------------------------------------
385------------------------------------------------------------------------------
387{-# INLINE_NORMAL eqBy #-}
388eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
389eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
391 eq_loop0 !_ s1 s2 = do
392 r <- step1 defState s1
394 Yield x s1' -> eq_loop1 SPEC x s1' s2
395 Skip s1' -> eq_loop0 SPEC s1' s2
398 eq_loop1 !_ x s1 s2 = do
399 r <- step2 defState s2
402 | eq x y -> eq_loop0 SPEC s1 s2'
403 | otherwise -> return False
404 Skip s2' -> eq_loop1 SPEC x s1 s2'
408 r <- step2 defState s2
410 Yield _ _ -> return False
411 Skip s2' -> eq_null s2'
414-- | Compare two streams lexicographically
415{-# INLINE_NORMAL cmpBy #-}
418 => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
419cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
421 cmp_loop0 !_ s1 s2 = do
422 r <- step1 defState s1
424 Yield x s1' -> cmp_loop1 SPEC x s1' s2
425 Skip s1' -> cmp_loop0 SPEC s1' s2
428 cmp_loop1 !_ x s1 s2 = do
429 r <- step2 defState s2
431 Yield y s2' -> case x `cmp` y of
432 EQ -> cmp_loop0 SPEC s1 s2'
434 Skip s2' -> cmp_loop1 SPEC x s1 s2'
438 r <- step2 defState s2
440 Yield _ _ -> return LT
441 Skip s2' -> cmp_null s2'
444{-# INLINE_NORMAL take #-}
445take :: Monad m => Int -> Stream m a -> Stream m a
446take n (Stream step state) = n `seq` Stream step' (state, 0)
448 {-# INLINE_LATE step' #-}
449 step' gst (st, i) | i < n = do
452 Yield x s -> Yield x (s, i + 1)
453 Skip s -> Skip (s, i)
455 step' _ (_, _) = return Stop
457------------------------------------------------------------------------------
459------------------------------------------------------------------------------
461{-# INLINE_LATE foldOneGroup #-}
467 -> State K.Stream m a
468 -> (State K.Stream m a -> s -> m (Step s a))
471foldOneGroup n (Fold fstep begin done) x gst step state = do
478 -- XXX is it strict enough?
479 go !_ st !acc i | i < n = do
484 go SPEC s acc1 (i + 1)
485 Skip s -> go SPEC s acc i
488 return (res, Nothing)
493-- groupsOf takes 11 sec to write the file from a stream, whereas just
494-- reading in the same file as a stream and folding each element of the stream
495-- using (+) takes just 1.5 sec, so we are still pretty slow (7x slow), there
496-- is scope to make it faster. There is a possibility of better fusion here.
498{-# INLINE_NORMAL groupsOf #-}
505groupsOf n f (Stream step state) =
506 n `seq` Stream stepOuter (Just state)
510 {-# INLINE_LATE stepOuter #-}
511 stepOuter gst (Just st) = do
512 -- We retrieve the first element of the stream before we start to fold
513 -- a chunk so that we do not return an empty chunk in case the stream
515 res <- step (adaptState gst) st
518 -- XXX how to make sure that stepInner and f get fused
519 -- This problem seems to be similar to the concatMap problem
520 (r, s1) <- foldOneGroup n f x (adaptState gst) step s
522 Skip s -> return $ Skip $ Just s
525 stepOuter _ Nothing = return Stop
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list