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)
61import Control.Applicative (liftA2)
62import Control.Monad.Trans (lift, MonadTrans)
63import GHC.Types (SPEC(..))
64import Prelude hiding (map, mapM, foldr, take)
66import Streamly.SVar (State(..), adaptState, defState)
67import Streamly.Fold.Types (Fold(..))
69import qualified Streamly.Streams.StreamK as K
71------------------------------------------------------------------------------
72-- The direct style stream type
73------------------------------------------------------------------------------
75-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
76-- the next state of the stream. 'Stop' indicates there are no more values in
78data Step s a = Yield a s | Skip s | Stop
80instance Functor (Step s) where
82 fmap f (Yield x s) = Yield (f x) s
83 fmap _ (Skip s) = Skip s
87-- | A stream consists of a step function that generates the next step given a
88-- current state, and the current state.
90 forall s. UnStream (State K.Stream m a -> s -> m (Step s a)) s
92unShare :: Stream m a -> Stream m a
93unShare (UnStream step state) = UnStream step' state
94 where step' gst = step (adaptState gst)
96pattern Stream :: (State K.Stream m a -> s -> m (Step s a)) -> s -> Stream m a
97pattern Stream step state <- (unShare -> UnStream step state)
98 where Stream = UnStream
100#if __GLASGOW_HASKELL__ >= 802
101{-# COMPLETE Stream #-}
104{-# INLINE_LATE fromStreamK #-}
105fromStreamK :: Monad m => K.Stream m a -> Stream m a
106fromStreamK = Stream step
109 let stop = return Stop
110 single a = return $ Yield a K.nil
111 yieldk a r = return $ Yield a r
112 in K.foldStreamShared gst yieldk single stop m1
114-- Convert a direct stream to and from CPS encoded stream
115{-# INLINE_LATE toStreamK #-}
116toStreamK :: Monad m => Stream m a -> K.Stream m a
117toStreamK (Stream step state) = go state
119 go st = K.mkStream $ \gst yld sng stp -> do
122 Yield x s -> yld x (go s)
123 Skip s -> K.foldStreamShared gst yld sng stp $ go s
126#ifndef DISABLE_FUSION
127{-# RULES "fromStreamK/toStreamK fusion"
128 forall s. toStreamK (fromStreamK s) = s #-}
129{-# RULES "toStreamK/fromStreamK fusion"
130 forall s. fromStreamK (toStreamK s) = s #-}
133------------------------------------------------------------------------------
135------------------------------------------------------------------------------
137-- | Map a monadic function over a 'Stream'
138{-# INLINE_NORMAL mapM #-}
139mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
140mapM f (Stream step state) = Stream step' state
142 {-# INLINE_LATE step' #-}
144 r <- step (adaptState gst) st
146 Yield x s -> f x >>= \a -> return $ Yield a s
147 Skip s -> return $ Skip s
151map :: Monad m => (a -> b) -> Stream m a -> Stream m b
152map f = mapM (return . f)
154instance Monad m => Functor (Stream m) where
158-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
159-- one shot operations, but helps immensely in operations composed of multiple
160-- combinators or the same combinator many times. There seems to be an
161-- opportunity to optimize here, can we get both, better perf for single ops
162-- as well as composed ops? Without SPEC, all single operation benchmarks
165-- The way we want a left fold to be strict, dually we want the right fold to
166-- be lazy. The correct signature of the fold function to keep it lazy must be
167-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
168-- earlier, which is incorrect. In the latter signature we have to feed the
169-- value to the fold function after evaluating the monadic action, depending on
170-- the bind behavior of the monad, the action may get evaluated immediately
171-- introducing unnecessary strictness to the fold. If the implementation is
172-- lazy the following example, must work:
174-- S.foldrM (\x t -> if x then return t else return False) (return True)
175-- (S.fromList [False,undefined] :: SerialT IO Bool)
177{-# INLINE_NORMAL foldrM #-}
178foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
179foldrM f z (Stream step state) = go SPEC state
182 r <- step defState st
184 Yield x s -> f x (go SPEC s)
188{-# INLINE_NORMAL foldrMx #-}
190 => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
191foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
194 r <- step defState st
196 Yield x s -> fstep x (go SPEC s)
200-- Note that foldr works on pure values, therefore it becomes necessarily
201-- strict when the monad m is strict. In that case it cannot terminate early,
202-- it would evaluate all of its input. Though, this should work fine with lazy
203-- monads. For example, if "any" is implemented using "foldr" instead of
204-- "foldrM" it performs the same with Identity monad but performs 1000x slower
207{-# INLINE_NORMAL foldr #-}
208foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
209foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
211-- Right fold to some transformer (T) monad. This can be useful to implement
212-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
214{-# INLINE_NORMAL foldrT #-}
215foldrT :: (Monad m, Monad (t m), MonadTrans t)
216 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
217foldrT f final (Stream step state) = go SPEC state
220 r <- lift $ step defState st
222 Yield x s -> f x (go SPEC s)
227toList :: Monad m => Stream m a -> m [a]
230-- XXX run begin action only if the stream is not empty.
231{-# INLINE_NORMAL foldlMx' #-}
232foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b
233foldlMx' fstep begin done (Stream step state) =
234 begin >>= \x -> go SPEC x state
237 go !_ acc st = acc `seq` do
238 r <- step defState st
243 Skip s -> go SPEC acc s
246{-# INLINE foldlx' #-}
247foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b
248foldlx' fstep begin done m =
249 foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done) m
251-- XXX implement in terms of foldlMx'?
252{-# INLINE_NORMAL foldlM' #-}
253foldlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> m b
254foldlM' fstep begin (Stream step state) = go SPEC begin state
256 go !_ acc st = acc `seq` do
257 r <- step defState st
262 Skip s -> go SPEC acc s
266foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b
267foldl' fstep = foldlM' (\b a -> return (fstep b a))
269-- | Convert a list of pure values to a 'Stream'
270{-# INLINE_LATE fromList #-}
271fromList :: Monad m => [a] -> Stream m a
272fromList = Stream step
274 {-# INLINE_LATE step #-}
275 step _ (x:xs) = return $ Yield x xs
276 step _ [] = return Stop
278------------------------------------------------------------------------------
280------------------------------------------------------------------------------
282{-# INLINE_NORMAL eqBy #-}
283eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool
284eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2
286 eq_loop0 !_ s1 s2 = do
287 r <- step1 defState s1
289 Yield x s1' -> eq_loop1 SPEC x s1' s2
290 Skip s1' -> eq_loop0 SPEC s1' s2
293 eq_loop1 !_ x s1 s2 = do
294 r <- step2 defState s2
297 | eq x y -> eq_loop0 SPEC s1 s2'
298 | otherwise -> return False
299 Skip s2' -> eq_loop1 SPEC x s1 s2'
303 r <- step2 defState s2
305 Yield _ _ -> return False
306 Skip s2' -> eq_null s2'
309-- | Compare two streams lexicographically
310{-# INLINE_NORMAL cmpBy #-}
313 => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering
314cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2
316 cmp_loop0 !_ s1 s2 = do
317 r <- step1 defState s1
319 Yield x s1' -> cmp_loop1 SPEC x s1' s2
320 Skip s1' -> cmp_loop0 SPEC s1' s2
323 cmp_loop1 !_ x s1 s2 = do
324 r <- step2 defState s2
326 Yield y s2' -> case x `cmp` y of
327 EQ -> cmp_loop0 SPEC s1 s2'
329 Skip s2' -> cmp_loop1 SPEC x s1 s2'
333 r <- step2 defState s2
335 Yield _ _ -> return LT
336 Skip s2' -> cmp_null s2'
339{-# INLINE_NORMAL take #-}
340take :: Monad m => Int -> Stream m a -> Stream m a
341take n (Stream step state) = n `seq` Stream step' (state, 0)
343 {-# INLINE_LATE step' #-}
344 step' gst (st, i) | i < n = do
347 Yield x s -> Yield x (s, i + 1)
348 Skip s -> Skip (s, i)
350 step' _ (_, _) = return Stop
352------------------------------------------------------------------------------
354------------------------------------------------------------------------------
356{-# INLINE_LATE foldOneGroup #-}
362 -> State K.Stream m a
363 -> (State K.Stream m a -> s -> m (Step s a))
366foldOneGroup n (Fold fstep begin done) x gst step state = do
373 -- XXX is it strict enough?
374 go !_ st !acc i | i < n = do
379 go SPEC s acc1 (i + 1)
380 Skip s -> go SPEC s acc i
383 return (res, Nothing)
388-- groupsOf takes 11 sec to write the file from a stream, whereas just
389-- reading in the same file as a stream and folding each element of the stream
390-- using (+) takes just 1.5 sec, so we are still pretty slow (7x slow), there
391-- is scope to make it faster. There is a possibility of better fusion here.
393{-# INLINE_NORMAL groupsOf #-}
400groupsOf n f (Stream step state) =
401 n `seq` Stream stepOuter (Just state)
405 {-# INLINE_LATE stepOuter #-}
406 stepOuter gst (Just st) = do
407 -- We retrieve the first element of the stream before we start to fold
408 -- a chunk so that we do not return an empty chunk in case the stream
410 res <- step (adaptState gst) st
413 -- XXX how to make sure that stepInner and f get fused
414 -- This problem seems to be similar to the concatMap problem
415 (r, s1) <- foldOneGroup n f x (adaptState gst) step s
417 Skip s -> return $ Skip $ Just s
420 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