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)
67import Control.Applicative (liftA2)
68import Control.Monad (ap)
69import Control.Monad.Trans (lift, MonadTrans)
70import GHC.Types (SPEC(..))
71import Prelude hiding (map, mapM, foldr, take, concatMap)
73import Streamly.SVar (State(..), adaptState, defState)
74import Streamly.Fold.Types (Fold(..))
76import qualified Streamly.Streams.StreamK as K
78------------------------------------------------------------------------------
79-- The direct style stream type
80------------------------------------------------------------------------------
82-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
83-- the next state of the stream. 'Stop' indicates there are no more values in
85data Step s a = Yield a s | Skip s | Stop
87instance Functor (Step s) where
89 fmap f (Yield x s) = Yield (f x) s
90 fmap _ (Skip s) = Skip s
94-- | A stream consists of a step function that generates the next step given a
95-- current state, and the current state.
97 forall s. UnStream (State K.Stream m a -> s -> m (Step s a)) s
99unShare :: Stream m a -> Stream m a
100unShare (UnStream step state) = UnStream step' state
101 where step' gst = step (adaptState gst)
103pattern Stream :: (State K.Stream m a -> s -> m (Step s a)) -> s -> Stream m a
104pattern Stream step state <- (unShare -> UnStream step state)
105 where Stream = UnStream
107#if __GLASGOW_HASKELL__ >= 802
108{-# COMPLETE Stream #-}
111{-# INLINE_LATE fromStreamK #-}
112fromStreamK :: Monad m => K.Stream m a -> Stream m a
113fromStreamK = Stream step
116 let stop = return Stop
117 single a = return $ Yield a K.nil
118 yieldk a r = return $ Yield a r
119 in K.foldStreamShared gst yieldk single stop m1
121-- Convert a direct stream to and from CPS encoded stream
122{-# INLINE_LATE toStreamK #-}
123toStreamK :: Monad m => Stream m a -> K.Stream m a
124toStreamK (Stream step state) = go state
126 go st = K.mkStream $ \gst yld sng stp -> do
129 Yield x s -> yld x (go s)
130 Skip s -> K.foldStreamShared gst yld sng stp $ go s
133#ifndef DISABLE_FUSION
134{-# RULES "fromStreamK/toStreamK fusion"
135 forall s. toStreamK (fromStreamK s) = s #-}
136{-# RULES "toStreamK/fromStreamK fusion"
137 forall s. fromStreamK (toStreamK s) = s #-}
140------------------------------------------------------------------------------
142------------------------------------------------------------------------------
144{-# INLINE fromStreamD #-}
145fromStreamD :: (K.IsStream t, Monad m) => Stream m a -> t m a
146fromStreamD = K.fromStream . toStreamK
148------------------------------------------------------------------------------
150------------------------------------------------------------------------------
152-- | Map a monadic function over a 'Stream'
153{-# INLINE_NORMAL mapM #-}
154mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
155mapM f (Stream step state) = Stream step' state
157 {-# INLINE_LATE step' #-}
159 r <- step (adaptState gst) st
161 Yield x s -> f x >>= \a -> return $ Yield a s
162 Skip s -> return $ Skip s
166map :: Monad m => (a -> b) -> Stream m a -> Stream m b
167map f = mapM (return . f)
169instance Monad m => Functor (Stream m) where
173------------------------------------------------------------------------------
175------------------------------------------------------------------------------
177{-# INLINE_NORMAL concatMapM #-}
178concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b
179concatMapM f (Stream step state) = Stream step' (Left state)
181 {-# INLINE_LATE step' #-}
182 step' gst (Left st) = do
183 r <- step (adaptState gst) st
187 return $ Skip (Right (b_stream, s))
188 Skip s -> return $ Skip (Left s)
191 -- XXX flattenArrays is 5x faster than "concatMap fromArray". if somehow we
192 -- can get inner_step to inline and fuse here we can perhaps get the same
193 -- performance using "concatMap fromArray".
195 -- XXX using the pattern synonym "Stream" causes a major performance issue
196 -- here even if the synonym does not include an adaptState call. Need to
197 -- find out why. Is that something to be fixed in GHC?
198 step' gst (Right (UnStream inner_step inner_st, st)) = do
199 r <- inner_step (adaptState gst) inner_st
202 return $ Yield b (Right (Stream inner_step inner_s, st))
204 return $ Skip (Right (Stream inner_step inner_s, st))
205 Stop -> return $ Skip (Left st)
207{-# INLINE concatMap #-}
208concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b
209concatMap f = concatMapM (return . f)
211-- XXX The idea behind this rule is to rewrite any calls to "concatMap
212-- fromArray" automatically to flattenArrays which is much faster. However, we
213-- need an INLINE_EARLY on concatMap for this rule to fire. But if we use
214-- INLINE_EARLY on concatMap or fromArray then direct uses of
215-- "concatMap fromArray" (without the RULE) become much slower, this means
216-- "concatMap f" in general would become slower. Need to find a solution to
219-- {-# RULES "concatMap Array.toStreamD"
220-- concatMap Array.toStreamD = Array.flattenArray #-}
222-- | Create a singleton 'Stream' from a pure value.
223{-# INLINE_NORMAL yield #-}
224yield :: Monad m => a -> Stream m a
225yield x = Stream (\_ s -> return $ step undefined s) True
227 {-# INLINE_LATE step #-}
228 step _ True = Yield x False
231instance Monad m => Applicative (Stream m) where
237-- NOTE: even though concatMap for StreamD is 4x faster compared to StreamK,
238-- the monad instance does not seem to be significantly faster.
239instance Monad m => Monad (Stream m) where
240 {-# INLINE return #-}
243 (>>=) = flip concatMap
245-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
246-- one shot operations, but helps immensely in operations composed of multiple
247-- combinators or the same combinator many times. There seems to be an
248-- opportunity to optimize here, can we get both, better perf for single ops
249-- as well as composed ops? Without SPEC, all single operation benchmarks
252-- The way we want a left fold to be strict, dually we want the right fold to
253-- be lazy. The correct signature of the fold function to keep it lazy must be
254-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
255-- earlier, which is incorrect. In the latter signature we have to feed the
256-- value to the fold function after evaluating the monadic action, depending on
257-- the bind behavior of the monad, the action may get evaluated immediately
258-- introducing unnecessary strictness to the fold. If the implementation is
259-- lazy the following example, must work:
261-- S.foldrM (\x t -> if x then return t else return False) (return True)
262-- (S.fromList [False,undefined] :: SerialT IO Bool)
264{-# INLINE_NORMAL foldrM #-}
265foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
266foldrM f z (Stream step state) = go SPEC state
269 r <- step defState st
271 Yield x s -> f x (go SPEC s)
275{-# INLINE_NORMAL foldrMx #-}
277 => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
278foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
281 r <- step defState st
283 Yield x s -> fstep x (go SPEC s)
287-- Note that foldr works on pure values, therefore it becomes necessarily
288-- strict when the monad m is strict. In that case it cannot terminate early,
289-- it would evaluate all of its input. Though, this should work fine with lazy
290-- monads. For example, if "any" is implemented using "foldr" instead of
291-- "foldrM" it performs the same with Identity monad but performs 1000x slower
294{-# INLINE_NORMAL foldr #-}
295foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
296foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
298-- | Create a singleton 'Stream' from a monadic action.
299{-# INLINE_NORMAL yieldM #-}
300yieldM :: Monad m => m a -> Stream m a
301yieldM m = Stream step True
303 {-# INLINE_LATE step #-}
304 step _ True = m >>= \x -> return $ Yield x False
305 step _ False = return Stop
307-- this performs horribly, should not be used
308{-# INLINE_NORMAL foldrS #-}
311 => (a -> Stream m b -> Stream m b)
315foldrS f final (Stream step state) = go SPEC state
319 r <- yieldM $ step defState st
321 Yield x s -> f x (go SPEC s)
325-- Right fold to some transformer (T) monad. This can be useful to implement
326-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
328{-# INLINE_NORMAL foldrT #-}
329foldrT :: (Monad m, Monad (t m), MonadTrans t)
330 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
331foldrT f final (Stream step state) = go SPEC state
334 r <- lift $ step defState st
336 Yield x s -> f x (go SPEC s)
341toList :: Monad m => Stream m a -> m [a]
344-- XXX run begin action only if the stream is not empty.
345{-# INLINE_NORMAL foldlMx' #-}
346foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
347foldlMx' fstep begin done (Stream step state) =
348 begin >>= \x -> go SPEC x state
351 go !_ acc st = acc `seq` do
352 r <- step defState st
357 Skip s -> go SPEC acc s
360{-# INLINE foldlx' #-}
361foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
362foldlx' fstep begin done m =
363 foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
365-- XXX implement in terms of foldlMx'?
366{-# INLINE_NORMAL foldlM' #-}
367foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
368foldlM' fstep begin (Stream step state) = go SPEC begin state
370 go !_ acc st = acc `seq` do
371 r <- step defState st
376 Skip s -> go SPEC acc s
380foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
381foldl' fstep = foldlM' (\b a -> return (fstep b a))
383-- | Convert a list of pure values to a 'Stream'
384{-# INLINE_LATE fromList #-}
385fromList :: Monad m => [a] -> Stream m a
386fromList = Stream step
388 {-# INLINE_LATE step #-}
389 step _ (x:xs) = return $ Yield x xs
390 step _ [] = return Stop
392------------------------------------------------------------------------------
394------------------------------------------------------------------------------
396{-# INLINE_NORMAL eqBy #-}
397eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
398eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
400 eq_loop0 !_ s1 s2 = do
401 r <- step1 defState s1
403 Yield x s1' -> eq_loop1 SPEC x s1' s2
404 Skip s1' -> eq_loop0 SPEC s1' s2
407 eq_loop1 !_ x s1 s2 = do
408 r <- step2 defState s2
411 | eq x y -> eq_loop0 SPEC s1 s2'
412 | otherwise -> return False
413 Skip s2' -> eq_loop1 SPEC x s1 s2'
417 r <- step2 defState s2
419 Yield _ _ -> return False
420 Skip s2' -> eq_null s2'
423-- | Compare two streams lexicographically
424{-# INLINE_NORMAL cmpBy #-}
427 => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
428cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
430 cmp_loop0 !_ s1 s2 = do
431 r <- step1 defState s1
433 Yield x s1' -> cmp_loop1 SPEC x s1' s2
434 Skip s1' -> cmp_loop0 SPEC s1' s2
437 cmp_loop1 !_ x s1 s2 = do
438 r <- step2 defState s2
440 Yield y s2' -> case x `cmp` y of
441 EQ -> cmp_loop0 SPEC s1 s2'
443 Skip s2' -> cmp_loop1 SPEC x s1 s2'
447 r <- step2 defState s2
449 Yield _ _ -> return LT
450 Skip s2' -> cmp_null s2'
453{-# INLINE_NORMAL take #-}
454take :: Monad m => Int -> Stream m a -> Stream m a
455take n (Stream step state) = n `seq` Stream step' (state, 0)
457 {-# INLINE_LATE step' #-}
458 step' gst (st, i) | i < n = do
461 Yield x s -> Yield x (s, i + 1)
462 Skip s -> Skip (s, i)
464 step' _ (_, _) = return Stop
466------------------------------------------------------------------------------
468------------------------------------------------------------------------------
470{-# INLINE_LATE foldOneGroup #-}
476 -> State K.Stream m a
477 -> (State K.Stream m a -> s -> m (Step s a))
480foldOneGroup n (Fold fstep begin done) x gst step state = do
487 -- XXX is it strict enough?
488 go !_ st !acc i | i < n = do
493 go SPEC s acc1 (i + 1)
494 Skip s -> go SPEC s acc i
497 return (res, Nothing)
502-- groupsOf takes 11 sec to write the file from a stream, whereas just
503-- reading in the same file as a stream and folding each element of the stream
504-- using (+) takes just 1.5 sec, so we are still pretty slow (7x slow), there
505-- is scope to make it faster. There is a possibility of better fusion here.
507{-# INLINE_NORMAL groupsOf #-}
514groupsOf n f (Stream step state) =
515 n `seq` Stream stepOuter (Just state)
519 {-# INLINE_LATE stepOuter #-}
520 stepOuter gst (Just st) = do
521 -- We retrieve the first element of the stream before we start to fold
522 -- a chunk so that we do not return an empty chunk in case the stream
524 res <- step (adaptState gst) st
527 -- XXX how to make sure that stepInner and f get fused
528 -- This problem seems to be similar to the concatMap problem
529 (r, s1) <- foldOneGroup n f x (adaptState gst) step s
531 Skip s -> return $ Skip $ Just s
534 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