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)
45import Control.Applicative (liftA2)
46import Control.Monad.Trans (lift, MonadTrans)
47import GHC.Types (SPEC(..))
48import Prelude hiding (map, mapM, foldr)
49import Streamly.SVar (State(..), adaptState, defState)
51import qualified Streamly.Streams.StreamK as K
53------------------------------------------------------------------------------
54-- The direct style stream type
55------------------------------------------------------------------------------
57-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and
58-- the next state of the stream. 'Stop' indicates there are no more values in
60data Step s a = Yield a s | Skip s | Stop
62instance Functor (Step s) where
64 fmap f (Yield x s) = Yield (f x) s
65 fmap _ (Skip s) = Skip s
69-- | A stream consists of a step function that generates the next step given a
70-- current state, and the current state.
72 forall s. UnStream (State K.Stream m a -> s -> m (Step s a)) s
74unShare :: Stream m a -> Stream m a
75unShare (UnStream step state) = UnStream step' state
76 where step' gst = step (adaptState gst)
78pattern Stream :: (State K.Stream m a -> s -> m (Step s a)) -> s -> Stream m a
79pattern Stream step state <- (unShare -> UnStream step state)
80 where Stream = UnStream
82#if __GLASGOW_HASKELL__ >= 802
83{-# COMPLETE Stream #-}
86------------------------------------------------------------------------------
88------------------------------------------------------------------------------
90-- | Map a monadic function over a 'Stream'
91{-# INLINE_NORMAL mapM #-}
92mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b
93mapM f (Stream step state) = Stream step' state
95 {-# INLINE_LATE step' #-}
97 r <- step (adaptState gst) st
99 Yield x s -> f x >>= \a -> return $ Yield a s
100 Skip s -> return $ Skip s
104map :: Monad m => (a -> b) -> Stream m a -> Stream m b
105map f = mapM (return . f)
107instance Monad m => Functor (Stream m) where
111-- XXX Use of SPEC constructor in folds causes 2x performance degradation in
112-- one shot operations, but helps immensely in operations composed of multiple
113-- combinators or the same combinator many times. There seems to be an
114-- opportunity to optimize here, can we get both, better perf for single ops
115-- as well as composed ops? Without SPEC, all single operation benchmarks
118-- The way we want a left fold to be strict, dually we want the right fold to
119-- be lazy. The correct signature of the fold function to keep it lazy must be
120-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter
121-- earlier, which is incorrect. In the latter signature we have to feed the
122-- value to the fold function after evaluating the monadic action, depending on
123-- the bind behavior of the monad, the action may get evaluated immediately
124-- introducing unnecessary strictness to the fold. If the implementation is
125-- lazy the following example, must work:
127-- S.foldrM (\x t -> if x then return t else return False) (return True)
128-- (S.fromList [False,undefined] :: SerialT IO Bool)
130{-# INLINE_NORMAL foldrM #-}
131foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
132foldrM f z (Stream step state) = go SPEC state
135 r <- step defState st
137 Yield x s -> f x (go SPEC s)
141{-# INLINE_NORMAL foldrMx #-}
143 => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b
144foldrMx fstep final convert (Stream step state) = convert $ go SPEC state
147 r <- step defState st
149 Yield x s -> fstep x (go SPEC s)
153-- Note that foldr works on pure values, therefore it becomes necessarily
154-- strict when the monad m is strict. In that case it cannot terminate early,
155-- it would evaluate all of its input. Though, this should work fine with lazy
156-- monads. For example, if "any" is implemented using "foldr" instead of
157-- "foldrM" it performs the same with Identity monad but performs 1000x slower
160{-# INLINE_NORMAL foldr #-}
161foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b
162foldr f z = foldrM (\a b -> liftA2 f (return a) b) (return z)
164-- Right fold to some transformer (T) monad. This can be useful to implement
165-- stateless combinators like map, filtering, insertions, takeWhile, dropWhile.
167{-# INLINE_NORMAL foldrT #-}
168foldrT :: (Monad m, Monad (t m), MonadTrans t)
169 => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b
170foldrT f final (Stream step state) = go SPEC state
173 r <- lift $ step defState st
175 Yield x s -> f x (go SPEC s)
180toList :: Monad m => Stream m a -> m [a]
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