streaming 0.2.1.0 → 0.2.2.0
raw patch · 4 files changed
+225/−19 lines, 4 filesdep ~semigroupsnew-uploader
Dependency ranges changed: semigroups
Files
- changelog.md +21/−0
- src/Streaming/Internal.hs +9/−6
- src/Streaming/Prelude.hs +194/−12
- streaming.cabal +1/−1
changelog.md view
@@ -1,3 +1,24 @@+- ???++ Added `nubOrd`, `nubInt`, `nubOrdOn`, `nubIntOn`.++ Fix performance regression in `for`.+ + Add `foldMap` and `foldMap_`.+ + Fix the behaviour of `slidingWindow 1`.+ + Reintroduce `readFile` and `writeFile`, using plain `System.IO`+ instead of `ResourceT` machinery.++ Add `merge`, `mergeOn`, and `mergeBy`.++ Improve performance of `concat`.++ Improve performance of (`*>`), getting rid of the default implementation.++ Generalise type signature of `toList_`.+ - 0.2.1.0 Adding `Semigroup` instances for GHC 8.4.
src/Streaming/Internal.hs view
@@ -231,12 +231,8 @@ instance (Functor f, Monad m) => Monad (Stream f m) where return = Return {-# INLINE return #-}- stream1 >> stream2 = loop stream1 where- loop stream = case stream of- Return _ -> stream2- Effect m -> Effect (fmap loop m)- Step f -> Step (fmap loop f) - {-# INLINABLE (>>) #-}+ (>>) = (*>)+ {-# INLINE (>>) #-} -- (>>=) = _bind -- {-#INLINE (>>=) #-} --@@ -281,6 +277,13 @@ {-# INLINE pure #-} streamf <*> streamx = do {f <- streamf; x <- streamx; return (f x)} {-# INLINE (<*>) #-} + stream1 *> stream2 = loop stream1 where+ loop stream = case stream of+ Return _ -> stream2+ Effect m -> Effect (fmap loop m)+ Step f -> Step (fmap loop f)+ {-# INLINABLE (*>) #-}+ {- | The 'Alternative' instance glues streams together stepwise.
src/Streaming/Prelude.hs view
@@ -70,6 +70,7 @@ , stdinLn , readLn , fromHandle+ , readFile , iterate , iterateM , repeat@@ -91,6 +92,7 @@ , mapM_ , print , toHandle+ , writeFile , effects , erase , drained@@ -112,6 +114,10 @@ , store , chain , sequence+ , nubOrd+ , nubOrdOn+ , nubInt+ , nubIntOn , filter , filterM , mapMaybeM@@ -166,6 +172,8 @@ , fold_ , foldM , foldM_+ , foldMap+ , foldMap_ , all , all_ , any@@ -221,6 +229,13 @@ , partitionEithers , partition + -- * Merging streams+ -- $merging++ , merge+ , mergeOn+ , mergeBy+ -- * Maybes -- $maybes , catMaybes@@ -260,7 +275,7 @@ , print, zipWith, zip, zipWith3, zip3, unzip, seq, show, read , readLn, sequence, concat, span, break, readFile, writeFile , minimum, maximum, elem, notElem, all, any, head- , last)+ , last, foldMap) import qualified GHC.IO.Exception as G import qualified System.IO as IO@@ -270,6 +285,9 @@ import Control.Concurrent (threadDelay) import Data.Functor.Compose import Data.Functor.Of+import qualified Data.Set as Set+import qualified Data.IntSet as IntSet+import Data.Ord (Ordering (..), comparing) -- instance (Eq a) => Eq1 (Of a) where eq1 = (==) -- instance (Ord a) => Ord1 (Of a) where compare1 = compare@@ -525,7 +543,7 @@ return (Step (a :> loop rest)) {-# INLINABLE chain #-} -{-| Make a stream of traversable containers into a stream of their separate elements.+{-| Make a stream of foldable containers into a stream of their separate elements. This is just > concat str = for str each@@ -551,8 +569,18 @@ -} concat :: (Monad m, Foldable.Foldable f) => Stream (Of (f a)) m r -> Stream (Of a) m r-concat str = for str each-{-# INLINE concat #-}+concat = loop+ where+ loop str = case str of+ Return r -> Return r+ Effect m -> Effect (fmap loop m)+ Step (lst :> as) ->+ let inner [] = loop as+ inner (x:rest) = Step (x :> inner rest)+ in inner (Foldable.toList lst)+{-# INLINABLE concat #-}+-- The above hand-written loop is ~20% faster than the 'for' implementation+-- concat str = for str each {-| The natural @cons@ for a @Stream (Of a)@. @@ -1355,6 +1383,23 @@ mappedPost = mapsMPost {-# INLINE mappedPost #-} +{-| Map each element of the stream to a monoid, and take the monoidal sum of the results.++>>> S.foldMap Sum $ S.take 2 (S.stdinLn)+1<Enter>+2<Enter>+3<Enter>+Sum {getSum = 6} :> ()++ -}+foldMap :: (Monad m, Monoid w) => (a -> w) -> Stream (Of a) m r -> m (Of w r)+foldMap f = fold (\ !acc a -> mappend acc (f a)) mempty id+{-# INLINE foldMap #-}++foldMap_ :: (Monad m, Monoid w) => (a -> w) -> Stream (Of a) m r -> m w+foldMap_ f = fold_ (\ !acc a -> mappend acc (f a)) mempty id+{-# INLINE foldMap_ #-}+ {-| Fold streamed items into their monoidal sum >>> S.mconcat $ S.take 2 $ S.map (Data.Monoid.Last . Just) (S.stdinLn)@@ -1365,7 +1410,7 @@ -} mconcat :: (Monad m, Monoid w) => Stream (Of w) m r -> m (Of w r) mconcat = fold mappend mempty id-{-#INLINE mconcat #-}+{-# INLINE mconcat #-} data Maybe_ a = Just_ !a | Nothing_ mconcat_ :: (Monad m, Monoid w) => Stream (Of w) m r -> m w@@ -1375,7 +1420,7 @@ minimum = fold (\m a -> case m of Nothing_ -> Just_ a ; Just_ a' -> Just_ (min a a')) Nothing_ (\m -> case m of Nothing_ -> Nothing; Just_ r -> Just r)-{-#INLINE minimum #-}+{-# INLINE minimum #-} minimum_ :: (Monad m, Ord a) => Stream (Of a) m r -> m (Maybe a) minimum_ = fold_ (\m a -> case m of Nothing_ -> Just_ a ; Just_ a' -> Just_ (min a a'))@@ -1454,6 +1499,51 @@ {-#INLINABLE notElem_ #-} +{-| Remove repeated elements from a Stream. 'nubOrd' of course accumulates a 'Data.Set.Set' of+ elements that have already been seen and should thus be used with care.++>>> S.toList_ $ S.nubOrd $ S.take 5 S.readLn :: IO ([Int])+1<Enter>+2<Enter>+3<Enter>+1<Enter>+2<Enter>+[1,2,3]++-}++nubOrd :: (Monad m, Ord a) => Stream (Of a) m r -> Stream (Of a) m r+nubOrd = nubOrdOn id+{-# INLINE nubOrd #-}++{-| Use 'nubOrdOn' to have a custom ordering function for your elements. -}+nubOrdOn :: (Monad m, Ord b) => (a -> b) -> Stream (Of a) m r -> Stream (Of a) m r+nubOrdOn f xs = loop mempty xs where+ loop !set stream = case stream of+ Return r -> Return r+ Effect m -> Effect (liftM (loop set) m)+ Step (a :> rest) -> let !fa = f a in+ if Set.member fa set+ then loop set rest+ else Step (a :> loop (Set.insert fa set) rest)++{-| More efficient versions of above when working with 'Int's that use 'Data.IntSet.IntSet'. -}++nubInt :: Monad m => Stream (Of Int) m r -> Stream (Of Int) m r+nubInt = nubIntOn id+{-# INLINE nubInt #-}++nubIntOn :: Monad m => (a -> Int) -> Stream (Of a) m r -> Stream (Of a) m r+nubIntOn f xs = loop mempty xs where+ loop !set stream = case stream of+ Return r -> Return r+ Effect m -> Effect (liftM (loop set) m)+ Step (a :> rest) -> let !fa = f a in+ if IntSet.member fa set+ then loop set rest+ else Step (a :> loop (IntSet.insert fa set) rest)++ {-| > filter p = hoist effects (partition p) @@ -1948,7 +2038,7 @@ is a leading cause of space leaks. -}-toList_ :: Monad m => Stream (Of a) m () -> m [a]+toList_ :: Monad m => Stream (Of a) m r -> m [a] toList_ = fold_ (\diff a ls -> diff (a: ls)) id (\diff -> diff []) {-# INLINE toList_ #-} @@ -2097,7 +2187,7 @@ yield a = Step (a :> Return ()) {-# INLINE yield #-} --- | Zip two 'Streams's+-- | Zip two 'Stream's zip :: Monad m => (Stream (Of a) m r) -> (Stream (Of b) m r)@@ -2105,7 +2195,7 @@ zip = zipWith (,) {-# INLINE zip #-} --- | Zip two 'Streams's using the provided combining function+-- | Zip two 'Stream's using the provided combining function zipWith :: Monad m => (a -> b -> c) -> (Stream (Of a) m r)@@ -2149,7 +2239,7 @@ {-# INLINABLE zipWith3 #-} --- | Zip three streams together+-- | Zip three 'Stream's together zip3 :: Monad m => (Stream (Of a) m r) -> (Stream (Of b) m r)@@ -2296,8 +2386,34 @@ Right () -> loop rest {-# INLINABLE stdoutLn #-} +{-| Read the lines of a file, using a function of the type: \'@'Stream' ('Of' 'String') 'IO' () -> 'IO' a@\'+ to turn the stream into a value of type \''IO' a\'. +>>> S.writeFile "lines.txt" $ S.take 2 S.stdinLn+hello<Enter>+world<Enter>+>>> S.readFile "lines.txt" S.print+"hello"+"world" +-}+readFile :: FilePath -> (Stream (Of String) IO () -> IO a) -> IO a+readFile f s = IO.withFile f IO.ReadMode $ \h -> s (fromHandle h)++{-| Write a series of 'String's as lines to a file.++>>> S.writeFile "lines.txt" $ S.take 2 S.stdinLn+hello<Enter>+world<Enter>++>>> S.stdoutLn $ S.readFile "lines.txt"+hello+world++-}+writeFile :: FilePath -> Stream (Of String) IO r -> IO r+writeFile f = IO.withFile f IO.WriteMode . flip toHandle+ {-| Write 'String's to 'IO.stdout' using 'putStrLn' Unlike @stdoutLn@, @stdoutLn'@ does not handle a broken output pipe. Thus it can have a polymorphic return@@ -2481,7 +2597,7 @@ like 'MonadResource'. Thus I can independently filter and write to one file, but nub and write to another, or interact with a database and a logfile and the like: ->>> runResourceT $ (S.writeFile "hello2.txt" . S.nub) $ store (S.writeFile "hello.txt" . S.filter (/= "world")) $ each ["hello", "world", "goodbye", "world"]+>>> runResourceT $ (S.writeFile "hello2.txt" . S.nubOrd) $ store (S.writeFile "hello.txt" . S.filter (/= "world")) $ each ["hello", "world", "goodbye", "world"] >>> :! cat hello.txt hello goodbye@@ -2658,6 +2774,72 @@ +{- $merging+ These functions combine two sorted streams of orderable elements+ into one sorted stream. The elements of the merged stream are+ guaranteed to be in a sorted order if the two input streams are+ also sorted.++ The merge operation is /left-biased/: when merging two elements+ that compare as equal, the left element is chosen first.+-}++{- | Merge two streams of elements ordered with their 'Ord' instance.++ The return values of both streams are returned.++>>> S.print $ merge (each [1,3,5]) (each [2,4])+1+2+3+4+5+((), ())++-}+merge :: (Monad m, Ord a)+ => Stream (Of a) m r+ -> Stream (Of a) m s+ -> Stream (Of a) m (r, s)+merge = mergeBy compare+{-# INLINE merge #-}++{- | Merge two streams, ordering them by applying the given function to+ each element before comparing.++ The return values of both streams are returned.+-}+mergeOn :: (Monad m, Ord b)+ => (a -> b)+ -> Stream (Of a) m r+ -> Stream (Of a) m s+ -> Stream (Of a) m (r, s)+mergeOn f = mergeBy (comparing f)+{-# INLINE mergeOn #-}++{- | Merge two streams, ordering the elements using the given comparison function.++ The return values of both streams are returned.+-}+mergeBy :: Monad m+ => (a -> a -> Ordering)+ -> Stream (Of a) m r+ -> Stream (Of a) m s+ -> Stream (Of a) m (r, s)+mergeBy cmp = loop+ where+ loop str0 str1 = case str0 of+ Return r0 -> (\ r1 -> (r0, r1)) <$> str1+ Effect m -> Effect $ fmap (\ str -> loop str str1) m+ Step (a :> rest0) -> case str1 of+ Return r1 -> (\ r0 -> (r0, r1)) <$> str0+ Effect m -> Effect $ fmap (loop str0) m+ Step (b :> rest1) -> case cmp a b of+ LT -> Step (a :> loop rest0 str1)+ EQ -> Step (a :> loop rest0 str1) -- left-biased+ GT -> Step (b :> loop str0 rest1)+{-# INLINABLE mergeBy #-} + {- $maybes These functions discard the 'Nothing's that they encounter. They are analogous to the functions from @Data.Maybe@ that share their names.@@ -2716,7 +2898,7 @@ Left r -> return r Right (a,rest) -> do yield (sequ Seq.|> a)- window (Seq.drop 1 sequ Seq.|> a) rest+ window (Seq.drop 1 $ sequ Seq.|> a) rest setup 0 !sequ str = do yield sequ window (Seq.drop 1 sequ) str
streaming.cabal view
@@ -1,5 +1,5 @@ name: streaming-version: 0.2.1.0+version: 0.2.2.0 cabal-version: >=1.10 build-type: Simple synopsis: an elementary streaming prelude and general stream type.