{-# LANGUAGE BangPatterns #-} {-# LANGUAGE CPP #-} {-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ExistentialQuantification #-} {-# LANGUAGE FlexibleContexts #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE PatternSynonyms #-} {-# LANGUAGE RecordWildCards #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE ViewPatterns #-} {-# LANGUAGE RankNTypes #-} #include "inline.hs" -- | -- Module : Streamly.Streams.StreamD -- Copyright : (c) 2018 Harendra Kumar -- Copyright : (c) Roman Leshchinskiy 2008-2010 -- -- License : BSD3 -- Maintainer : harendra.kumar@gmail.com -- Stability : experimental -- Portability : GHC -- -- Direct style re-implementation of CPS style stream in StreamK module. The -- symbol or suffix 'D' in this module denotes the "Direct" style. GHC is able -- to INLINE and fuse direct style better, providing better performance than -- CPS implementation. -- -- @ -- import qualified Streamly.Streams.StreamD as D -- @ -- Some of the functions in this file have been adapted from the vector -- library, https://hackage.haskell.org/package/vector. module Streamly.Streams.StreamD ( -- * The stream type Step (..) , Stream (..) -- * Construction , nil , cons -- * Deconstruction , uncons -- * Generation -- ** Unfolds , unfoldr , unfoldrM -- ** Specialized Generation -- | Generate a monadic stream from a seed. , repeat , replicate , replicateM , fromIndices , fromIndicesM , generate , generateM -- ** Enumerations , enumerateFromStepIntegral , enumerateFromIntegral , enumerateFromThenIntegral , enumerateFromToIntegral , enumerateFromThenToIntegral , enumerateFromStepNum , numFrom , numFromThen , enumerateFromToFractional , enumerateFromThenToFractional -- ** Conversions -- | Transform an input structure into a stream. -- | Direct style stream does not support @fromFoldable@. , yield , yieldM , fromList , fromListM , fromStreamK , fromStreamD -- * Elimination -- ** General Folds , foldrS , foldrT , foldrM , foldrMx , foldr , foldr1 , foldl' , foldlM' , foldlS , foldlT , reverse , reverse' , foldlx' , foldlMx' -- ** Specialized Folds , tap , drain , null , head , tail , last , elem , notElem , all , any , maximum , maximumBy , minimum , minimumBy , findIndices , lookup , findM , find , (!!) -- ** Flattening nested streams , concatMapM , concatMap -- ** Grouping , groupsOf , groupsBy , groupsRollingBy -- ** Splitting , splitBy , splitSuffixBy , wordsBy , splitSuffixBy' , splitOn , splitSuffixOn -- ** Substreams , isPrefixOf , isSubsequenceOf , stripPrefix -- ** Map and Fold , mapM_ -- ** Conversions -- | Transform a stream into another type. , toList , toListRev , toStreamK , toStreamD -- * Transformation -- ** By folding (scans) , scanlM' , scanl' , scanlM , scanl , scanl1M' , scanl1' , scanl1M , scanl1 , prescanl' , prescanlM' , postscanl , postscanlM , postscanl' , postscanlM' , postscanlx' , postscanlMx' , scanlMx' , scanlx' -- * Filtering , filter , filterM , uniq , take , takeWhile , takeWhileM , drop , dropWhile , dropWhileM -- * Mapping , map , mapM , sequence -- * Inserting , insertBy -- * Deleting , deleteBy -- ** Map and Filter , mapMaybe , mapMaybeM -- * Zipping , indexed , indexedR , zipWith , zipWithM -- * Comparisons , eqBy , cmpBy -- * Merging , mergeBy , mergeByM -- * Transformation comprehensions , the -- * Exceptions , before , after , bracket , onException , finally , handle ) where import Control.Exception (Exception) import Control.Monad.Catch (MonadCatch) import Control.Monad.IO.Class (MonadIO(..)) import Control.Monad.Trans (MonadTrans(lift)) import Data.Word (Word32) import Data.Bits (shiftR, shiftL, (.|.), (.&.)) import Data.Maybe (fromJust, isJust) import Foreign.Ptr (Ptr) import Foreign.Storable (Storable(..)) import GHC.Types (SPEC(..)) import Prelude hiding (map, mapM, mapM_, repeat, foldr, last, take, filter, takeWhile, drop, dropWhile, all, any, maximum, minimum, elem, notElem, null, head, tail, zipWith, lookup, foldr1, sequence, (!!), scanl, scanl1, concatMap, replicate, enumFromTo, concat, reverse) import qualified Control.Monad.Catch as MC import Streamly.Mem.Array.Types (Array(..)) import Streamly.Fold.Types (Fold(..)) import Streamly.SVar (MonadAsync, defState, adaptState) import Streamly.Sink.Types (Sink(..)) import Streamly.Streams.StreamD.Type import qualified Streamly.Mem.Array.Types as A import qualified Streamly.Mem.Ring as RB import qualified Streamly.Streams.StreamK as K ------------------------------------------------------------------------------ -- Construction ------------------------------------------------------------------------------ -- | An empty 'Stream'. {-# INLINE_NORMAL nil #-} nil :: Monad m => Stream m a nil = Stream (\_ _ -> return Stop) () {-# INLINE_NORMAL consM #-} consM :: Monad m => m a -> Stream m a -> Stream m a consM m (Stream step state) = Stream step1 Nothing where {-# INLINE_LATE step1 #-} step1 _ Nothing = m >>= \x -> return $ Yield x (Just state) step1 gst (Just st) = do r <- step gst st return $ case r of Yield a s -> Yield a (Just s) Skip s -> Skip (Just s) Stop -> Stop -- XXX implement in terms of consM? -- cons x = consM (return x) -- -- | Can fuse but has O(n^2) complexity. {-# INLINE_NORMAL cons #-} cons :: Monad m => a -> Stream m a -> Stream m a cons x (Stream step state) = Stream step1 Nothing where {-# INLINE_LATE step1 #-} step1 _ Nothing = return $ Yield x (Just state) step1 gst (Just st) = do r <- step gst st return $ case r of Yield a s -> Yield a (Just s) Skip s -> Skip (Just s) Stop -> Stop ------------------------------------------------------------------------------- -- Deconstruction ------------------------------------------------------------------------------- -- Does not fuse, has the same performance as the StreamK version. {-# INLINE_NORMAL uncons #-} uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a)) uncons (UnStream step state) = go state where go st = do r <- step defState st case r of Yield x s -> return $ Just (x, Stream step s) Skip s -> go s Stop -> return Nothing ------------------------------------------------------------------------------ -- Generation by unfold ------------------------------------------------------------------------------ {-# INLINE_NORMAL unfoldrM #-} unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a unfoldrM next state = Stream step state where {-# INLINE_LATE step #-} step _ st = do r <- next st return $ case r of Just (x, s) -> Yield x s Nothing -> Stop {-# INLINE_LATE unfoldr #-} unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a unfoldr f = unfoldrM (return . f) ------------------------------------------------------------------------------ -- Specialized Generation ------------------------------------------------------------------------------ repeat :: Monad m => a -> Stream m a repeat x = Stream (\_ _ -> return $ Yield x ()) () {-# INLINE_NORMAL replicateM #-} replicateM :: Monad m => Int -> m a -> Stream m a replicateM n p = Stream step n where {-# INLINE_LATE step #-} step _ i | i <= 0 = return Stop | otherwise = do x <- p return $ Yield x (i - 1) {-# INLINE_NORMAL replicate #-} replicate :: Monad m => Int -> a -> Stream m a replicate n x = replicateM n (return x) -- This would not work properly for floats, therefore we put an Integral -- constraint. -- | Can be used to enumerate unbounded integrals. This does not check for -- overflow or underflow for bounded integrals. {-# INLINE_NORMAL enumerateFromStepIntegral #-} enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a enumerateFromStepIntegral from stride = from `seq` stride `seq` Stream step from where {-# INLINE_LATE step #-} step _ !x = return $ Yield x $! (x + stride) -- We are assuming that "to" is constrained by the type to be within -- max/min bounds. {-# INLINE enumerateFromToIntegral #-} enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a enumerateFromToIntegral from to = takeWhile (<= to) $ enumerateFromStepIntegral from 1 {-# INLINE enumerateFromIntegral #-} enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a enumerateFromIntegral from = enumerateFromToIntegral from maxBound data EnumState a = EnumInit | EnumYield a a a | EnumStop {-# INLINE_NORMAL enumerateFromThenToIntegralUp #-} enumerateFromThenToIntegralUp :: (Monad m, Integral a) => a -> a -> a -> Stream m a enumerateFromThenToIntegralUp from next to = Stream step EnumInit where {-# INLINE_LATE step #-} step _ EnumInit = return $ if to < next then if to < from then Stop else Yield from EnumStop else -- from <= next <= to let stride = next - from in Skip $ EnumYield from stride (to - stride) step _ (EnumYield x stride toMinus) = return $ if x > toMinus then Yield x EnumStop else Yield x $ EnumYield (x + stride) stride toMinus step _ EnumStop = return Stop {-# INLINE_NORMAL enumerateFromThenToIntegralDn #-} enumerateFromThenToIntegralDn :: (Monad m, Integral a) => a -> a -> a -> Stream m a enumerateFromThenToIntegralDn from next to = Stream step EnumInit where {-# INLINE_LATE step #-} step _ EnumInit = return $ if to > next then if to > from then Stop else Yield from EnumStop else -- from >= next >= to let stride = next - from in Skip $ EnumYield from stride (to - stride) step _ (EnumYield x stride toMinus) = return $ if x < toMinus then Yield x EnumStop else Yield x $ EnumYield (x + stride) stride toMinus step _ EnumStop = return Stop {-# INLINE_NORMAL enumerateFromThenToIntegral #-} enumerateFromThenToIntegral :: (Monad m, Integral a) => a -> a -> a -> Stream m a enumerateFromThenToIntegral from next to | next >= from = enumerateFromThenToIntegralUp from next to | otherwise = enumerateFromThenToIntegralDn from next to {-# INLINE_NORMAL enumerateFromThenIntegral #-} enumerateFromThenIntegral :: (Monad m, Integral a, Bounded a) => a -> a -> Stream m a enumerateFromThenIntegral from next = if next > from then enumerateFromThenToIntegralUp from next maxBound else enumerateFromThenToIntegralDn from next minBound -- For floating point numbers if the increment is less than the precision then -- it just gets lost. Therefore we cannot always increment it correctly by just -- repeated addition. -- 9007199254740992 + 1 + 1 :: Double => 9.007199254740992e15 -- 9007199254740992 + 2 :: Double => 9.007199254740994e15 -- Instead we accumulate the increment counter and compute the increment -- everytime before adding it to the starting number. -- -- This works for Integrals as well as floating point numbers, but -- enumerateFromStepIntegral is faster for integrals. {-# INLINE_NORMAL enumerateFromStepNum #-} enumerateFromStepNum :: (Monad m, Num a) => a -> a -> Stream m a enumerateFromStepNum from stride = Stream step 0 where {-# INLINE_LATE step #-} step _ !i = return $ (Yield $! (from + i * stride)) $! (i + 1) {-# INLINE_NORMAL numFrom #-} numFrom :: (Monad m, Num a) => a -> Stream m a numFrom from = enumerateFromStepNum from 1 {-# INLINE_NORMAL numFromThen #-} numFromThen :: (Monad m, Num a) => a -> a -> Stream m a numFromThen from next = enumerateFromStepNum from (next - from) -- We cannot write a general function for Num. The only way to write code -- portable between the two is to use a 'Real' constraint and convert between -- Fractional and Integral using fromRational which is horribly slow. {-# INLINE_NORMAL enumerateFromToFractional #-} enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> Stream m a enumerateFromToFractional from to = takeWhile (<= to + 1 / 2) $ enumerateFromStepNum from 1 {-# INLINE_NORMAL enumerateFromThenToFractional #-} enumerateFromThenToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> a -> Stream m a enumerateFromThenToFractional from next to = takeWhile predicate $ numFromThen from next where mid = (next - from) / 2 predicate | next >= from = (<= to + mid) | otherwise = (>= to + mid) ------------------------------------------------------------------------------- -- Generation by Conversion ------------------------------------------------------------------------------- {-# INLINE_NORMAL fromIndicesM #-} fromIndicesM :: Monad m => (Int -> m a) -> Stream m a fromIndicesM gen = Stream step 0 where {-# INLINE_LATE step #-} step _ i = do x <- gen i return $ Yield x (i + 1) {-# INLINE fromIndices #-} fromIndices :: Monad m => (Int -> a) -> Stream m a fromIndices gen = fromIndicesM (return . gen) {-# INLINE_NORMAL generateM #-} generateM :: Monad m => Int -> (Int -> m a) -> Stream m a generateM n gen = n `seq` Stream step 0 where {-# INLINE_LATE step #-} step _ i | i < n = do x <- gen i return $ Yield x (i + 1) | otherwise = return Stop {-# INLINE generate #-} generate :: Monad m => Int -> (Int -> a) -> Stream m a generate n gen = generateM n (return . gen) -- XXX we need the MonadAsync constraint because of a rewrite rule. -- | Convert a list of monadic actions to a 'Stream' {-# INLINE_LATE fromListM #-} fromListM :: MonadAsync m => [m a] -> Stream m a fromListM = Stream step where {-# INLINE_LATE step #-} step _ (m:ms) = m >>= \x -> return $ Yield x ms step _ [] = return Stop {-# INLINE toStreamD #-} toStreamD :: (K.IsStream t, Monad m) => t m a -> Stream m a toStreamD = fromStreamK . K.toStream ------------------------------------------------------------------------------ -- Elimination by Folds ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- Right Folds ------------------------------------------------------------------------------ {-# INLINE_NORMAL foldr1 #-} foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a) foldr1 f m = do r <- uncons m case r of Nothing -> return Nothing Just (h, t) -> fmap Just (foldr f h t) ------------------------------------------------------------------------------ -- Left Folds ------------------------------------------------------------------------------ {-# INLINE_NORMAL foldlT #-} foldlT :: (Monad m, Monad (s m), MonadTrans s) => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b foldlT fstep begin (Stream step state) = go SPEC begin state where go !_ acc st = do r <- lift $ step defState st case r of Yield x s -> go SPEC (fstep acc x) s Skip s -> go SPEC acc s Stop -> acc -- Note, this is going to have horrible performance, because of the nature of -- the stream type (i.e. direct stream vs CPS). Its only for reference, it is -- likely be practically unusable. {-# INLINE_NORMAL foldlS #-} foldlS :: Monad m => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin)) where step' gst (Left (st, acc)) = do r <- step (adaptState gst) st return $ case r of Yield x s -> Skip (Left (s, fstep acc x)) Skip s -> Skip (Left (s, acc)) Stop -> Skip (Right acc) step' gst (Right (Stream stp stt)) = do r <- stp (adaptState gst) stt return $ case r of Yield x s -> Yield x (Right (Stream stp s)) Skip s -> Skip (Right (Stream stp s)) Stop -> Stop ------------------------------------------------------------------------------ -- Specialized Folds ------------------------------------------------------------------------------ -- | Run a streaming composition, discard the results. {-# INLINE_LATE drain #-} drain :: Monad m => Stream m a -> m () -- drain m = foldrM (\x xs -> x `seq` xs) (return ()) m drain (Stream step state) = go SPEC state where go !_ st = do r <- step defState st case r of Yield _ s -> go SPEC s Skip s -> go SPEC s Stop -> return () {-# INLINE_NORMAL null #-} null :: Monad m => Stream m a -> m Bool null m = foldrM (\_ _ -> return False) (return True) m -- XXX SPEC? {-# INLINE_NORMAL head #-} head :: Monad m => Stream m a -> m (Maybe a) head m = foldrM (\x _ -> return (Just x)) (return Nothing) m -- Does not fuse, has the same performance as the StreamK version. {-# INLINE_NORMAL tail #-} tail :: Monad m => Stream m a -> m (Maybe (Stream m a)) tail (UnStream step state) = go state where go st = do r <- step defState st case r of Yield _ s -> return (Just $ Stream step s) Skip s -> go s Stop -> return Nothing -- XXX will it fuse? need custom impl? {-# INLINE_NORMAL last #-} last :: Monad m => Stream m a -> m (Maybe a) last = foldl' (\_ y -> Just y) Nothing {-# INLINE_NORMAL elem #-} elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool -- elem e m = foldrM (\x xs -> if x == e then return True else xs) (return False) m elem e (Stream step state) = go state where go st = do r <- step defState st case r of Yield x s | x == e -> return True | otherwise -> go s Skip s -> go s Stop -> return False {-# INLINE_NORMAL notElem #-} notElem :: (Monad m, Eq a) => a -> Stream m a -> m Bool notElem e s = fmap not (elem e s) {-# INLINE_NORMAL all #-} all :: Monad m => (a -> Bool) -> Stream m a -> m Bool -- all p m = foldrM (\x xs -> if p x then xs else return False) (return True) m all p (Stream step state) = go state where go st = do r <- step defState st case r of Yield x s | p x -> go s | otherwise -> return False Skip s -> go s Stop -> return True {-# INLINE_NORMAL any #-} any :: Monad m => (a -> Bool) -> Stream m a -> m Bool -- any p m = foldrM (\x xs -> if p x then return True else xs) (return False) m any p (Stream step state) = go state where go st = do r <- step defState st case r of Yield x s | p x -> return True | otherwise -> go s Skip s -> go s Stop -> return False {-# INLINE_NORMAL maximum #-} maximum :: (Monad m, Ord a) => Stream m a -> m (Maybe a) maximum (Stream step state) = go Nothing state where go Nothing st = do r <- step defState st case r of Yield x s -> go (Just x) s Skip s -> go Nothing s Stop -> return Nothing go (Just acc) st = do r <- step defState st case r of Yield x s | acc <= x -> go (Just x) s | otherwise -> go (Just acc) s Skip s -> go (Just acc) s Stop -> return (Just acc) {-# INLINE_NORMAL maximumBy #-} maximumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a) maximumBy cmp (Stream step state) = go Nothing state where go Nothing st = do r <- step defState st case r of Yield x s -> go (Just x) s Skip s -> go Nothing s Stop -> return Nothing go (Just acc) st = do r <- step defState st case r of Yield x s -> case cmp acc x of GT -> go (Just acc) s _ -> go (Just x) s Skip s -> go (Just acc) s Stop -> return (Just acc) {-# INLINE_NORMAL minimum #-} minimum :: (Monad m, Ord a) => Stream m a -> m (Maybe a) minimum (Stream step state) = go Nothing state where go Nothing st = do r <- step defState st case r of Yield x s -> go (Just x) s Skip s -> go Nothing s Stop -> return Nothing go (Just acc) st = do r <- step defState st case r of Yield x s | acc <= x -> go (Just acc) s | otherwise -> go (Just x) s Skip s -> go (Just acc) s Stop -> return (Just acc) {-# INLINE_NORMAL minimumBy #-} minimumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a) minimumBy cmp (Stream step state) = go Nothing state where go Nothing st = do r <- step defState st case r of Yield x s -> go (Just x) s Skip s -> go Nothing s Stop -> return Nothing go (Just acc) st = do r <- step defState st case r of Yield x s -> case cmp acc x of GT -> go (Just x) s _ -> go (Just acc) s Skip s -> go (Just acc) s Stop -> return (Just acc) {-# INLINE_NORMAL (!!) #-} (!!) :: (Monad m) => Stream m a -> Int -> m (Maybe a) (Stream step state) !! i = go i state where go n st = do r <- step defState st case r of Yield x s | n < 0 -> return Nothing | n == 0 -> return $ Just x | otherwise -> go (n - 1) s Skip s -> go n s Stop -> return Nothing {-# INLINE_NORMAL lookup #-} lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b) lookup e m = foldrM (\(a, b) xs -> if e == a then return (Just b) else xs) (return Nothing) m {-# INLINE_NORMAL findM #-} findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a) findM p m = foldrM (\x xs -> p x >>= \r -> if r then return (Just x) else xs) (return Nothing) m {-# INLINE find #-} find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a) find p = findM (return . p) {-# INLINE_NORMAL findIndices #-} findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int findIndices p (Stream step state) = Stream step' (state, 0) where {-# INLINE_LATE step' #-} step' gst (st, i) = do r <- step (adaptState gst) st return $ case r of Yield x s -> if p x then Yield i (s, i+1) else Skip (s, i+1) Skip s -> Skip (s, i+1) Stop -> Stop {-# INLINE toListRev #-} toListRev :: Monad m => Stream m a -> m [a] toListRev = foldl' (flip (:)) [] -- We can implement reverse as: -- -- > reverse = foldlS (flip cons) nil -- -- However, this implementation is unusable because of the horrible performance -- of cons. So we just convert it to a list first and then stream from the -- list. -- -- XXX Maybe we can use an Array instead of a list here? {-# INLINE_NORMAL reverse #-} reverse :: Monad m => Stream m a -> Stream m a reverse m = Stream step Nothing where {-# INLINE_LATE step #-} step _ Nothing = do xs <- toListRev m return $ Skip (Just xs) step _ (Just (x:xs)) = return $ Yield x (Just xs) step _ (Just []) = return Stop -- Much faster reverse for Storables {-# INLINE_NORMAL reverse' #-} reverse' :: forall m a. (MonadIO m, Storable a) => Stream m a -> Stream m a {- -- This commented implementation copies the whole stream into one single array -- and then streams from that array, this is 3-4x faster than the chunked code -- that follows. Though this could be problematic due to unbounded large -- allocations. We need to figure out why the chunked code is slower and if we -- can optimize the chunked code to work as fast as this one. It may be a -- fusion issue? import Foreign.ForeignPtr (touchForeignPtr) import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr) import Foreign.Ptr (Ptr, plusPtr) reverse' m = Stream step Nothing where {-# INLINE_LATE step #-} step _ Nothing = do arr <- A.fromStreamD m let p = aEnd arr `plusPtr` negate (sizeOf (undefined :: a)) return $ Skip $ Just (aStart arr, p) step _ (Just (start, p)) | p < unsafeForeignPtrToPtr start = return Stop step _ (Just (start, p)) = do let !x = A.unsafeInlineIO $ do r <- peek p touchForeignPtr start return r next = p `plusPtr` negate (sizeOf (undefined :: a)) return $ Yield x (Just (start, next)) -} reverse' m = A.flattenArraysRev $ fromStreamK $ K.reverse $ toStreamK $ A.fromStreamDArraysOf A.defaultChunkSize m ------------------------------------------------------------------------------ -- Grouping/Splitting ------------------------------------------------------------------------------ {-# INLINE_NORMAL splitSuffixBy' #-} splitSuffixBy' :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b splitSuffixBy' predicate f (Stream step state) = Stream (stepOuter f) (Just state) where {-# INLINE_LATE stepOuter #-} stepOuter (Fold fstep initial done) gst (Just st) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial acc' <- fstep acc x if (predicate x) then done acc' >>= \val -> return $ Yield val (Just s) else go SPEC s acc' Skip s -> return $ Skip $ Just s Stop -> return Stop where go !_ stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do acc' <- fstep acc x if (predicate x) then done acc' >>= \val -> return $ Yield val (Just s) else go SPEC s acc' Skip s -> go SPEC s acc Stop -> done acc >>= \val -> return $ Yield val Nothing stepOuter _ _ Nothing = return Stop {-# INLINE_NORMAL groupsBy #-} groupsBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b groupsBy cmp f (Stream step state) = Stream (stepOuter f) (Just state, Nothing) where {-# INLINE_LATE stepOuter #-} stepOuter (Fold fstep initial done) gst (Just st, Nothing) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial acc' <- fstep acc x go SPEC x s acc' Skip s -> return $ Skip $ (Just s, Nothing) Stop -> return Stop where go !_ prev stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if cmp x prev then do acc' <- fstep acc x go SPEC prev s acc' else done acc >>= \r -> return $ Yield r (Just s, Just x) Skip s -> go SPEC prev s acc Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing) stepOuter (Fold fstep initial done) gst (Just st, Just prev) = do acc <- initial acc' <- fstep acc prev go SPEC st acc' where -- XXX code duplicated from the previous equation go !_ stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if cmp x prev then do acc' <- fstep acc x go SPEC s acc' else done acc >>= \r -> return $ Yield r (Just s, Just x) Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing) stepOuter _ _ (Nothing,_) = return Stop {-# INLINE_NORMAL groupsRollingBy #-} groupsRollingBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b groupsRollingBy cmp f (Stream step state) = Stream (stepOuter f) (Just state, Nothing) where {-# INLINE_LATE stepOuter #-} stepOuter (Fold fstep initial done) gst (Just st, Nothing) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial acc' <- fstep acc x go SPEC x s acc' Skip s -> return $ Skip $ (Just s, Nothing) Stop -> return Stop where go !_ prev stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if cmp prev x then do acc' <- fstep acc x go SPEC x s acc' else done acc >>= \r -> return $ Yield r (Just s, Just x) Skip s -> go SPEC prev s acc Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing) stepOuter (Fold fstep initial done) gst (Just st, Just prev') = do acc <- initial acc' <- fstep acc prev' go SPEC prev' st acc' where go !_ prevv stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if cmp prevv x then do acc' <- fstep acc x go SPEC x s acc' else done acc >>= \r -> return $ Yield r (Just s, Just x) Skip s -> go SPEC prevv s acc Stop -> done acc >>= \r -> return $ Yield r (Nothing, Nothing) stepOuter _ _ (Nothing, _) = return Stop {-# INLINE_NORMAL splitBy #-} splitBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b splitBy predicate f (Stream step state) = Stream (step' f) (Just state) where {-# INLINE_LATE step' #-} step' (Fold fstep initial done) gst (Just st) = initial >>= go SPEC st where go !_ stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if predicate x then done acc >>= \r -> return $ Yield r (Just s) else do acc' <- fstep acc x go SPEC s acc' Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r Nothing step' _ _ Nothing = return Stop {-# INLINE_NORMAL splitSuffixBy #-} splitSuffixBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b splitSuffixBy predicate f (Stream step state) = Stream (step' f) (Just state) where {-# INLINE_LATE step' #-} step' (Fold fstep initial done) gst (Just st) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial if predicate x then done acc >>= \val -> return $ Yield val (Just s) else do acc' <- fstep acc x go SPEC s acc' Skip s -> return $ Skip $ Just s Stop -> return Stop where go !_ stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if predicate x then done acc >>= \r -> return $ Yield r (Just s) else do acc' <- fstep acc x go SPEC s acc' Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r Nothing step' _ _ Nothing = return Stop {-# INLINE_NORMAL wordsBy #-} wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b wordsBy predicate f (Stream step state) = Stream (stepOuter f) (Just state) where {-# INLINE_LATE stepOuter #-} stepOuter (Fold fstep initial done) gst (Just st) = do res <- step (adaptState gst) st case res of Yield x s -> do if predicate x then return $ Skip (Just s) else do acc <- initial acc' <- fstep acc x go SPEC s acc' Skip s -> return $ Skip $ Just s Stop -> return Stop where go !_ stt !acc = do res <- step (adaptState gst) stt case res of Yield x s -> do if predicate x then done acc >>= \r -> return $ Yield r (Just s) else do acc' <- fstep acc x go SPEC s acc' Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r Nothing stepOuter _ _ Nothing = return Stop -- String search algorithms: -- http://www-igm.univ-mlv.fr/~lecroq/string/index.html {- -- TODO can we unify the splitting operations using a splitting configuration -- like in the split package. -- data SplitStyle = Infix | Suffix | Prefix deriving (Eq, Show) data SplitOptions = SplitOptions { style :: SplitStyle , withSep :: Bool -- ^ keep the separators in output -- , compact :: Bool -- ^ treat multiple consecutive separators as one -- , trimHead :: Bool -- ^ drop blank at head -- , trimTail :: Bool -- ^ drop blank at tail } -} data SplitOnState s a = GO_START | GO_EMPTY_PAT s | GO_SINGLE_PAT s a | GO_SHORT_PAT s | GO_KARP_RABIN s !(RB.Ring a) !(Ptr a) | GO_DONE {-# INLINE_NORMAL splitOn #-} splitOn :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a) => Array a -> Fold m a b -> Stream m a -> Stream m b splitOn patArr@Array{..} (Fold fstep initial done) (Stream step state) = Stream stepOuter GO_START where patLen = A.length patArr maxIndex = patLen - 1 elemBits = sizeOf (undefined :: a) * 8 {-# INLINE_LATE stepOuter #-} stepOuter _ GO_START = if patLen == 0 then return $ Skip $ GO_EMPTY_PAT state else if patLen == 1 then do r <- liftIO $ (A.unsafeIndexIO patArr 0) return $ Skip $ GO_SINGLE_PAT state r else if sizeOf (undefined :: a) * patLen <= sizeOf (undefined :: Word) then return $ Skip $ GO_SHORT_PAT state else do (rb, rhead) <- liftIO $ RB.new patLen return $ Skip $ GO_KARP_RABIN state rb rhead stepOuter gst (GO_SINGLE_PAT stt pat) = initial >>= go SPEC stt where go !_ st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do if pat == x then do r <- done acc return $ Yield r (GO_SINGLE_PAT s pat) else fstep acc x >>= go SPEC s Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r GO_DONE stepOuter gst (GO_SHORT_PAT stt) = initial >>= go0 SPEC 0 (0 :: Word) stt where mask :: Word mask = (1 `shiftL` (elemBits * patLen)) - 1 addToWord wrd a = (wrd `shiftL` elemBits) .|. fromIntegral (fromEnum a) patWord :: Word patWord = mask .&. A.foldl' addToWord 0 patArr go0 !_ !idx wrd st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do let wrd' = addToWord wrd x if idx == maxIndex then do if wrd' .&. mask == patWord then do r <- done acc return $ Yield r (GO_SHORT_PAT s) else go1 SPEC wrd' s acc else go0 SPEC (idx + 1) wrd' s acc Skip s -> go0 SPEC idx wrd s acc Stop -> do acc' <- if idx /= 0 then go2 wrd idx acc else return acc done acc' >>= \r -> return $ Yield r GO_DONE {-# INLINE go1 #-} go1 !_ wrd st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do let wrd' = addToWord wrd x old = (mask .&. wrd) `shiftR` (elemBits * (patLen - 1)) acc' <- fstep acc (toEnum $ fromIntegral old) if wrd' .&. mask == patWord then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s) else go1 SPEC wrd' s acc' Skip s -> go1 SPEC wrd s acc Stop -> do acc' <- go2 wrd patLen acc done acc' >>= \r -> return $ Yield r GO_DONE go2 !wrd !n !acc | n > 0 = do let old = (mask .&. wrd) `shiftR` (elemBits * (n - 1)) fstep acc (toEnum $ fromIntegral old) >>= go2 wrd (n - 1) go2 _ _ acc = return acc stepOuter gst (GO_KARP_RABIN stt rb rhead) = do initial >>= go0 SPEC 0 rhead stt where k = 2891336453 :: Word32 coeff = k ^ patLen addCksum cksum a = cksum * k + fromIntegral (fromEnum a) deltaCksum cksum old new = addCksum cksum new - coeff * fromIntegral (fromEnum old) -- XXX shall we use a random starting hash or 1 instead of 0? patHash = A.foldl' addCksum 0 patArr -- rh == ringHead go0 !_ !idx !rh st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do rh' <- liftIO $ RB.unsafeInsert rb rh x if idx == maxIndex then do let fold = RB.unsafeFoldRing (RB.ringBound rb) let !ringHash = fold addCksum 0 rb if ringHash == patHash then go2 SPEC ringHash rh' s acc else go1 SPEC ringHash rh' s acc else go0 SPEC (idx + 1) rh' s acc Skip s -> go0 SPEC idx rh s acc Stop -> do !acc' <- if idx /= 0 then RB.unsafeFoldRingM rh fstep acc rb else return acc done acc' >>= \r -> return $ Yield r GO_DONE -- XXX Theoretically this code can do 4 times faster if GHC generates -- optimal code. If we use just "(cksum' == patHash)" condition it goes -- 4x faster, as soon as we add the "RB.unsafeEqArray rb v" condition -- the generated code changes drastically and becomes 4x slower. Need -- to investigate what is going on with GHC. {-# INLINE go1 #-} go1 !_ !cksum !rh st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do old <- liftIO $ peek rh let cksum' = deltaCksum cksum old x acc' <- fstep acc old if (cksum' == patHash) then do rh' <- liftIO (RB.unsafeInsert rb rh x) go2 SPEC cksum' rh' s acc' else do rh' <- liftIO (RB.unsafeInsert rb rh x) go1 SPEC cksum' rh' s acc' Skip s -> go1 SPEC cksum rh s acc Stop -> do acc' <- RB.unsafeFoldRingFullM rh fstep acc rb done acc' >>= \r -> return $ Yield r GO_DONE go2 !_ !cksum' !rh' s !acc' = do if RB.unsafeEqArray rb rh' patArr then do r <- done acc' return $ Yield r (GO_KARP_RABIN s rb rhead) else go1 SPEC cksum' rh' s acc' stepOuter gst (GO_EMPTY_PAT st) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial acc' <- fstep acc x done acc' >>= \r -> return $ Yield r (GO_EMPTY_PAT s) Skip s -> return $ Skip (GO_EMPTY_PAT s) Stop -> return Stop stepOuter _ GO_DONE = return Stop {-# INLINE_NORMAL splitSuffixOn #-} splitSuffixOn :: forall m a b. (MonadIO m, Storable a, Enum a, Eq a) => Bool -> Array a -> Fold m a b -> Stream m a -> Stream m b splitSuffixOn withSep patArr@Array{..} (Fold fstep initial done) (Stream step state) = Stream stepOuter GO_START where patLen = A.length patArr maxIndex = patLen - 1 elemBits = sizeOf (undefined :: a) * 8 {-# INLINE_LATE stepOuter #-} stepOuter _ GO_START = if patLen == 0 then return $ Skip $ GO_EMPTY_PAT state else if patLen == 1 then do r <- liftIO $ (A.unsafeIndexIO patArr 0) return $ Skip $ GO_SINGLE_PAT state r else if sizeOf (undefined :: a) * patLen <= sizeOf (undefined :: Word) then return $ Skip $ GO_SHORT_PAT state else do (rb, rhead) <- liftIO $ RB.new patLen return $ Skip $ GO_KARP_RABIN state rb rhead stepOuter gst (GO_SINGLE_PAT stt pat) = do -- This first part is the only difference between splitOn and -- splitSuffixOn. -- If the last element is a separator do not issue a blank segment. res <- step (adaptState gst) stt case res of Yield x s -> do acc <- initial if pat == x then do acc' <- if withSep then fstep acc x else return acc done acc' >>= \r -> return $ Yield r (GO_SINGLE_PAT s pat) else fstep acc x >>= go SPEC s Skip s -> return $ Skip $ (GO_SINGLE_PAT s pat) Stop -> return Stop where -- This is identical for splitOn and splitSuffixOn go !_ st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do if pat == x then do acc' <- if withSep then fstep acc x else return acc r <- done acc' return $ Yield r (GO_SINGLE_PAT s pat) else fstep acc x >>= go SPEC s Skip s -> go SPEC s acc Stop -> done acc >>= \r -> return $ Yield r GO_DONE stepOuter gst (GO_SHORT_PAT stt) = do -- Call "initial" only if the stream yields an element, otherwise we -- may call "initial" but never yield anything. initial may produce a -- side effect, therefore we will end up doing and discard a side -- effect. let idx = 0 let wrd = 0 res <- step (adaptState gst) stt case res of Yield x s -> do acc <- initial let wrd' = addToWord wrd x acc' <- if withSep then fstep acc x else return acc if idx == maxIndex then do if wrd' .&. mask == patWord then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s) else go0 SPEC (idx + 1) wrd' s acc' else go0 SPEC (idx + 1) wrd' s acc' Skip s -> return $ Skip (GO_SHORT_PAT s) Stop -> return Stop where mask :: Word mask = (1 `shiftL` (elemBits * patLen)) - 1 addToWord wrd a = (wrd `shiftL` elemBits) .|. fromIntegral (fromEnum a) patWord :: Word patWord = mask .&. A.foldl' addToWord 0 patArr go0 !_ !idx wrd st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do let wrd' = addToWord wrd x acc' <- if withSep then fstep acc x else return acc if idx == maxIndex then do if wrd' .&. mask == patWord then do r <- done acc' return $ Yield r (GO_SHORT_PAT s) else go1 SPEC wrd' s acc' else go0 SPEC (idx + 1) wrd' s acc' Skip s -> go0 SPEC idx wrd s acc Stop -> do if (idx == maxIndex) && (wrd .&. mask == patWord) then return Stop else do acc' <- if idx /= 0 && not withSep then go2 wrd idx acc else return acc done acc' >>= \r -> return $ Yield r GO_DONE {-# INLINE go1 #-} go1 !_ wrd st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do let wrd' = addToWord wrd x old = (mask .&. wrd) `shiftR` (elemBits * (patLen - 1)) acc' <- if withSep then fstep acc x else fstep acc (toEnum $ fromIntegral old) if wrd' .&. mask == patWord then done acc' >>= \r -> return $ Yield r (GO_SHORT_PAT s) else go1 SPEC wrd' s acc' Skip s -> go1 SPEC wrd s acc Stop -> -- If the last sequence is a separator do not issue a blank -- segment. if wrd .&. mask == patWord then return Stop else do acc' <- if withSep then return acc else go2 wrd patLen acc done acc' >>= \r -> return $ Yield r GO_DONE go2 !wrd !n !acc | n > 0 = do let old = (mask .&. wrd) `shiftR` (elemBits * (n - 1)) fstep acc (toEnum $ fromIntegral old) >>= go2 wrd (n - 1) go2 _ _ acc = return acc stepOuter gst (GO_KARP_RABIN stt rb rhead) = do let idx = 0 res <- step (adaptState gst) stt case res of Yield x s -> do acc <- initial acc' <- if withSep then fstep acc x else return acc rh' <- liftIO (RB.unsafeInsert rb rhead x) if idx == maxIndex then do let fold = RB.unsafeFoldRing (RB.ringBound rb) let !ringHash = fold addCksum 0 rb if ringHash == patHash then go2 SPEC ringHash rh' s acc' else go0 SPEC (idx + 1) rh' s acc' else go0 SPEC (idx + 1) rh' s acc' Skip s -> return $ Skip (GO_KARP_RABIN s rb rhead) Stop -> return Stop where k = 2891336453 :: Word32 coeff = k ^ patLen addCksum cksum a = cksum * k + fromIntegral (fromEnum a) deltaCksum cksum old new = addCksum cksum new - coeff * fromIntegral (fromEnum old) -- XXX shall we use a random starting hash or 1 instead of 0? patHash = A.foldl' addCksum 0 patArr -- rh == ringHead go0 !_ !idx !rh st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do acc' <- if withSep then fstep acc x else return acc rh' <- liftIO (RB.unsafeInsert rb rh x) if idx == maxIndex then do let fold = RB.unsafeFoldRing (RB.ringBound rb) let !ringHash = fold addCksum 0 rb if ringHash == patHash then go2 SPEC ringHash rh' s acc' else go1 SPEC ringHash rh' s acc' else go0 SPEC (idx + 1) rh' s acc' Skip s -> go0 SPEC idx rh s acc Stop -> do -- do not issue a blank segment when we end at pattern if (idx == maxIndex) && RB.unsafeEqArray rb rh patArr then return Stop else do !acc' <- if idx /= 0 && not withSep then RB.unsafeFoldRingM rh fstep acc rb else return acc done acc' >>= \r -> return $ Yield r GO_DONE -- XXX Theoretically this code can do 4 times faster if GHC generates -- optimal code. If we use just "(cksum' == patHash)" condition it goes -- 4x faster, as soon as we add the "RB.unsafeEqArray rb v" condition -- the generated code changes drastically and becomes 4x slower. Need -- to investigate what is going on with GHC. {-# INLINE go1 #-} go1 !_ !cksum !rh st !acc = do res <- step (adaptState gst) st case res of Yield x s -> do old <- liftIO $ peek rh let cksum' = deltaCksum cksum old x acc' <- if withSep then fstep acc x else fstep acc old if (cksum' == patHash) then do rh' <- liftIO (RB.unsafeInsert rb rh x) go2 SPEC cksum' rh' s acc' else do rh' <- liftIO (RB.unsafeInsert rb rh x) go1 SPEC cksum' rh' s acc' Skip s -> go1 SPEC cksum rh s acc Stop -> do if RB.unsafeEqArray rb rh patArr then return Stop else do acc' <- if withSep then return acc else RB.unsafeFoldRingFullM rh fstep acc rb done acc' >>= \r -> return $ Yield r GO_DONE go2 !_ !cksum' !rh' s !acc' = do if RB.unsafeEqArray rb rh' patArr then do r <- done acc' return $ Yield r (GO_KARP_RABIN s rb rhead) else go1 SPEC cksum' rh' s acc' stepOuter gst (GO_EMPTY_PAT st) = do res <- step (adaptState gst) st case res of Yield x s -> do acc <- initial acc' <- fstep acc x done acc' >>= \r -> return $ Yield r (GO_EMPTY_PAT s) Skip s -> return $ Skip (GO_EMPTY_PAT s) Stop -> return Stop stepOuter _ GO_DONE = return Stop ------------------------------------------------------------------------------ -- Substreams ------------------------------------------------------------------------------ {-# INLINE_NORMAL isPrefixOf #-} isPrefixOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool isPrefixOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing) where go (sa, sb, Nothing) = do r <- stepa defState sa case r of Yield x sa' -> go (sa', sb, Just x) Skip sa' -> go (sa', sb, Nothing) Stop -> return True go (sa, sb, Just x) = do r <- stepb defState sb case r of Yield y sb' -> if x == y then go (sa, sb', Nothing) else return False Skip sb' -> go (sa, sb', Just x) Stop -> return False {-# INLINE_NORMAL isSubsequenceOf #-} isSubsequenceOf :: (Eq a, Monad m) => Stream m a -> Stream m a -> m Bool isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing) where go (sa, sb, Nothing) = do r <- stepa defState sa case r of Yield x sa' -> go (sa', sb, Just x) Skip sa' -> go (sa', sb, Nothing) Stop -> return True go (sa, sb, Just x) = do r <- stepb defState sb case r of Yield y sb' -> if x == y then go (sa, sb', Nothing) else go (sa, sb', Just x) Skip sb' -> go (sa, sb', Just x) Stop -> return False {-# INLINE_NORMAL stripPrefix #-} stripPrefix :: (Eq a, Monad m) => Stream m a -> Stream m a -> m (Maybe (Stream m a)) stripPrefix (Stream stepa ta) (Stream stepb tb) = go (ta, tb, Nothing) where go (sa, sb, Nothing) = do r <- stepa defState sa case r of Yield x sa' -> go (sa', sb, Just x) Skip sa' -> go (sa', sb, Nothing) Stop -> return $ Just (Stream stepb sb) go (sa, sb, Just x) = do r <- stepb defState sb case r of Yield y sb' -> if x == y then go (sa, sb', Nothing) else return Nothing Skip sb' -> go (sa, sb', Just x) Stop -> return Nothing ------------------------------------------------------------------------------ -- Map and Fold ------------------------------------------------------------------------------ -- | Execute a monadic action for each element of the 'Stream' {-# INLINE_NORMAL mapM_ #-} mapM_ :: Monad m => (a -> m b) -> Stream m a -> m () mapM_ m = drain . mapM m ------------------------------------------------------------------------------ -- Exceptions ------------------------------------------------------------------------------ -- | Run a side effect before the stream yields its first element. {-# INLINE_NORMAL before #-} before :: Monad m => m b -> Stream m a -> Stream m a before action (Stream step state) = Stream step' Nothing where {-# INLINE_LATE step' #-} step' _ Nothing = action >> return (Skip (Just state)) step' gst (Just st) = do res <- step gst st case res of Yield x s -> return $ Yield x (Just s) Skip s -> return $ Skip (Just s) Stop -> return Stop -- | Run a side effect whenever the stream stops normally. {-# INLINE_NORMAL after #-} after :: Monad m => m b -> Stream m a -> Stream m a after action (Stream step state) = Stream step' state where {-# INLINE_LATE step' #-} step' gst st = do res <- step gst st case res of Yield x s -> return $ Yield x s Skip s -> return $ Skip s Stop -> action >> return Stop -- XXX These combinators are expensive due to the call to -- onException/handle/try on each step. Therefore, when possible, they should -- be called in an outer loop where we perform less iterations. For example, we -- cannot call them on each iteration in a char stream, instead we can call -- them when doing an IO on an array. -- -- XXX For high performance error checks in busy streams we may need another -- Error constructor in step. -- -- | Run a side effect whenever the stream aborts due to an exception. The -- exception is not caught simply rethrown. {-# INLINE_NORMAL onException #-} onException :: MonadCatch m => m b -> Stream m a -> Stream m a onException action (Stream step state) = Stream step' state where {-# INLINE_LATE step' #-} step' gst st = do res <- step gst st `MC.onException` action case res of Yield x s -> return $ Yield x s Skip s -> return $ Skip s Stop -> return Stop -- | Run a side effect whenever the stream stops normally or aborts due to an -- exception. {-# INLINE finally #-} finally :: MonadCatch m => m b -> Stream m a -> Stream m a finally action xs = after action $ onException action xs -- XXX bracket is like concatMap, it generates a stream and then flattens it. -- Like concatMap it has 10x worse performance compared to linear fused -- compositions. -- -- | Run the first action before the stream starts and remember its output, -- generate a stream using the output, run the second action using the -- remembered value as an argument whenever the stream ends normally or due to -- an exception. {-# INLINE_NORMAL bracket #-} bracket :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a bracket bef aft bet = Stream step' Nothing where {-# INLINE_LATE step' #-} step' _ Nothing = bef >>= \x -> return (Skip (Just (bet x, x))) -- NOTE: It is important to use UnStream instead of the Stream pattern -- here, otherwise we get huge perf degradation, see note in concatMap. step' gst (Just (UnStream step state, v)) = do res <- step gst state `MC.onException` aft v case res of Yield x s -> return $ Yield x (Just (Stream step s, v)) Skip s -> return $ Skip (Just (Stream step s, v)) Stop -> aft v >> return Stop -- | When evaluating a stream if an exception occurs, stream evaluation aborts -- and the specified exception handler is run with the exception as argument. {-# INLINE_NORMAL handle #-} handle :: (MonadCatch m, Exception e) => (e -> m a) -> Stream m a -> Stream m a handle f (Stream step state) = Stream step' (Just state) where {-# INLINE_LATE step' #-} step' gst (Just st) = do res <- MC.try $ step gst st case res of Left e -> f e >>= \x -> return (Yield x Nothing) Right r -> case r of Yield x s -> return $ Yield x (Just s) Skip s -> return $ Skip (Just s) Stop -> return Stop step' _ Nothing = return Stop ------------------------------------------------------------------------------ -- Transformation by Folding (Scans) ------------------------------------------------------------------------------ ------------------------------------------------------------------------------ -- Prescans ------------------------------------------------------------------------------ -- XXX Is a prescan useful, discarding the last step does not sound useful? I -- am not sure about the utility of this function, so this is implemented but -- not exposed. We can expose it if someone provides good reasons why this is -- useful. -- -- XXX We have to execute the stream one step ahead to know that we are at the -- last step. The vector implementation of prescan executes the last fold step -- but does not yield the result. This means we have executed the effect but -- discarded value. This does not sound right. In this implementation we are -- not executing the last fold step. {-# INLINE_NORMAL prescanlM' #-} prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b prescanlM' f mz (Stream step state) = Stream step' (state, mz) where {-# INLINE_LATE step' #-} step' gst (st, prev) = do r <- step (adaptState gst) st case r of Yield x s -> do acc <- prev return $ Yield acc (s, f acc x) Skip s -> return $ Skip (s, prev) Stop -> return Stop {-# INLINE prescanl' #-} prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b prescanl' f z = prescanlM' (\a b -> return (f a b)) (return z) ------------------------------------------------------------------------------ -- Monolithic postscans (postscan followed by a map) ------------------------------------------------------------------------------ -- The performance of a modular postscan followed by a map seems to be -- equivalent to this monolithic scan followed by map therefore we may not need -- this implementation. We just have it for performance comparison and in case -- modular version does not perform well in some situation. -- {-# INLINE_NORMAL postscanlMx' #-} postscanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b postscanlMx' fstep begin done (Stream step state) = do Stream step' (state, begin) where {-# INLINE_LATE step' #-} step' gst (st, acc) = do r <- step (adaptState gst) st case r of Yield x s -> do old <- acc y <- fstep old x v <- done y v `seq` y `seq` return (Yield v (s, return y)) Skip s -> return $ Skip (s, acc) Stop -> return Stop {-# INLINE_NORMAL postscanlx' #-} postscanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b postscanlx' fstep begin done s = postscanlMx' (\b a -> return (fstep b a)) (return begin) (return . done) s -- XXX do we need consM strict to evaluate the begin value? {-# INLINE scanlMx' #-} scanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b scanlMx' fstep begin done s = (begin >>= \x -> x `seq` done x) `consM` postscanlMx' fstep begin done s {-# INLINE scanlx' #-} scanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b scanlx' fstep begin done s = scanlMx' (\b a -> return (fstep b a)) (return begin) (return . done) s ------------------------------------------------------------------------------ -- postscans ------------------------------------------------------------------------------ -- XXX if we make the initial value of the accumulator monadic then should we -- execute it even if the stream is empty? In that case we would have generated -- the effect but discarded the value, but that is what a fold does when the -- stream is empty. Whatever we decide, need to reconcile this with prescan. -- If we execute the initial value here without even using it then it is ok to -- execute the last step there as well without using the value. -- Looking at the duality with right fold, in case of right fold we always -- perform the action when the construction terminates, so in case of left fold -- we should perform it only when the reduction starts. {-# INLINE_NORMAL postscanlM' #-} postscanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b postscanlM' fstep begin (Stream step state) = begin `seq` Stream step' (state, begin) where {-# INLINE_LATE step' #-} step' gst (st, acc) = acc `seq` do r <- step (adaptState gst) st case r of Yield x s -> do y <- fstep acc x y `seq` return (Yield y (s, y)) Skip s -> return $ Skip (s, acc) Stop -> return Stop {-# INLINE_NORMAL postscanl' #-} postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a postscanl' f = postscanlM' (\a b -> return (f a b)) {-# INLINE_NORMAL postscanlM #-} postscanlM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b postscanlM fstep begin (Stream step state) = Stream step' (state, begin) where {-# INLINE_LATE step' #-} step' gst (st, acc) = do r <- step (adaptState gst) st case r of Yield x s -> do y <- fstep acc x return (Yield y (s, y)) Skip s -> return $ Skip (s, acc) Stop -> return Stop {-# INLINE_NORMAL postscanl #-} postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a postscanl f = postscanlM (\a b -> return (f a b)) {-# INLINE_NORMAL scanlM' #-} scanlM' :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b scanlM' fstep begin s = begin `seq` (begin `cons` postscanlM' fstep begin s) {-# INLINE scanl' #-} scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b scanl' f = scanlM' (\a b -> return (f a b)) {-# INLINE_NORMAL scanlM #-} scanlM :: Monad m => (b -> a -> m b) -> b -> Stream m a -> Stream m b scanlM fstep begin s = begin `cons` postscanlM fstep begin s {-# INLINE scanl #-} scanl :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b scanl f = scanlM (\a b -> return (f a b)) {-# INLINE_NORMAL scanl1M #-} scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a scanl1M fstep (Stream step state) = Stream step' (state, Nothing) where {-# INLINE_LATE step' #-} step' gst (st, Nothing) = do r <- step gst st case r of Yield x s -> return $ Yield x (s, Just x) Skip s -> return $ Skip (s, Nothing) Stop -> return Stop step' gst (st, Just acc) = do r <- step gst st case r of Yield y s -> do z <- fstep acc y return $ Yield z (s, Just z) Skip s -> return $ Skip (s, Just acc) Stop -> return Stop {-# INLINE scanl1 #-} scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a scanl1 f = scanl1M (\x y -> return (f x y)) {-# INLINE_NORMAL scanl1M' #-} scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a scanl1M' fstep (Stream step state) = Stream step' (state, Nothing) where {-# INLINE_LATE step' #-} step' gst (st, Nothing) = do r <- step gst st case r of Yield x s -> x `seq` return $ Yield x (s, Just x) Skip s -> return $ Skip (s, Nothing) Stop -> return Stop step' gst (st, Just acc) = acc `seq` do r <- step gst st case r of Yield y s -> do z <- fstep acc y z `seq` return $ Yield z (s, Just z) Skip s -> return $ Skip (s, Just acc) Stop -> return Stop {-# INLINE scanl1' #-} scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a scanl1' f = scanl1M' (\x y -> return (f x y)) {-# INLINE tap #-} tap :: Monad m => Sink m a -> Stream m a -> Stream m a tap (Sink fstep) (Stream step state) = Stream step' state where step' gst st = do r <- step gst st case r of Yield x s -> do fstep x return $ Yield x s Skip s -> return $ Skip s Stop -> return $ Stop ------------------------------------------------------------------------------- -- Filtering ------------------------------------------------------------------------------- {-# INLINE_NORMAL takeWhileM #-} takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a takeWhileM f (Stream step state) = Stream step' state where {-# INLINE_LATE step' #-} step' gst st = do r <- step gst st case r of Yield x s -> do b <- f x return $ if b then Yield x s else Stop Skip s -> return $ Skip s Stop -> return Stop {-# INLINE takeWhile #-} takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a takeWhile f = takeWhileM (return . f) {-# INLINE_NORMAL drop #-} drop :: Monad m => Int -> Stream m a -> Stream m a drop n (Stream step state) = Stream step' (state, Just n) where {-# INLINE_LATE step' #-} step' gst (st, Just i) | i > 0 = do r <- step gst st return $ case r of Yield _ s -> Skip (s, Just (i - 1)) Skip s -> Skip (s, Just i) Stop -> Stop | otherwise = return $ Skip (st, Nothing) step' gst (st, Nothing) = do r <- step gst st return $ case r of Yield x s -> Yield x (s, Nothing) Skip s -> Skip (s, Nothing) Stop -> Stop data DropWhileState s a = DropWhileDrop s | DropWhileYield a s | DropWhileNext s {-# INLINE_NORMAL dropWhileM #-} dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a dropWhileM f (Stream step state) = Stream step' (DropWhileDrop state) where {-# INLINE_LATE step' #-} step' gst (DropWhileDrop st) = do r <- step gst st case r of Yield x s -> do b <- f x if b then return $ Skip (DropWhileDrop s) else return $ Skip (DropWhileYield x s) Skip s -> return $ Skip (DropWhileDrop s) Stop -> return Stop step' gst (DropWhileNext st) = do r <- step gst st case r of Yield x s -> return $ Skip (DropWhileYield x s) Skip s -> return $ Skip (DropWhileNext s) Stop -> return Stop step' _ (DropWhileYield x st) = return $ Yield x (DropWhileNext st) {-# INLINE dropWhile #-} dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a dropWhile f = dropWhileM (return . f) {-# INLINE_NORMAL filterM #-} filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a filterM f (Stream step state) = Stream step' state where {-# INLINE_LATE step' #-} step' gst st = do r <- step gst st case r of Yield x s -> do b <- f x return $ if b then Yield x s else Skip s Skip s -> return $ Skip s Stop -> return Stop {-# INLINE filter #-} filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a filter f = filterM (return . f) {-# INLINE_NORMAL uniq #-} uniq :: (Eq a, Monad m) => Stream m a -> Stream m a uniq (Stream step state) = Stream step' (Nothing, state) where {-# INLINE_LATE step' #-} step' gst (Nothing, st) = do r <- step gst st case r of Yield x s -> return $ Yield x (Just x, s) Skip s -> return $ Skip (Nothing, s) Stop -> return Stop step' gst (Just x, st) = do r <- step gst st case r of Yield y s | x == y -> return $ Skip (Just x, s) | otherwise -> return $ Yield x (Just y, s) Skip s -> return $ Skip (Just x, s) Stop -> return Stop ------------------------------------------------------------------------------ -- Transformation by Mapping ------------------------------------------------------------------------------ {-# INLINE_NORMAL sequence #-} sequence :: Monad m => Stream m (m a) -> Stream m a sequence (Stream step state) = Stream step' state where {-# INLINE_LATE step' #-} step' gst st = do r <- step (adaptState gst) st case r of Yield x s -> x >>= \a -> return (Yield a s) Skip s -> return $ Skip s Stop -> return Stop ------------------------------------------------------------------------------ -- Inserting ------------------------------------------------------------------------------ {-# INLINE_NORMAL insertBy #-} insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing) where {-# INLINE_LATE step' #-} step' gst (st, False, _) = do r <- step gst st case r of Yield x s -> case cmp a x of GT -> return $ Yield x (s, False, Nothing) _ -> return $ Yield a (s, True, Just x) Skip s -> return $ Skip (s, False, Nothing) Stop -> return $ Yield a (st, True, Nothing) step' _ (_, True, Nothing) = return Stop step' gst (st, True, Just prev) = do r <- step gst st case r of Yield x s -> return $ Yield prev (s, True, Just x) Skip s -> return $ Skip (s, True, Just prev) Stop -> return $ Yield prev (st, True, Nothing) ------------------------------------------------------------------------------ -- Deleting ------------------------------------------------------------------------------ {-# INLINE_NORMAL deleteBy #-} deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a deleteBy eq x (Stream step state) = Stream step' (state, False) where {-# INLINE_LATE step' #-} step' gst (st, False) = do r <- step gst st case r of Yield y s -> return $ if eq x y then Skip (s, True) else Yield y (s, False) Skip s -> return $ Skip (s, False) Stop -> return Stop step' gst (st, True) = do r <- step gst st case r of Yield y s -> return $ Yield y (s, True) Skip s -> return $ Skip (s, True) Stop -> return Stop ------------------------------------------------------------------------------ -- Transformation by Map and Filter ------------------------------------------------------------------------------ -- XXX Will this always fuse properly? {-# INLINE_NORMAL mapMaybe #-} mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b mapMaybe f = fmap fromJust . filter isJust . map f {-# INLINE_NORMAL mapMaybeM #-} mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b mapMaybeM f = fmap fromJust . filter isJust . mapM f ------------------------------------------------------------------------------ -- Zipping ------------------------------------------------------------------------------ {-# INLINE_NORMAL indexed #-} indexed :: Monad m => Stream m a -> Stream m (Int, a) indexed (Stream step state) = Stream step' (state, 0) where {-# INLINE_LATE step' #-} step' gst (st, i) = i `seq` do r <- step (adaptState gst) st case r of Yield x s -> return $ Yield (i, x) (s, i+1) Skip s -> return $ Skip (s, i) Stop -> return Stop {-# INLINE_NORMAL indexedR #-} indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a) indexedR m (Stream step state) = Stream step' (state, m) where {-# INLINE_LATE step' #-} step' gst (st, i) = i `seq` do r <- step (adaptState gst) st case r of Yield x s -> let i' = i - 1 in return $ Yield (i, x) (s, i') Skip s -> return $ Skip (s, i) Stop -> return Stop {-# INLINE_NORMAL zipWithM #-} zipWithM :: Monad m => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing) where {-# INLINE_LATE step #-} step gst (sa, sb, Nothing) = do r <- stepa (adaptState gst) sa return $ case r of Yield x sa' -> Skip (sa', sb, Just x) Skip sa' -> Skip (sa', sb, Nothing) Stop -> Stop step gst (sa, sb, Just x) = do r <- stepb (adaptState gst) sb case r of Yield y sb' -> do z <- f x y return $ Yield z (sa, sb', Nothing) Skip sb' -> return $ Skip (sa, sb', Just x) Stop -> return Stop {-# RULES "zipWithM xs xs" forall f xs. zipWithM f xs xs = mapM (\x -> f x x) xs #-} {-# INLINE zipWith #-} zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c zipWith f = zipWithM (\a b -> return (f a b)) ------------------------------------------------------------------------------ -- Merging ------------------------------------------------------------------------------ {-# INLINE_NORMAL mergeByM #-} mergeByM :: (Monad m) => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a mergeByM cmp (Stream stepa ta) (Stream stepb tb) = Stream step (Just ta, Just tb, Nothing, Nothing) where {-# INLINE_LATE step #-} -- one of the values is missing, and the corresponding stream is running step gst (Just sa, sb, Nothing, b) = do r <- stepa gst sa return $ case r of Yield a sa' -> Skip (Just sa', sb, Just a, b) Skip sa' -> Skip (Just sa', sb, Nothing, b) Stop -> Skip (Nothing, sb, Nothing, b) step gst (sa, Just sb, a, Nothing) = do r <- stepb gst sb return $ case r of Yield b sb' -> Skip (sa, Just sb', a, Just b) Skip sb' -> Skip (sa, Just sb', a, Nothing) Stop -> Skip (sa, Nothing, a, Nothing) -- both the values are available step _ (sa, sb, Just a, Just b) = do res <- cmp a b return $ case res of GT -> Yield b (sa, sb, Just a, Nothing) _ -> Yield a (sa, sb, Nothing, Just b) -- one of the values is missing, corresponding stream is done step _ (Nothing, sb, Nothing, Just b) = return $ Yield b (Nothing, sb, Nothing, Nothing) step _ (sa, Nothing, Just a, Nothing) = return $ Yield a (sa, Nothing, Nothing, Nothing) step _ (Nothing, Nothing, Nothing, Nothing) = return Stop {-# INLINE mergeBy #-} mergeBy :: (Monad m) => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a mergeBy cmp = mergeByM (\a b -> return $ cmp a b) ------------------------------------------------------------------------------ -- Transformation comprehensions ------------------------------------------------------------------------------ {-# INLINE_NORMAL the #-} the :: (Eq a, Monad m) => Stream m a -> m (Maybe a) the (Stream step state) = go state where go st = do r <- step defState st case r of Yield x s -> go' x s Skip s -> go s Stop -> return Nothing go' n st = do r <- step defState st case r of Yield x s | x == n -> go' n s | otherwise -> return Nothing Skip s -> go' n s Stop -> return (Just n)