on master help sign in
Merge pull request #194 from composewell/misc-fixes

Fixes for iterateM, uniq, fromIndicesM

commitb1106ffcc90fb772ec0cd01810faa33f10d49fd5 authorHarendra Kumar <harendra.kumar@gmail.com> dateMon 3 Jun 2019, 07:43 (7 years ago) parentcfd77bd4ac4891
6 files changed+129−26
Changelog.md+5 benchmark/LinearOps.hs+8 src/Streamly/Prelude.hs+17−11 src/Streamly/Streams/StreamD.hs+1−1 src/Streamly/Streams/StreamK.hs+15 test/Prop.hs+83−14
Changelog.md+5
@@ -4,6 +4,11 @@
44
55* Change the signature of `foldrM` to ensure that it is lazy
66* `scanx` would now require an additional `Monad m` constraint.
7+* Change the signature of `iterateM` to ensure that it is lazy.
8+
9+### Bug Fixes
10+
11+* Fix a bug that caused `uniq` function to yield the same element twice.
712
813### Major Enhancements
914
benchmark/LinearOps.hs+8
@@ -85,6 +85,14 @@ sourceFromList n = S.fromList [n..n+value]
8585sourceFromListM :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
8686sourceFromListM n = S.fromListM (Prelude.fmap return [n..n+value])
8787
88+{-# INLINE sourceFromIndices #-}
89+sourceFromIndices :: (Monad m, S.IsStream t) => Int -> t m Int
90+sourceFromIndices n = S.take value $ S.fromIndices (+ n)
91+
92+{-# INLINE sourceFromIndicesM #-}
93+sourceFromIndicesM :: (S.MonadAsync m, S.IsStream t) => Int -> t m Int
94+sourceFromIndicesM n = S.take value $ S.fromIndicesM (Prelude.fmap return (+ n))
95+
8896{-# INLINE sourceFromFoldable #-}
8997sourceFromFoldable :: S.IsStream t => Int -> t m Int
9098sourceFromFoldable n = S.fromFoldable [n..n+value]
src/Streamly/Prelude.hs+17−11
@@ -680,9 +680,8 @@ yieldM = K.yieldM
680680-- @since 0.6.0
681681{-# INLINE fromIndices #-}
682682fromIndices :: (IsStream t, Monad m) => (Int -> a) -> t m a
683−fromIndices = fromStreamD . D.fromIndices
683+fromIndices = fromStreamS . S.fromIndices
684684
685−-- XXX this needs to be concurrent
686685--
687686-- |
688687-- @
@@ -692,10 +691,17 @@ fromIndices = fromStreamD . D.fromIndices
692691-- Generate an infinite stream, whose values are the output of a monadic
693692-- function @f@ applied on the corresponding index. Index starts at 0.
694693--
694+-- /Concurrent/
695+--
695696-- @since 0.6.0
696−{-# INLINE fromIndicesM #-}
697−fromIndicesM :: (IsStream t, Monad m) => (Int -> m a) -> t m a
698−fromIndicesM = fromStreamD . D.fromIndicesM
697+{-# INLINE_EARLY fromIndicesM #-}
698+fromIndicesM :: (IsStream t, MonadAsync m) => (Int -> m a) -> t m a
699+fromIndicesM = K.fromIndicesM
700+
701+{-# RULES "fromIndicesM serial" fromIndicesM = fromIndicesMSerial #-}
702+{-# INLINE fromIndicesMSerial #-}
703+fromIndicesMSerial :: MonadAsync m => (Int -> m a) -> SerialT m a
704+fromIndicesMSerial = fromStreamS . S.fromIndicesM
699705
700706-- |
701707-- @
@@ -774,7 +780,7 @@ iterate step = fromStream . go
774780
775781-- |
776782-- @
777−-- iterateM f m = m \`consM` iterateM f m
783+-- iterateM f m = m >>= \a -> return a \`consM` iterateM f (f a)
778784-- @
779785--
780786-- Generate an infinite stream with the first element generated by the action
@@ -788,21 +794,21 @@ iterate step = fromStream . go
788794--
789795-- @
790796-- drain $ serially $ S.take 10 $ S.iterateM
791−-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
797+-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) (return 0)
792798--
793799-- drain $ asyncly $ S.take 10 $ S.iterateM
794−-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) 0
800+-- (\\x -> threadDelay 1000000 >> print x >> return (x + 1)) (return 0)
795801-- @
796802--
797803-- /Concurrent/
798804--
799805-- @since 0.1.2
800−iterateM :: (IsStream t, MonadAsync m) => (a -> m a) -> a -> t m a
806+iterateM :: (IsStream t, MonadAsync m) => (a -> m a) -> m a -> t m a
801807iterateM step = go
802808 where
803809 go s = K.mkStream $ \st stp sng yld -> do
804− next <- step s
805− K.foldStreamShared st stp sng yld (return s |: go next)
810+ next <- s
811+ K.foldStreamShared st stp sng yld (return next |: go (step next))
806812
807813------------------------------------------------------------------------------
808814-- Conversions
src/Streamly/Streams/StreamD.hs+1−1
@@ -2107,7 +2107,7 @@ uniq (Stream step state) = Stream step' (Nothing, state)
21072107 r <- step gst st
21082108 case r of
21092109 Yield y s | x == y -> return $ Skip (Just x, s)
2110− | otherwise -> return $ Yield x (Just y, s)
2110+ | otherwise -> return $ Yield y (Just y, s)
21112111 Skip s -> return $ Skip (Just x, s)
21122112 Stop -> return Stop
21132113
src/Streamly/Streams/StreamK.hs+15
@@ -63,6 +63,8 @@ module Streamly.Streams.StreamK
6363 , repeat
6464 , replicate
6565 , replicateM
66+ , fromIndices
67+ , fromIndicesM
6668
6769 -- ** Conversions
6870 , yield
@@ -283,6 +285,19 @@ replicate n a = go n
283285 where
284286 go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)
285287
288+{-# INLINE fromIndicesM #-}
289+fromIndicesM :: (IsStream t, MonadAsync m) => (Int -> m a) -> t m a
290+fromIndicesM gen = go 0
291+ where
292+ go i = mkStream $ \st stp sng yld -> do
293+ foldStreamShared st stp sng yld (gen i |: go (i + 1))
294+
295+{-# INLINE fromIndices #-}
296+fromIndices :: IsStream t => (Int -> a) -> t m a
297+fromIndices gen = go 0
298+ where
299+ go n = (gen n) `cons` go (n + 1)
300+
286301-------------------------------------------------------------------------------
287302-- Conversions
288303-------------------------------------------------------------------------------
test/Prop.hs+83−14
@@ -12,7 +12,7 @@ import Control.Exception
1212import Control.Monad (when, forM_, replicateM, replicateM_)
1313import Control.Monad.IO.Class (MonadIO(..))
1414import Data.Function ((&))
15−import Data.IORef (readIORef, modifyIORef, newIORef)
15+import Data.IORef (readIORef, modifyIORef, newIORef, modifyIORef', IORef)
1616import Data.List
1717 (sort, foldl', scanl', findIndices, findIndex, elemIndices,
1818 elemIndex, find, insertBy, intersperse, foldl1', (\\),
@@ -146,15 +146,56 @@ constructWithDoubleFromThenTo op l =
146146 in constructWithLen stream list op l
147147#endif
148148
149−constructWithIterate :: IsStream t => (t IO Int -> SerialT IO Int) -> Spec
150−constructWithIterate t = do
151− it "iterate" $
152− (S.toList . t . S.take 100) (S.iterate (+ 1) (0 :: Int))
153− `shouldReturn` take 100 (iterate (+ 1) 0)
154− it "iterateM" $ do
155− let addM y = return (y + 1)
156− S.toList . t . S.take 100 $ S.iterateM addM (0 :: Int)
157− `shouldReturn` take 100 (iterate (+ 1) 0)
149+constructWithIterate ::
150+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
151+constructWithIterate op len =
152+ withMaxSuccess maxTestCount $
153+ monadicIO $ do
154+ stream <-
155+ run $
156+ (S.toList . op . S.take (fromIntegral len))
157+ (S.iterate (+ 1) (0 :: Int))
158+ let list = take (fromIntegral len) (iterate (+ 1) 0)
159+ listEquals (==) stream list
160+
161+constructWithIterateM ::
162+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
163+constructWithIterateM op len =
164+ withMaxSuccess maxTestCount $
165+ monadicIO $ do
166+ mvl <- run (newIORef [] :: IO (IORef [Int]))
167+ let addM mv x y = modifyIORef' mv (++ [y + x]) >> return (y + x)
168+ list = take (fromIntegral len) (iterate (+ 1) 0)
169+ run $
170+ S.drain . op $
171+ S.take (fromIntegral len) $
172+ S.iterateM (addM mvl 1) (addM mvl 0 0 :: IO Int)
173+ streamEffect <- run $ readIORef mvl
174+ listEquals (==) streamEffect list
175+
176+constructWithFromIndices ::
177+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
178+constructWithFromIndices op len =
179+ withMaxSuccess maxTestCount $
180+ monadicIO $ do
181+ stream <-
182+ run $ (S.toList . op . S.take (fromIntegral len)) (S.fromIndices id)
183+ let list = take (fromIntegral len) (iterate (+ 1) 0)
184+ listEquals (==) stream list
185+
186+constructWithFromIndicesM ::
187+ IsStream t => (t IO Int -> SerialT IO Int) -> Word8 -> Property
188+constructWithFromIndicesM op len =
189+ withMaxSuccess maxTestCount $
190+ monadicIO $ do
191+ mvl <- run (newIORef [] :: IO (IORef [Int]))
192+ let addIndex mv i = modifyIORef' mv (++ [i]) >> return i
193+ list = take (fromIntegral len) (iterate (+ 1) 0)
194+ run $
195+ S.drain . op $
196+ S.take (fromIntegral len) $ S.fromIndicesM (addIndex mvl)
197+ streamEffect <- run $ readIORef mvl
198+ listEquals (==) streamEffect list
158199
159200-------------------------------------------------------------------------------
160201-- Concurrent generation
@@ -560,7 +601,8 @@ transformCombineOpsOrdered constr desc eq t = do
560601 transform (dropWhile (> 0)) t (S.dropWhile (> 0))
561602 prop (desc <> " scan") $ transform (scanl' (+) 0) t (S.scanl' (+) 0)
562603
563− -- XXX add uniq
604+ prop (desc <> " uniq") $ transform referenceUniq t S.uniq
605+
564606 prop (desc <> " deleteBy (<=) 0") $
565607 transform (deleteBy (<=) 0) t (S.deleteBy (<=) 0)
566608
@@ -599,10 +641,23 @@ wrapOutOfBounds f i x | null x = Nothing
599641 | otherwise = Just (f x i)
600642
601643wrapThe :: Eq a => [a] -> Maybe a
602−wrapThe (x:xs) | all (x ==) xs = Just x
603− | otherwise = Nothing
644+wrapThe (x:xs)
645+ | all (x ==) xs = Just x
646+ | otherwise = Nothing
604647wrapThe [] = Nothing
605648
649+-- This is the reference uniq implementation to compare uniq against,
650+-- we can use uniq from vector package, but for now this should
651+-- suffice.
652+referenceUniq :: Eq a => [a] -> [a]
653+referenceUniq = go
654+ where
655+ go [] = []
656+ go (x:[]) = [x]
657+ go (x:y:xs)
658+ | x == y = go (x : xs)
659+ | otherwise = x : go (y : xs)
660+
606661eliminationOps
607662 :: ([Int] -> t IO Int)
608663 -> String
@@ -971,10 +1026,24 @@ main = hspec
9711026 serialOps $ prop "serially DoubleFromThenTo" .
9721027 constructWithDoubleFromThenTo
9731028#endif
1029+
1030+ serialOps $ prop "serially iterate" . constructWithIterate
1031+
9741032 -- XXX test for all types of streams
975− constructWithIterate serially
1033+ serialOps $ prop "serially iterateM" . constructWithIterateM
1034+ -- take doesn't work well on concurrent streams. Even though it
1035+ -- seems like take only has a problem when used with parallely.
1036+ -- wSerialOps $ prop "wSerially iterateM" wSerially . constructWithIterate
1037+ -- aheadOps $ prop "aheadly iterateM" aheadly . onstructWithIterate
1038+ -- asyncOps $ prop "asyncly iterateM" asyncly . constructWithIterate
1039+ -- wAsyncOps $ prop "wAsyncly iterateM" wAsyncly . onstructWithIterate
1040+ -- parallelOps $ prop "parallely iterateM" parallely . onstructWithIterate
9761041 -- XXX add tests for fromIndices
9771042
1043+ serialOps $ prop "serially fromIndices" . constructWithFromIndices
1044+
1045+ serialOps $ prop "serially fromIndicesM" . constructWithFromIndicesM
1046+
9781047 describe "Functor operations" $ do
9791048 serialOps $ functorOps S.fromFoldable "serially" (==)
9801049 serialOps $ functorOps folded "serially folded" (==)