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
19-- Maintainer : harendra.kumar@gmail.com
20-- Stability : experimental
23module Streamly.Streams.StreamD.Type
27 -- XXX UnStream is exported to avoid a performance issue in concatMap if we
28 -- use the pattern synonym "Stream".
29#if __GLASGOW_HASKELL__ >= 800
30 , Stream (Stream, UnStream)
60import Control.Applicative (liftA2)
61import Control.Monad.Trans (lift, MonadTrans)
62import GHC.Types (SPEC(..))
63import Prelude hiding (map, mapM, foldr, take)
65import Streamly.SVar (State(..), adaptState, defState)
66import Streamly.Fold.Types (Fold(..))
68import qualified Streamly.Streams.StreamK as K
70------------------------------------------------------------------------------
71-- The direct style stream type
72------------------------------------------------------------------------------
74-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
75-- the next state of the stream. 'Stop' indicates there are no more values in
77data Step s a = Yield a s | Skip s | Stop
79instance Functor (Step s) where
81 fmap f (Yield x s) = Yield (f x) s
82 fmap _ (Skip s) = Skip s
86-- | A stream consists of a step function that generates the next step given a
87-- current state, and the current state.
89 forall s. UnStream (State K.Stream m a -> s -> m (Step s a)) s
91unShare :: Stream m a -> Stream m a
92unShare (UnStream step state) = UnStream step' state
93 where step' gst = step (adaptState gst)
95pattern Stream :: (State K.Stream m a -> s -> m (Step s a)) -> s -> Stream m a
96pattern Stream step state <- (unShare -> UnStream step state)
97 where Stream = UnStream
99#if __GLASGOW_HASKELL__ >= 802
100{-# COMPLETE Stream #-}
103{-# INLINE_LATE fromStreamK #-}
104fromStreamK :: Monad m => K.Stream m a -> Stream m a
105fromStreamK = Stream step
108 let stop = return Stop
109 single a = return $ Yield a K.nil
110 yieldk a r = return $ Yield a r
111 in K.foldStreamShared gst yieldk single stop m1
113-- Convert a direct stream to and from CPS encoded stream
114{-# INLINE_LATE toStreamK #-}
115toStreamK :: Monad m => Stream m a -> K.Stream m a
116toStreamK (Stream step state) = go state
118 go st = K.mkStream $ \gst yld sng stp -> do
121 Yield x s -> yld x (go s)
122 Skip s -> K.foldStreamShared gst yld sng stp $ go s
125#ifndef DISABLE_FUSION
126{-# RULES "fromStreamK/toStreamK fusion"
127 forall s. toStreamK (fromStreamK s) = s #-}
128{-# RULES "toStreamK/fromStreamK fusion"
129 forall s. fromStreamK (toStreamK s) = s #-}
132------------------------------------------------------------------------------
134------------------------------------------------------------------------------
136-- | Map a monadic function over a 'Stream'
137{-# INLINE_NORMAL mapM #-}
138mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
139mapM f (Stream step state) = Stream step' state
141 {-# INLINE_LATE step' #-}
143 r <- step (adaptState gst) st
145 Yield x s -> f x >>= \a -> return $ Yield a s
146 Skip s -> return $ Skip s
150map :: Monad m => (a -> b) -> Stream m a -> Stream m b
151map f = mapM (return . f)
153instance Monad m => Functor (Stream m) where
157-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
158-- one shot operations, but helps immensely in operations composed of multiple
159-- combinators or the same combinator many times. There seems to be an
160-- opportunity to optimize here, can we get both, better perf for single ops
161-- as well as composed ops? Without SPEC, all single operation benchmarks
164-- The way we want a left fold to be strict, dually we want the right fold to
165-- be lazy. The correct signature of the fold function to keep it lazy must be
166-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
167-- earlier, which is incorrect. In the latter signature we have to feed the
168-- value to the fold function after evaluating the monadic action, depending on
169-- the bind behavior of the monad, the action may get evaluated immediately
170-- introducing unnecessary strictness to the fold. If the implementation is
171-- lazy the following example, must work:
173-- S.foldrM (\x t -> if x then return t else return False) (return True)
174-- (S.fromList [False,undefined] :: SerialT IO Bool)
176{-# INLINE_NORMAL foldrM #-}
177foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
178foldrM f z (Stream step state) = go SPEC state
181 r <- step defState st
183 Yield x s -> f x (go SPEC s)
187{-# INLINE_NORMAL foldrMx #-}
189 => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
190foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
193 r <- step defState st
195 Yield x s -> fstep x (go SPEC s)
199-- Note that foldr works on pure values, therefore it becomes necessarily
200-- strict when the monad m is strict. In that case it cannot terminate early,
201-- it would evaluate all of its input. Though, this should work fine with lazy
202-- monads. For example, if "any" is implemented using "foldr" instead of
203-- "foldrM" it performs the same with Identity monad but performs 1000x slower
206{-# INLINE_NORMAL foldr #-}
207foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
208foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
210-- Right fold to some transformer (T) monad. This can be useful to implement
211-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
213{-# INLINE_NORMAL foldrT #-}
214foldrT :: (Monad m, Monad (t m), MonadTrans t)
215 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
216foldrT f final (Stream step state) = go SPEC state
219 r <- lift $ step defState st
221 Yield x s -> f x (go SPEC s)
226toList :: Monad m => Stream m a -> m [a]
229-- XXX run begin action only if the stream is not empty.
230{-# INLINE_NORMAL foldlMx' #-}
231foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
232foldlMx' fstep begin done (Stream step state) =
233 begin >>= \x -> go SPEC x state
236 go !_ acc st = acc `seq` do
237 r <- step defState st
242 Skip s -> go SPEC acc s
245{-# INLINE foldlx' #-}
246foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
247foldlx' fstep begin done m =
248 foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
250-- XXX implement in terms of foldlMx'?
251{-# INLINE_NORMAL foldlM' #-}
252foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
253foldlM' fstep begin (Stream step state) = go SPEC begin state
255 go !_ acc st = acc `seq` do
256 r <- step defState st
261 Skip s -> go SPEC acc s
265foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
266foldl' fstep = foldlM' (\b a -> return (fstep b a))
268-- | Convert a list of pure values to a 'Stream'
269{-# INLINE_LATE fromList #-}
270fromList :: Monad m => [a] -> Stream m a
271fromList = Stream step
273 {-# INLINE_LATE step #-}
274 step _ (x:xs) = return $ Yield x xs
275 step _ [] = return Stop
277------------------------------------------------------------------------------
279------------------------------------------------------------------------------
281{-# INLINE_NORMAL eqBy #-}
282eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
283eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
285 eq_loop0 !_ s1 s2 = do
286 r <- step1 defState s1
288 Yield x s1' -> eq_loop1 SPEC x s1' s2
289 Skip s1' -> eq_loop0 SPEC s1' s2
292 eq_loop1 !_ x s1 s2 = do
293 r <- step2 defState s2
296 | eq x y -> eq_loop0 SPEC s1 s2'
297 | otherwise -> return False
298 Skip s2' -> eq_loop1 SPEC x s1 s2'
302 r <- step2 defState s2
304 Yield _ _ -> return False
305 Skip s2' -> eq_null s2'
308-- | Compare two streams lexicographically
309{-# INLINE_NORMAL cmpBy #-}
312 => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
313cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
315 cmp_loop0 !_ s1 s2 = do
316 r <- step1 defState s1
318 Yield x s1' -> cmp_loop1 SPEC x s1' s2
319 Skip s1' -> cmp_loop0 SPEC s1' s2
322 cmp_loop1 !_ x s1 s2 = do
323 r <- step2 defState s2
325 Yield y s2' -> case x `cmp` y of
326 EQ -> cmp_loop0 SPEC s1 s2'
328 Skip s2' -> cmp_loop1 SPEC x s1 s2'
332 r <- step2 defState s2
334 Yield _ _ -> return LT
335 Skip s2' -> cmp_null s2'
338{-# INLINE_NORMAL take #-}
339take :: Monad m => Int -> Stream m a -> Stream m a
340take n (Stream step state) = n `seq` Stream step' (state, 0)
342 {-# INLINE_LATE step' #-}
343 step' gst (st, i) | i < n = do
346 Yield x s -> Yield x (s, i + 1)
347 Skip s -> Skip (s, i)
349 step' _ (_, _) = return Stop
351------------------------------------------------------------------------------
353------------------------------------------------------------------------------
355{-# INLINE_LATE foldOneGroup #-}
361 -> State K.Stream m a
362 -> (State K.Stream m a -> s -> m (Step s a))
365foldOneGroup n (Fold fstep begin done) x gst step state = do
372 -- XXX is it strict enough?
373 go !_ st !acc i | i < n = do
378 go SPEC s acc1 (i + 1)
379 Skip s -> go SPEC s acc i
382 return (res, Nothing)
387-- groupsOf takes 11 sec to write the file from a stream, whereas just
388-- reading in the same file as a stream and folding each element of the stream
389-- using (+) takes just 1.5 sec, so we are still pretty slow (7x slow), there
390-- is scope to make it faster. There is a possibility of better fusion here.
392{-# INLINE_NORMAL groupsOf #-}
399groupsOf n f (Stream step state) =
400 n `seq` Stream stepOuter (Just state)
404 {-# INLINE_LATE stepOuter #-}
405 stepOuter gst (Just st) = do
406 -- We retrieve the first element of the stream before we start to fold
407 -- a chunk so that we do not return an empty chunk in case the stream
409 res <- step (adaptState gst) st
412 -- XXX how to make sure that stepInner and f get fused
413 -- This problem seems to be similar to the concatMap problem
414 (r, s1) <- foldOneGroup n f x (adaptState gst) step s
416 Skip s -> return $ Skip $ Just s
419 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