Add stream generation functions to Data.Stream

This commit is contained in:
Harendra Kumar 2022-08-07 23:40:28 +05:30
parent b528bae8ce
commit 98d3ea9d27
13 changed files with 911 additions and 139 deletions

View File

@ -13,10 +13,8 @@ module Stream.Common
( MonadAsync
-- Generation
, enumerateFromTo
, replicate
, unfoldrM
, fromListM
, fromFoldableM
, append
, append2
@ -32,7 +30,6 @@ module Stream.Common
, sourceUnfoldrAction
, sourceConcatMapId
, sourceFromFoldable
, sourceFromFoldableM
-- Benchmark stream elimination
, benchIOSink
@ -78,15 +75,12 @@ import Streamly.Prelude (foldl', scanl')
import qualified Streamly.Internal.Data.Stream.IsStream as Stream
import qualified Streamly.Prelude as Stream
import Streamly.Benchmark.Prelude
( composeN, sourceUnfoldr, sourceUnfoldr, sourceFromFoldable
, sourceFromFoldableM, sourceUnfoldrAction, sourceConcatMapId, benchIOSink
( composeN, sourceConcatMapId, benchIOSink
, concatStreamsWith, concatPairsWith
)
#else
import Control.DeepSeq (NFData)
import Streamly.Internal.Data.Stream (unfold)
import qualified Streamly.Internal.Data.Stream as Stream
import qualified Streamly.Internal.Data.Unfold as Unfold
#endif
import Gauge
@ -118,41 +112,25 @@ append2 = Stream.append2
drain :: Monad m => Stream m a -> m ()
drain = Stream.fold Fold.drain
{-# INLINE enumerateFromTo #-}
enumerateFromTo :: Monad m => Int -> Int -> Stream m Int
#ifdef USE_PRELUDE
enumerateFromTo = Stream.enumerateFromTo
#else
enumerateFromTo from to = Stream.unfold Unfold.enumerateFromTo (from, to)
#endif
{-# INLINE replicate #-}
replicate :: Monad m => Int -> a -> Stream m a
#ifdef USE_PRELUDE
replicate = Stream.replicate
#else
replicate n = Stream.unfold (Unfold.replicateM n) . return
#endif
{-# INLINE unfoldrM #-}
unfoldrM :: MonadAsync m => (b -> m (Maybe (a, b))) -> b -> Stream m a
#ifdef USE_PRELUDE
unfoldrM = Stream.unfoldrM
#else
unfoldrM step = Stream.unfold (Unfold.unfoldrM step)
#endif
{-# INLINE fromListM #-}
fromListM :: MonadAsync m => [m a] -> Stream m a
#ifdef USE_PRELUDE
fromListM = Stream.fromListM
#else
fromListM = Stream.unfold Unfold.fromListM
fromListM = Stream.sequence . Stream.fromList
#endif
{-# INLINE fromFoldableM #-}
fromFoldableM :: MonadAsync m => [m a] -> Stream m a
#ifdef USE_PRELUDE
fromFoldableM = Stream.fromFoldableM
#else
fromFoldableM = Stream.sequence . Stream.fromFoldable
#endif
{-# INLINE sourceUnfoldrM #-}
sourceUnfoldrM :: MonadAsync m => Int -> Int -> Stream m Int
sourceUnfoldrM count start = unfoldrM step start
sourceUnfoldrM count start = Stream.unfoldrM step start
where
@ -161,10 +139,9 @@ sourceUnfoldrM count start = unfoldrM step start
then return Nothing
else return (Just (cnt, cnt + 1))
#ifndef USE_PRELUDE
{-# INLINE sourceUnfoldr #-}
sourceUnfoldr :: Monad m => Int -> Int -> Stream m Int
sourceUnfoldr count start = unfold (Unfold.unfoldr step) start
sourceUnfoldr count start = Stream.unfoldr step start
where
@ -175,7 +152,7 @@ sourceUnfoldr count start = unfold (Unfold.unfoldr step) start
{-# INLINE sourceUnfoldrAction #-}
sourceUnfoldrAction :: (Monad m1, Monad m) => Int -> Int -> Stream m (m1 Int)
sourceUnfoldrAction value n = unfold (Unfold.unfoldr step) n
sourceUnfoldrAction value n = Stream.unfoldr step n
where
@ -188,10 +165,7 @@ sourceUnfoldrAction value n = unfold (Unfold.unfoldr step) n
sourceFromFoldable :: Int -> Int -> Stream m Int
sourceFromFoldable value n = Stream.fromFoldable [n..n+value]
{-# INLINE sourceFromFoldableM #-}
sourceFromFoldableM :: Monad m => Int -> Int -> Stream m Int
sourceFromFoldableM value n = Stream.fromFoldableM (fmap return [n..n+value])
#ifndef USE_PRELUDE
{-# INLINE benchIOSink #-}
benchIOSink
:: (NFData b)

View File

@ -22,7 +22,7 @@
module Stream.Exceptions (benchmarks) where
import Control.Exception (SomeException, Exception, throwIO)
import Stream.Common (drain, enumerateFromTo)
import Stream.Common (drain)
import Streamly.Internal.Data.Stream.Serial (SerialT)
import System.IO (Handle, hClose, hPutChar)
@ -36,7 +36,6 @@ import qualified Streamly.Internal.FileSystem.Handle as IFH
import qualified Streamly.Internal.Data.Stream.IsStream as Stream
#else
import qualified Streamly.Internal.Data.Stream as Stream
import qualified Streamly.Internal.Data.Unfold as Unfold
#endif
import Gauge hiding (env)
@ -59,7 +58,7 @@ replicateM :: Common.MonadAsync m => Int -> m a -> SerialT m a
#ifdef USE_PRELUDE
replicateM = Stream.replicateM
#else
replicateM = Stream.unfold . Unfold.replicateM
replicateM n = Stream.sequence . Stream.replicate n
#endif
data BenchException
@ -79,7 +78,7 @@ retryNoneSimple length from =
where
source = enumerateFromTo from (from + length)
source = Stream.enumerateFromTo from (from + length)
retryNone :: Int -> Int -> IO ()
retryNone length from = do
@ -121,7 +120,7 @@ retryUnknown length from = do
where
source = enumerateFromTo from (from + length)
source = Stream.enumerateFromTo from (from + length)
o_1_space_serial_exceptions :: Int -> [Benchmark]

View File

@ -32,15 +32,14 @@ import Test.Inspection
import qualified Streamly.Internal.Data.Stream.StreamD as D
#endif
import Streamly.Benchmark.Prelude (benchIO)
import qualified Stream.Common as Common
#ifdef USE_PRELUDE
import qualified Streamly.Internal.Data.Stream.IsStream as S
import Streamly.Benchmark.Prelude hiding
(benchIOSrc, sourceUnfoldrM, apDiscardFst, apDiscardSnd, apLiftA2, toNullAp
, monadThen, toNullM, toNullM3, filterAllInM, filterAllOutM, filterSome
, breakAfterSome, toListM, toListSome)
import Streamly.Benchmark.Prelude
( sourceFoldMapM, sourceFoldMapWith, sourceFoldMapWithM
, sourceFoldMapWithStream, concatFoldableWith, concatForFoldableWith)
#else
import Streamly.Benchmark.Prelude (benchIO)
import qualified Streamly.Internal.Data.Stream as S
#endif
import qualified Streamly.Internal.Data.Unfold as UF
@ -214,7 +213,7 @@ inspect $ 'concatMapPure `hasNoType` ''SPEC
{-# INLINE concatMapRepl #-}
concatMapRepl :: Int -> Int -> Int -> IO ()
concatMapRepl outer inner n =
drain $ S.concatMap (Common.replicate inner) (sourceUnfoldrM outer n)
drain $ S.concatMap (S.replicate inner) (sourceUnfoldrM outer n)
#ifdef INSPECTION
inspect $ hasNoTypeClasses 'concatMapRepl
@ -423,7 +422,7 @@ o_n_space_monad value =
, benchIO "(>>=) (sqrt n x sqrt n) (toListSome)" $
toListSome value
, benchIO "naive prime sieve (n/4)"
(\n -> S.fold Fold.sum $ sieve $ enumerateFromTo 2 (value `div` 4 + n))
(\n -> S.fold Fold.sum $ sieve $ S.enumerateFromTo 2 (value `div` 4 + n))
]
]

View File

@ -13,26 +13,23 @@ module Stream.Generate (benchmarks) where
import Data.Functor.Identity (Identity)
import qualified GHC.Exts as GHC
import qualified Stream.Common as Common
#ifdef USE_PRELUDE
import qualified GHC.Exts as GHC
import Streamly.Benchmark.Prelude (sourceFromFoldableM, absTimes)
import qualified Streamly.Prelude as S
import qualified Streamly.Internal.Data.Stream.IsStream as Stream
import qualified Prelude
#else
import qualified Streamly.Internal.Data.Stream as Stream
#endif
import qualified Prelude
import Gauge
import Streamly.Benchmark.Common
import Streamly.Internal.Data.Stream.Serial (SerialT)
#ifdef USE_PRELUDE
import Streamly.Prelude (MonadAsync)
import Stream.Common hiding (MonadAsync, replicate, enumerateFromTo)
import Streamly.Benchmark.Prelude hiding
(benchIOSrc, sourceUnfoldrM, apDiscardFst, apDiscardSnd, apLiftA2, toNullAp
, monadThen, toNullM, toNullM3, filterAllInM, filterAllOutM, filterSome
, breakAfterSome, toListM, toListSome)
import Stream.Common hiding (MonadAsync)
#else
import Stream.Common
#endif
@ -49,7 +46,14 @@ import Prelude hiding (repeat, replicate, iterate)
-- fromList
-------------------------------------------------------------------------------
#ifdef USE_PRELUDE
{-# INLINE sourceFromList #-}
sourceFromList :: Monad m => Int -> Int -> SerialT m Int
sourceFromList value n = Stream.fromList [n..n+value]
{-# INLINE sourceFromListM #-}
sourceFromListM :: MonadAsync m => Int -> Int -> SerialT m Int
sourceFromListM value n = fromListM (fmap return [n..n+value])
{-# INLINE sourceIsList #-}
sourceIsList :: Int -> Int -> SerialT Identity Int
sourceIsList value n = GHC.fromList [n..n+value]
@ -57,7 +61,6 @@ sourceIsList value n = GHC.fromList [n..n+value]
{-# INLINE sourceIsString #-}
sourceIsString :: Int -> Int -> SerialT Identity Char
sourceIsString value n = GHC.fromString (Prelude.replicate (n + value) 'a')
#endif
{-# INLINE readInstance #-}
readInstance :: String -> SerialT Identity Int
@ -76,56 +79,83 @@ readInstanceList str =
[(x,"")] -> x
_ -> error "readInstance: no parse"
#ifdef USE_PRELUDE
{-# INLINE repeat #-}
repeat :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
repeat count = S.take count . S.repeat
{-# INLINE repeatM #-}
repeatM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
repeatM count = S.take count . S.repeatM . return
repeat :: Monad m => Int -> Int -> SerialT m Int
repeat count = Stream.take count . Stream.repeat
{-# INLINE replicate #-}
replicate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
replicate = S.replicate
replicate :: Monad m => Int -> Int -> SerialT m Int
replicate = Stream.replicate
{-# INLINE replicateM #-}
replicateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
replicateM count = S.replicateM count . return
-------------------------------------------------------------------------------
-- enumerate
-------------------------------------------------------------------------------
{-# INLINE sourceIntFromTo #-}
sourceIntFromTo :: Monad m => Int -> Int -> SerialT m Int
sourceIntFromTo value n = Stream.enumerateFromTo n (n + value)
{-# INLINE sourceIntFromThenTo #-}
sourceIntFromThenTo :: Monad m => Int -> Int -> SerialT m Int
sourceIntFromThenTo value n = Stream.enumerateFromThenTo n (n + 1) (n + value)
{-# INLINE sourceFracFromTo #-}
sourceFracFromTo :: Monad m => Int -> Int -> SerialT m Double
sourceFracFromTo value n =
Stream.enumerateFromTo (fromIntegral n) (fromIntegral (n + value))
{-# INLINE sourceFracFromThenTo #-}
sourceFracFromThenTo :: Monad m => Int -> Int -> SerialT m Double
sourceFracFromThenTo value n = Stream.enumerateFromThenTo (fromIntegral n)
(fromIntegral n + 1.0001) (fromIntegral (n + value))
{-# INLINE sourceIntegerFromStep #-}
sourceIntegerFromStep :: Monad m => Int -> Int -> SerialT m Integer
sourceIntegerFromStep value n =
Stream.take value $ Stream.enumerateFromThen (fromIntegral n) (fromIntegral n + 1)
{-# INLINE enumerateFrom #-}
enumerateFrom :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerateFrom count = S.take count . S.enumerateFrom
enumerateFrom :: Monad m => Int -> Int -> SerialT m Int
enumerateFrom count = Stream.take count . Stream.enumerateFrom
{-# INLINE enumerateFromTo #-}
enumerateFromTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerateFromTo :: Monad m => Int -> Int -> SerialT m Int
enumerateFromTo = sourceIntFromTo
{-# INLINE enumerateFromThen #-}
enumerateFromThen :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerateFromThen count inp = S.take count $ S.enumerateFromThen inp (inp + 1)
enumerateFromThen :: Monad m => Int -> Int -> SerialT m Int
enumerateFromThen count inp = Stream.take count $ Stream.enumerateFromThen inp (inp + 1)
{-# INLINE enumerateFromThenTo #-}
enumerateFromThenTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerateFromThenTo :: Monad m => Int -> Int -> SerialT m Int
enumerateFromThenTo = sourceIntFromThenTo
-- n ~ 1
{-# INLINE enumerate #-}
enumerate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerate count n = S.take (count + n) S.enumerate
enumerate :: Monad m => Int -> Int -> SerialT m Int
enumerate count n = Stream.take (count + n) Stream.enumerate
-- n ~ 1
{-# INLINE enumerateTo #-}
enumerateTo :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
enumerateTo count n = S.enumerateTo (minBound + count + n)
enumerateTo :: Monad m => Int -> Int -> SerialT m Int
enumerateTo count n = Stream.enumerateTo (minBound + count + n)
{-# INLINE iterate #-}
iterate :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
iterate count = S.take count . S.iterate (+1)
iterate :: Monad m => Int -> Int -> SerialT m Int
iterate count = Stream.take count . Stream.iterate (+1)
{-# INLINE iterateM #-}
iterateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
iterateM count = S.take count . S.iterateM (return . (+1)) . return
iterateM :: MonadAsync m => Int -> Int -> SerialT m Int
iterateM count = Stream.take count . Stream.iterateM (return . (+1)) . return
#ifdef USE_PRELUDE
{-# INLINE repeatM #-}
repeatM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
repeatM count = S.take count . S.repeatM . return
{-# INLINE replicateM #-}
replicateM :: (MonadAsync m, S.IsStream t) => Int -> Int -> t m Int
replicateM count = S.replicateM count . return
{-# INLINE fromIndices #-}
fromIndices :: (Monad m, S.IsStream t) => Int -> Int -> t m Int
@ -151,37 +181,37 @@ o_1_space_generation value =
[ bgroup "generation"
[ benchIOSrc "unfoldr" (sourceUnfoldr value)
, benchIOSrc "unfoldrM" (sourceUnfoldrM value)
#ifdef USE_PRELUDE
, benchIOSrc "repeat" (repeat value)
, benchIOSrc "repeatM" (repeatM value)
, benchIOSrc "replicate" (replicate value)
, benchIOSrc "replicateM" (replicateM value)
, benchIOSrc "iterate" (iterate value)
, benchIOSrc "iterateM" (iterateM value)
, benchIOSrc "fromIndices" (fromIndices value)
, benchIOSrc "fromIndicesM" (fromIndicesM value)
, benchIOSrc "intFromTo" (sourceIntFromTo value)
, benchIOSrc "intFromThenTo" (sourceIntFromThenTo value)
, benchIOSrc "integerFromStep" (sourceIntegerFromStep value)
, benchIOSrc "fracFromThenTo" (sourceFracFromThenTo value)
, benchIOSrc "fracFromTo" (sourceFracFromTo value)
, benchIOSrc "fromList" (sourceFromList value)
, benchIOSrc "fromListM" (sourceFromListM value)
, benchPureSrc "IsList.fromList" (sourceIsList value)
, benchPureSrc "IsString.fromString" (sourceIsString value)
, benchIOSrc "fromListM" (sourceFromListM value)
, benchIOSrc "enumerateFrom" (enumerateFrom value)
, benchIOSrc "enumerateFromTo" (enumerateFromTo value)
, benchIOSrc "enumerateFromThen" (enumerateFromThen value)
, benchIOSrc "enumerateFromThenTo" (enumerateFromThenTo value)
, benchIOSrc "enumerate" (enumerate value)
, benchIOSrc "enumerateTo" (enumerateTo value)
#ifdef USE_PRELUDE
, benchIOSrc "repeatM" (repeatM value)
, benchIOSrc "replicateM" (replicateM value)
, benchIOSrc "fromIndices" (fromIndices value)
, benchIOSrc "fromIndicesM" (fromIndicesM value)
#endif
-- These essentially test cons and consM
, benchIOSrc "fromFoldable" (sourceFromFoldable value)
, benchIOSrc "fromFoldableM" (sourceFromFoldableM value)
#ifdef USE_PRELUDE
, benchIOSrc "fromFoldableM" (sourceFromFoldableM value)
, benchIOSrc "absTimes" $ absTimes value
#endif
, Common.benchIOSrc "mfix_10" (mfixUnfold 10)

View File

@ -42,7 +42,7 @@ import Prelude hiding (reverse, tail)
{-# INLINE sourceUnfoldrState #-}
sourceUnfoldrState :: Common.MonadAsync m =>
Int -> Int -> SerialT (StateT Int m) Int
sourceUnfoldrState value n = Common.unfoldrM step n
sourceUnfoldrState value n = Stream.unfoldrM step n
where
step cnt =
if cnt > n + value

View File

@ -32,11 +32,7 @@ import qualified Prelude
import qualified Stream.Common as Common
import qualified Streamly.Internal.Data.Unfold as Unfold
#ifdef USE_PRELUDE
import Streamly.Benchmark.Prelude hiding
( benchIOSrc, sourceUnfoldrM, apDiscardFst, apDiscardSnd, apLiftA2, toNullAp
, monadThen, toNullM, toNullM3, filterAllInM, filterAllOutM, filterSome
, breakAfterSome, toListM, toListSome, transformMapM, transformComposeMapM
, transformTeeMapM, transformZipMapM, mapN, mapM)
import Streamly.Benchmark.Prelude (benchIO)
import qualified Streamly.Internal.Data.Stream.IsStream as Stream
import Streamly.Internal.Data.Time.Units
#else
@ -247,7 +243,7 @@ o_n_space_mapping :: Int -> [Benchmark]
o_n_space_mapping value =
[ bgroup "mapping"
[ benchIO "naive prime sieve"
(\n -> Stream.fold FL.sum $ sieveScan $ Common.enumerateFromTo 2 (value + n))
(\n -> Stream.fold FL.sum $ sieveScan $ Stream.enumerateFromTo 2 (value + n))
]
]

View File

@ -14,6 +14,7 @@ module Streamly.Internal.Data.Stream.Bottom
-- * Generation
fromPure
, fromEffect
, fromList
, timesWith
, absTimesWith
, relTimesWith
@ -64,6 +65,7 @@ import Streamly.Internal.System.IO (defaultChunkSize)
import qualified Streamly.Internal.Data.Array.Unboxed.Type as A
import qualified Streamly.Internal.Data.Fold as Fold
import qualified Streamly.Internal.Data.Stream.Common as Common
import qualified Streamly.Internal.Data.Stream.StreamK as K
import qualified Streamly.Internal.Data.Stream.StreamD as D
@ -87,6 +89,20 @@ import Streamly.Internal.Data.Stream.Type
-- >>> import qualified Streamly.Internal.Data.Parser as Parser
-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
------------------------------------------------------------------------------
-- Generation
------------------------------------------------------------------------------
-- |
-- >>> fromList = Prelude.foldr Stream.cons Stream.nil
--
-- Construct a stream from a list of pure values. This is more efficient than
-- 'fromFoldable'.
--
{-# INLINE fromList #-}
fromList :: Monad m => [a] -> Stream m a
fromList = fromStreamK . Common.fromList
------------------------------------------------------------------------------
-- Generation - Time related
------------------------------------------------------------------------------
@ -100,8 +116,8 @@ import Streamly.Internal.Data.Stream.Type
-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.
--
-- >>> import Control.Concurrent (threadDelay)
-- >>> import Streamly.Internal.Data.Stream.Generate as Stream (timesWith)
-- >>> Stream.mapM_ (\x -> print x >> threadDelay 1000000) $ Stream.take 3 $ Stream.timesWith 0.01
-- >>> f = Fold.drainBy (\x -> print x >> threadDelay 1000000)
-- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
@ -119,7 +135,8 @@ timesWith g = fromStreamD $ D.times g
-- expensive in terms of CPU usage. Any granularity lower than 1 ms is treated
-- as 1 ms.
--
-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01
-- >>> f = Fold.drainBy print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
@ -137,7 +154,8 @@ absTimesWith = fmap (uncurry addToAbsTime64) . timesWith
-- clock is more expensive in terms of CPU usage. Any granularity lower than 1
-- ms is treated as 1 ms.
--
-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
-- >>> f = Fold.drainBy print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
@ -172,7 +190,7 @@ foldContinue f s = D.foldContinue f $ toStreamD s
-- 'Fold' can terminate early without consuming the full stream. See the
-- documentation of individual 'Fold's for termination behavior.
--
-- >>> Stream.fold Fold.sum (Stream.unfold Unfold.enumerateFromTo (1, 100))
-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)
-- 5050
--
-- Folds never fail, therefore, they produce a default value even when no input
@ -224,7 +242,7 @@ foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)
--
-- Same as 'fmap'.
--
-- >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.unfold Unfold.fromList [1,2,3]
-- >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.fromList [1,2,3]
-- [2,3,4]
--
{-# INLINE map #-}
@ -239,7 +257,7 @@ map f = fromStreamD . D.map f . toStreamD
--
-- >>> import Data.Maybe (fromJust)
-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)
-- >>> s = Stream.unfold Unfold.enumerateFromTo (1.0, 100.0)
-- >>> s = Stream.enumerateFromTo 1.0 100.0
-- >>> :{
-- Stream.fold Fold.toList
-- $ fmap (fromJust . fst)
@ -340,7 +358,7 @@ findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)
-- | Insert an effect and its output before consuming an element of a stream
-- except the first one.
--
-- >>> input = Stream.unfold Unfold.fromList "hello"
-- >>> input = Stream.fromList "hello"
-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input
-- h.,e.,l.,l.,o"h,e,l,l,o"
--
@ -433,11 +451,11 @@ concatM generator = concatMapM (\() -> generator) (fromPure ())
-- termination aligns:
--
-- >>> f = Fold.take 2 Fold.sum
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList []
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.fromList []
-- [0]
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..9]
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.fromList [1..9]
-- [3,7,11,15,9]
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.unfold Unfold.fromList [1..10]
-- >>> Stream.fold Fold.toList $ Stream.foldManyPost f $ Stream.fromList [1..10]
-- [3,7,11,15,19,0]
--
-- /Pre-release/
@ -468,8 +486,8 @@ zipWithM f m1 m2 = fromStreamD $ D.zipWithM f (toStreamD m1) (toStreamD m2)
-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends
-- first, the element @a@ from previous evaluation of stream @a@ is discarded.
--
-- >>> s1 = Stream.unfold Unfold.fromList [1,2,3]
-- >>> s2 = Stream.unfold Unfold.fromList [4,5,6]
-- >>> s1 = Stream.fromList [1,2,3]
-- >>> s2 = Stream.fromList [4,5,6]
-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2
-- [5,7,9]
--

View File

@ -0,0 +1,580 @@
-- |
-- Module : Streamly.Internal.Data.Stream.Enumerate
-- Copyright : (c) 2018 Composewell Technologies
--
-- License : BSD3
-- Maintainer : streamly@composewell.com
-- Stability : experimental
-- Portability : GHC
--
-- The functions defined in this module should be rarely needed for direct use,
-- try to use the operations from the 'Enumerable' type class
-- instances instead.
--
-- This module provides an 'Enumerable' type class to enumerate 'Enum' types
-- into a stream. The operations in this type class correspond to similar
-- perations in the 'Enum' type class, the only difference is that they produce
-- a stream instead of a list. These operations cannot be defined generically
-- based on the 'Enum' type class. We provide instances for commonly used
-- types. If instances for other types are needed convenience functions defined
-- in this module can be used to define them. Alternatively, these functions
-- can be used directly.
-- XXX The Unfold.Enumeration module is more modular, check the differences and
-- reconcile the two.
module Streamly.Internal.Data.Stream.Enumerate
(
Enumerable (..)
-- ** Enumerating 'Bounded' 'Enum' Types
, enumerate
, enumerateTo
, enumerateFromBounded
-- ** Enumerating 'Enum' Types not larger than 'Int'
, enumerateFromToSmall
, enumerateFromThenToSmall
, enumerateFromThenSmallBounded
-- ** Enumerating 'Bounded' 'Integral' Types
, enumerateFromIntegral
, enumerateFromThenIntegral
-- ** Enumerating 'Integral' Types
, enumerateFromToIntegral
, enumerateFromThenToIntegral
-- ** Enumerating unbounded 'Integral' Types
, enumerateFromStepIntegral
-- ** Enumerating 'Fractional' Types
, enumerateFromFractional
, enumerateFromToFractional
, enumerateFromThenFractional
, enumerateFromThenToFractional
)
where
import Data.Fixed
import Data.Int
import Data.Ratio
import Data.Word
import Numeric.Natural
import Data.Functor.Identity (Identity(..))
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD)
import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D
-- $setup
-- >>> import Streamly.Data.Fold as Fold
-- >>> import Streamly.Internal.Data.Stream as Stream
-- >>> import Streamly.Internal.Data.Stream.Enumerate as Stream
-------------------------------------------------------------------------------
-- Enumeration of Integral types
-------------------------------------------------------------------------------
--
-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose
-- first element is @from@ and the successive elements are in increments of
-- @step@.
--
-- CAUTION: This function is not safe for finite integral types. It does not
-- check for overflow, underflow or bounds.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)
-- [0,-2,-4]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromStepIntegral #-}
enumerateFromStepIntegral
:: (Monad m, Integral a)
=> a -> a -> Stream m a
enumerateFromStepIntegral from stride =
fromStreamD $ D.enumerateFromStepIntegral from stride
-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a
-- stream whose first element is @from@ and the successive elements are in
-- increments of @1@. The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)
-- [0,1,2,3]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromIntegral #-}
enumerateFromIntegral
:: (Monad m, Integral a, Bounded a)
=> a -> Stream m a
enumerateFromIntegral from = fromStreamD $ D.enumerateFromIntegral from
-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from
-- then@ generates a stream whose first element is @from@, the second element
-- is @then@ and the successive elements are in increments of @then - from@.
-- The stream is bounded by the size of the 'Integral' type.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenIntegral #-}
enumerateFromThenIntegral
:: (Monad m, Integral a, Bounded a)
=> a -> a -> Stream m a
enumerateFromThenIntegral from next =
fromStreamD $ D.enumerateFromThenIntegral from next
-- | Enumerate an 'Integral' type up to a given limit.
-- @enumerateFromToIntegral from to@ generates a finite stream whose first
-- element is @from@ and successive elements are in increments of @1@ up to
-- @to@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4
-- [0,1,2,3,4]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromToIntegral #-}
enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a
enumerateFromToIntegral from to =
fromStreamD $ D.enumerateFromToIntegral from to
-- | Enumerate an 'Integral' type in steps up to a given limit.
-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose
-- first element is @from@, the second element is @then@ and the successive
-- elements are in increments of @then - from@ up to @to@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToIntegral #-}
enumerateFromThenToIntegral
:: (Monad m, Integral a)
=> a -> a -> a -> Stream m a
enumerateFromThenToIntegral from next to =
fromStreamD $ D.enumerateFromThenToIntegral from next to
-------------------------------------------------------------------------------
-- Enumeration of Fractional types
-------------------------------------------------------------------------------
--
-- Even though the underlying implementation of enumerateFromFractional and
-- enumerateFromThenFractional works for any 'Num' we have restricted these to
-- 'Fractional' because these do not perform any bounds check, in contrast to
-- integral versions and are therefore not equivalent substitutes for those.
--
-- | Numerically stable enumeration from a 'Fractional' number in steps of size
-- @1@. @enumerateFromFractional from@ generates a stream whose first element
-- is @from@ and the successive elements are in increments of @1@. No overflow
-- or underflow checks are performed.
--
-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1
-- [1.1,2.1,3.1,4.1]
--
-- @
--
--
-- @since 0.6.0
{-# INLINE enumerateFromFractional #-}
enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a
enumerateFromFractional from = fromStreamD $ D.enumerateFromNum from
-- | Numerically stable enumeration from a 'Fractional' number in steps.
-- @enumerateFromThenFractional from then@ generates a stream whose first
-- element is @from@, the second element is @then@ and the successive elements
-- are in increments of @then - from@. No overflow or underflow checks are
-- performed.
--
-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)
-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]
--
-- @
--
-- @since 0.6.0
{-# INLINE enumerateFromThenFractional #-}
enumerateFromThenFractional
:: (Monad m, Fractional a)
=> a -> a -> Stream m a
enumerateFromThenFractional from next = fromStreamD $ D.enumerateFromThenNum from next
-- | Numerically stable enumeration from a 'Fractional' number to a given
-- limit. @enumerateFromToFractional from to@ generates a finite stream whose
-- first element is @from@ and successive elements are in increments of @1@ up
-- to @to@.
--
-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6
-- [1.1,2.1,3.1,4.1,5.1]
--
-- @
--
-- Notice that the last element is equal to the specified @to@ value after
-- rounding to the nearest integer.
--
-- @since 0.6.0
{-# INLINE enumerateFromToFractional #-}
enumerateFromToFractional
:: (Monad m, Fractional a, Ord a)
=> a -> a -> Stream m a
enumerateFromToFractional from to =
fromStreamD $ D.enumerateFromToFractional from to
-- | Numerically stable enumeration from a 'Fractional' number in steps up to a
-- given limit. @enumerateFromThenToFractional from then to@ generates a
-- finite stream whose first element is @from@, the second element is @then@
-- and the successive elements are in increments of @then - from@ up to @to@.
--
-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For
-- example:
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6
-- [0.1,2.0,3.9,5.799999999999999]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)
-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]
--
-- @
--
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToFractional #-}
enumerateFromThenToFractional
:: (Monad m, Fractional a, Ord a)
=> a -> a -> a -> Stream m a
enumerateFromThenToFractional from next to =
fromStreamD $ D.enumerateFromThenToFractional from next to
-------------------------------------------------------------------------------
-- Enumeration of Enum types not larger than Int
-------------------------------------------------------------------------------
--
-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.
--
-- @since 0.6.0
{-# INLINE enumerateFromToSmall #-}
enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a
enumerateFromToSmall from to =
fmap toEnum
$ enumerateFromToIntegral (fromEnum from) (fromEnum to)
-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.
--
-- @since 0.6.0
{-# INLINE enumerateFromThenToSmall #-}
enumerateFromThenToSmall :: (Monad m, Enum a)
=> a -> a -> a -> Stream m a
enumerateFromThenToSmall from next to =
fmap toEnum
$ enumerateFromThenToIntegral
(fromEnum from) (fromEnum next) (fromEnum to)
-- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.
--
-- Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a
-- type is bounded but does not have a 'Bounded' instance then we can go on
-- enumerating it beyond the legal values of the type, resulting in the failure
-- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'
-- instance for this function to be safely used.
--
-- @since 0.6.0
{-# INLINE enumerateFromThenSmallBounded #-}
enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)
=> a -> a -> Stream m a
enumerateFromThenSmallBounded from next =
if fromEnum next >= fromEnum from
then enumerateFromThenTo from next maxBound
else enumerateFromThenTo from next minBound
-------------------------------------------------------------------------------
-- Enumerable type class
-------------------------------------------------------------------------------
--
-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream
-- enumerations like this:
--
-- {-# RULES "fromList enumFrom" [1]
-- forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}
--
-- But this does not work because enumFrom is a class method and GHC rewrites
-- it quickly, so we do not get a chance to have our rule fired.
-- | Types that can be enumerated as a stream. The operations in this type
-- class are equivalent to those in the 'Enum' type class, except that these
-- generate a stream instead of a list. Use the functions in
-- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.
--
-- @since 0.6.0
class Enum a => Enumerable a where
-- | @enumerateFrom from@ generates a stream starting with the element
-- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
-- generating an infinite stream when the type is not 'Bounded'.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)
-- [0,1,2,3]
--
-- @
--
-- For 'Fractional' types, enumeration is numerically stable. However, no
-- overflow or underflow checks are performed.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1
-- [1.1,2.1,3.1,4.1]
--
-- @
--
-- @since 0.6.0
enumerateFrom :: (Monad m) => a -> Stream m a
-- | Generate a finite stream starting with the element @from@, enumerating
-- the type up to the value @to@. If @to@ is smaller than @from@ then an
-- empty stream is returned.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4
-- [0,1,2,3,4]
--
-- @
--
-- For 'Fractional' types, the last element is equal to the specified @to@
-- value after rounding to the nearest integral value.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4
-- [1.1,2.1,3.1,4.1]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6
-- [1.1,2.1,3.1,4.1,5.1]
--
-- @
--
-- @since 0.6.0
enumerateFromTo :: (Monad m) => a -> a -> Stream m a
-- | @enumerateFromThen from then@ generates a stream whose first element
-- is @from@, the second element is @then@ and the successive elements are
-- in increments of @then - from@. Enumeration can occur downwards or
-- upwards depending on whether @then@ comes before or after @from@. For
-- 'Bounded' types the stream ends when 'maxBound' is reached, for
-- unbounded types it keeps enumerating infinitely.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
enumerateFromThen :: (Monad m) => a -> a -> Stream m a
-- | @enumerateFromThenTo from then to@ generates a finite stream whose
-- first element is @from@, the second element is @then@ and the successive
-- elements are in increments of @then - from@ up to @to@. Enumeration can
-- occur downwards or upwards depending on whether @then@ comes before or
-- after @from@.
--
-- @
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6
-- [0,2,4,6]
--
-- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)
-- [0,-2,-4,-6]
--
-- @
--
-- @since 0.6.0
enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a
-- MAYBE: Sometimes it is more convenient to know the count rather then the
-- ending or starting element. For those cases we can define the folllowing
-- APIs. All of these will work only for bounded types if we represent the
-- count by Int.
--
-- enumerateN
-- enumerateFromN
-- enumerateToN
-- enumerateFromStep
-- enumerateFromStepN
-------------------------------------------------------------------------------
-- Convenient functions for bounded types
-------------------------------------------------------------------------------
--
-- |
-- > enumerate = enumerateFrom minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'
--
-- @since 0.6.0
{-# INLINE enumerate #-}
enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a
enumerate = enumerateFrom minBound
-- |
-- > enumerateTo = enumerateFromTo minBound
--
-- Enumerate a 'Bounded' type from its 'minBound' to specified value.
--
-- @since 0.6.0
{-# INLINE enumerateTo #-}
enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a
enumerateTo = enumerateFromTo minBound
-- |
-- > enumerateFromBounded = enumerateFromTo from maxBound
--
-- 'enumerateFrom' for 'Bounded' 'Enum' types.
--
-- @since 0.6.0
{-# INLINE enumerateFromBounded #-}
enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)
=> a -> Stream m a
enumerateFromBounded from = enumerateFromTo from maxBound
-------------------------------------------------------------------------------
-- Enumerable Instances
-------------------------------------------------------------------------------
--
-- For Enum types smaller than or equal to Int size.
#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE) \
instance Enumerable SMALL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromBounded; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenSmallBounded; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToSmall; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToSmall }
ENUMERABLE_BOUNDED_SMALL(())
ENUMERABLE_BOUNDED_SMALL(Bool)
ENUMERABLE_BOUNDED_SMALL(Ordering)
ENUMERABLE_BOUNDED_SMALL(Char)
-- For bounded Integral Enum types, may be larger than Int.
#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE) \
instance Enumerable INTEGRAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromIntegral; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenIntegral; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToIntegral; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToIntegral }
ENUMERABLE_BOUNDED_INTEGRAL(Int)
ENUMERABLE_BOUNDED_INTEGRAL(Int8)
ENUMERABLE_BOUNDED_INTEGRAL(Int16)
ENUMERABLE_BOUNDED_INTEGRAL(Int32)
ENUMERABLE_BOUNDED_INTEGRAL(Int64)
ENUMERABLE_BOUNDED_INTEGRAL(Word)
ENUMERABLE_BOUNDED_INTEGRAL(Word8)
ENUMERABLE_BOUNDED_INTEGRAL(Word16)
ENUMERABLE_BOUNDED_INTEGRAL(Word32)
ENUMERABLE_BOUNDED_INTEGRAL(Word64)
-- For unbounded Integral Enum types.
#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE) \
instance Enumerable INTEGRAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom from = enumerateFromStepIntegral from 1; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen from next = \
enumerateFromStepIntegral from (next - from); \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToIntegral; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToIntegral }
ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)
#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT) \
instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where { \
{-# INLINE enumerateFrom #-}; \
enumerateFrom = enumerateFromFractional; \
{-# INLINE enumerateFromThen #-}; \
enumerateFromThen = enumerateFromThenFractional; \
{-# INLINE enumerateFromTo #-}; \
enumerateFromTo = enumerateFromToFractional; \
{-# INLINE enumerateFromThenTo #-}; \
enumerateFromThenTo = enumerateFromThenToFractional }
ENUMERABLE_FRACTIONAL(Float,)
ENUMERABLE_FRACTIONAL(Double,)
ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)
ENUMERABLE_FRACTIONAL((Ratio a),Integral a)
instance Enumerable a => Enumerable (Identity a) where
{-# INLINE enumerateFrom #-}
enumerateFrom (Identity from) =
fmap Identity $ enumerateFrom from
{-# INLINE enumerateFromThen #-}
enumerateFromThen (Identity from) (Identity next) =
fmap Identity $ enumerateFromThen from next
{-# INLINE enumerateFromTo #-}
enumerateFromTo (Identity from) (Identity to) =
fmap Identity $ enumerateFromTo from to
{-# INLINE enumerateFromThenTo #-}
enumerateFromThenTo (Identity from) (Identity next) (Identity to) =
fmap Identity
$ enumerateFromThenTo from next to
-- TODO
{-
instance Enumerable a => Enumerable (Last a)
instance Enumerable a => Enumerable (First a)
instance Enumerable a => Enumerable (Max a)
instance Enumerable a => Enumerable (Min a)
instance Enumerable a => Enumerable (Const a b)
instance Enumerable (f a) => Enumerable (Alt f a)
instance Enumerable (f a) => Enumerable (Ap f a)
-}

View File

@ -1,3 +1,5 @@
{-# OPTIONS_GHC -Wno-orphans #-}
-- |
-- Module : Streamly.Internal.Data.Stream.Generate
-- Copyright : (c) 2017 Composewell Technologies
@ -6,6 +8,15 @@
-- Stability : experimental
-- Portability : GHC
--
-- Some idioms:
--
-- >>> fromEffect = Stream.sequence . Stream.fromPure
-- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
-- >>> repeatM = Stream.sequence . Stream.repeat
-- >>> replicateM n = Stream.sequence . Stream.replicate n
-- >>> fromListM = Stream.sequence . Stream.fromList
-- >>> fromFoldableM = Stream.sequence . Stream.fromFoldable
--
module Streamly.Internal.Data.Stream.Generate
(
-- * Primitives
@ -17,51 +28,79 @@ module Streamly.Internal.Data.Stream.Generate
-- * From 'Unfold'
, unfold
-- * Unfolding
, unfoldr
, unfoldrM
-- * From Values
, Stream.fromPure
, Stream.fromEffect
, repeat
, replicate
-- * Enumeration
, Enumerable (..)
, enumerate
, enumerateTo
-- * Time Enumeration
, times
, timesWith
, absTimes
, absTimesWith
, relTimes
, relTimesWith
, durations
, timeout
-- * Iteration
, iterate
, iterateM
-- * Cyclic Elements
, mfix
-- * From Containers
, Bottom.fromList
, fromFoldable
, fromFoldableM
-- * From memory
, fromPtr
, fromPtrN
, fromByteStr#
-- , fromByteArray#
, fromUnboxedIORef
)
where
#include "inline.hs"
import Control.Monad.IO.Class (MonadIO)
import Data.Word (Word8)
import Foreign.Storable (Storable)
import GHC.Exts (Addr#, Ptr (Ptr))
import Streamly.Internal.Data.Stream.Bottom
(absTimesWith, relTimesWith, timesWith)
import Streamly.Internal.Data.Stream.Type (Stream, fromStreamK, toStreamK)
import Streamly.Internal.Data.Stream.Enumerate
(Enumerable(..), enumerate, enumerateTo)
import Streamly.Internal.Data.Stream.Type
(Stream, fromStreamD, fromStreamK, toStreamK)
import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)
import Streamly.Internal.Data.Unboxed (Unboxed)
import Streamly.Internal.Data.Unfold.Type (Unfold)
import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed
import qualified Streamly.Internal.Data.Stream.Bottom as Bottom
import qualified Streamly.Internal.Data.Stream.StreamD as D
import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
import qualified Streamly.Internal.Data.Stream.Type as Stream
import Prelude hiding (iterate, replicate, repeat)
-- $setup
-- >>> :m
-- >>> import Control.Concurrent (threadDelay)
-- >>> import Data.Function (fix)
-- >>> import Prelude hiding (take)
-- >>> import Data.Function ((&))
-- >>> import qualified Streamly.Data.Fold as Fold
-- >>> import qualified Streamly.Data.Unfold as Unfold
-- >>> import qualified Streamly.Internal.Data.Stream as Stream
@ -84,6 +123,77 @@ import qualified Streamly.Internal.Data.Stream.Type as Stream
unfold :: Monad m => Unfold m a b -> a -> Stream m b
unfold unf = Stream.fromStreamD . D.unfold unf
------------------------------------------------------------------------------
-- Generation by Unfolding
------------------------------------------------------------------------------
-- |
-- >>> :{
-- unfoldr step s =
-- case step s of
-- Nothing -> Stream.nil
-- Just (a, b) -> a `Stream.cons` unfoldr step b
-- :}
--
-- Build a stream by unfolding a /pure/ step function @step@ starting from a
-- seed @s@. The step function returns the next element in the stream and the
-- next seed value. When it is done it returns 'Nothing' and the stream ends.
-- For example,
--
-- >>> :{
-- let f b =
-- if b > 2
-- then Nothing
-- else Just (b, b + 1)
-- in Stream.fold Fold.toList $ Stream.unfoldr f 0
-- :}
-- [0,1,2]
--
{-# INLINE_EARLY unfoldr #-}
unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a
unfoldr step seed = fromStreamD (D.unfoldr step seed)
{-# RULES "unfoldr fallback to StreamK" [1]
forall a b. D.toStreamK (D.unfoldr a b) = K.unfoldr a b #-}
-- | Build a stream by unfolding a /monadic/ step function starting from a
-- seed. The step function returns the next element in the stream and the next
-- seed value. When it is done it returns 'Nothing' and the stream ends. For
-- example,
--
-- >>> :{
-- let f b =
-- if b > 2
-- then return Nothing
-- else return (Just (b, b + 1))
-- in Stream.fold Fold.toList $ Stream.unfoldrM f 0
-- :}
-- [0,1,2]
--
--
{-# INLINE unfoldrM #-}
unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a
unfoldrM step = fromStreamD . D.unfoldrM step
------------------------------------------------------------------------------
-- From Values
------------------------------------------------------------------------------
-- |
-- Generate an infinite stream by repeating a pure value.
--
{-# INLINE_NORMAL repeat #-}
repeat :: Monad m => a -> Stream m a
repeat = fromStreamD . D.repeat
-- |
-- >>> replicate n = Stream.take n . Stream.repeat
--
-- Generate a stream of length @n@ by repeating a value @n@ times.
--
{-# INLINE_NORMAL replicate #-}
replicate :: Monad m => Int -> a -> Stream m a
replicate n = fromStreamD . D.replicate n
------------------------------------------------------------------------------
-- Time Enumeration
------------------------------------------------------------------------------
@ -93,7 +203,8 @@ unfold unf = Stream.fromStreamD . D.unfold unf
-- (epoch) denoting the start of the stream and the second component is a time
-- relative to the reference.
--
-- >>> Stream.mapM_ (\x -> print x >> threadDelay 1000000) $ Stream.take 3 $ Stream.times
-- >>> f = Fold.drainBy (\x -> print x >> threadDelay 1000000)
-- >>> Stream.fold f $ Stream.take 3 $ Stream.times
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))
@ -109,7 +220,8 @@ times = timesWith 0.01
-- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms
-- granularity.
--
-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes
-- >>> f = Fold.drainBy print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
-- AbsTime (TimeSpec {sec = ..., nsec = ...})
@ -125,7 +237,8 @@ absTimes = fmap (uncurry addToAbsTime64) times
-- | @relTimes@ returns a stream of relative time values starting from 0,
-- using a clock of granularity 10 ms.
--
-- >>> Stream.mapM_ print $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes
-- >>> f = Fold.drainBy print
-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
-- RelTime64 (NanoSecond64 ...)
@ -138,6 +251,72 @@ absTimes = fmap (uncurry addToAbsTime64) times
relTimes :: MonadIO m => Stream m RelTime64
relTimes = fmap snd times
-- | @durations g@ returns a stream of relative time values measuring the time
-- elapsed since the immediate predecessor element of the stream was generated.
-- The first element of the stream is always 0. @durations@ uses a clock of
-- granularity @g@ specified in seconds. A low granularity clock is more
-- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.
-- Durations lower than 1 ms will be 0.
--
-- Note: This API is not safe on 32-bit machines.
--
-- /Unimplemented/
--
{-# INLINE durations #-}
durations :: -- Monad m =>
Double -> t m RelTime64
durations = undefined
-- | Generate a singleton event at or after the specified absolute time. Note
-- that this is different from a threadDelay, a threadDelay starts from the
-- time when the action is evaluated, whereas if we use AbsTime based timeout
-- it will immediately expire if the action is evaluated too late.
--
-- /Unimplemented/
--
{-# INLINE timeout #-}
timeout :: -- Monad m =>
AbsTime -> t m ()
timeout = undefined
------------------------------------------------------------------------------
-- Iterating functions
------------------------------------------------------------------------------
-- |
-- >>> iterate f x = x `Stream.cons` iterate f x
--
-- Generate an infinite stream with @x@ as the first element and each
-- successive element derived by applying the function @f@ on the previous
-- element.
--
-- >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1
-- [1,2,3,4,5]
--
{-# INLINE_NORMAL iterate #-}
iterate :: Monad m => (a -> a) -> a -> Stream m a
iterate step = fromStreamD . D.iterate step
-- |
-- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)
--
-- Generate an infinite stream with the first element generated by the action
-- @m@ and each successive element derived by applying the monadic function
-- @f@ on the previous element.
--
-- >>> :{
-- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)
-- & Stream.take 3
-- & Stream.fold Fold.toList
-- :}
-- 0
-- 1
-- [0,1,2]
--
{-# INLINE iterateM #-}
iterateM :: Monad m => (a -> m a) -> m a -> Stream m a
iterateM step = fromStreamD . D.iterateM step
-- | We can define cyclic structures using @let@:
--
-- >>> let (a, b) = ([1, b], head a) in (a, b)
@ -176,8 +355,8 @@ relTimes = fmap snd times
-- where
-- f action = do
-- let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act
-- x <- Stream.unfold Unfold.fromListM [incr 1 action, incr 2 action]
-- y <- Stream.unfold Unfold.fromList [4,5]
-- x <- Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action]
-- y <- Stream.fromList [4,5]
-- return (x, y)
-- :}
--
@ -206,18 +385,6 @@ mfix f = fromStreamK $ K.mfix (toStreamK . f)
fromFoldable :: Foldable f => f a -> Stream m a
fromFoldable = fromStreamK . K.fromFoldable
-- |
-- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil
--
-- Construct a stream from a 'Foldable' containing monadic actions.
--
-- >>> Stream.fold Fold.toList $ Stream.fromFoldableM $ map return [1,2,3]
-- [1,2,3]
--
{-# INLINE fromFoldableM #-}
fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a
fromFoldableM = Prelude.foldr Stream.consM Stream.nil
------------------------------------------------------------------------------
-- From pointers
------------------------------------------------------------------------------
@ -260,3 +427,10 @@ fromByteStr# :: MonadIO m => Addr# -> Stream m Word8
fromByteStr# addr =
Stream.fromStreamD $ D.takeWhile (/= 0) $ D.fromPtr $ Ptr addr
-- | Construct a stream by reading an 'Unboxed' 'IORef' repeatedly.
--
-- /Pre-release/
--
{-# INLINE fromUnboxedIORef #-}
fromUnboxedIORef :: (MonadIO m, Unboxed a) => Unboxed.IORef a -> Stream m a
fromUnboxedIORef = fromStreamD . Unboxed.toStreamD

View File

@ -94,7 +94,6 @@ where
import Control.Monad.IO.Class (MonadIO(..))
import Foreign.Ptr (Ptr, plusPtr)
import Foreign.Storable (Storable (peek), sizeOf)
import Streamly.Internal.Control.Concurrent (MonadAsync)
import Streamly.Internal.Data.Time.Clock
(Clock(Monotonic), asyncClock, readClock)
import Streamly.Internal.Data.Time.Units
@ -452,10 +451,9 @@ iterate step st = iterateM (return . step) (return st)
-- From containers
-------------------------------------------------------------------------------
-- 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 :: Monad m => [m a] -> Stream m a
#ifdef USE_UNFOLDS_EVERYWHERE
fromListM = unfold Unfold.fromListM
#else

View File

@ -294,6 +294,7 @@ fromPure = fromStreamK . K.fromPure
-- | Create a singleton stream from a monadic action.
--
-- >>> fromEffect m = m `consM` Stream.nil
-- >>> fromEffect = Stream.sequence . Stream.fromPure
--
-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")
-- hello

View File

@ -308,6 +308,7 @@ library
, Streamly.Internal.Data.Pipe
, Streamly.Internal.Data.Stream.Type
, Streamly.Internal.Data.Stream.Eliminate
, Streamly.Internal.Data.Stream.Enumerate
, Streamly.Internal.Data.Stream.Generate
, Streamly.Internal.Data.Stream.Transform
, Streamly.Internal.Data.Stream.Bottom

View File

@ -190,6 +190,7 @@ extra-source-files:
core/src/Streamly/Internal/Data/Stream.hs
core/src/Streamly/Internal/Data/Stream/Bottom.hs
core/src/Streamly/Internal/Data/Stream/Eliminate.hs
core/src/Streamly/Internal/Data/Stream/Enumerate.hs
core/src/Streamly/Internal/Data/Stream/Exception.hs
core/src/Streamly/Internal/Data/Stream/Expand.hs
core/src/Streamly/Internal/Data/Stream/Generate.hs
@ -601,6 +602,7 @@ library
, Streamly.Internal.Data.Pipe
, Streamly.Internal.Data.Stream.Type
, Streamly.Internal.Data.Stream.Bottom
, Streamly.Internal.Data.Stream.Enumerate
, Streamly.Internal.Data.Stream.Generate
, Streamly.Internal.Data.Stream.Eliminate
, Streamly.Internal.Data.Stream.Expand