on master help sign in
Add Enumerable typeclass for Unfold
6d98f4eRanjeet Kumar Ranjan
benchmark/Streamly/Benchmark/Data/Unfold.hs+5−5
@@ -73,7 +73,7 @@ drainTransformation unf f seed = drainGeneration (f unf) seed
73drainTransformationDefault ::
73drainTransformationDefault ::
74 Monad m => Int -> (Unfold m Int Int -> Unfold m c d) -> c -> m ()
74 Monad m => Int -> (Unfold m Int Int -> Unfold m c d) -> c -> m ()
75drainTransformationDefault to =
75drainTransformationDefault to =
76− drainTransformation (UF.enumerateFromToIntegral to)
76+ drainTransformation (UF.enumerateFromToIntegral to)
77
77
78{-# INLINE drainProduct #-}
78{-# INLINE drainProduct #-}
79drainProduct ::
79drainProduct ::
@@ -96,7 +96,7 @@ drainProductDefault to = drainProduct src src
96
96
97 where
97 where
98
98
99− src = UF.enumerateFromToIntegral to
99+ src = UF.enumerateFromToIntegral to
100
100
101-------------------------------------------------------------------------------
101-------------------------------------------------------------------------------
102-- Operations on input
102-- Operations on input
@@ -253,7 +253,7 @@ enumerateFromStepIntegral size start =
253{-# INLINE enumerateFromToIntegral #-}
253{-# INLINE enumerateFromToIntegral #-}
254enumerateFromToIntegral :: Monad m => Int -> Int -> m ()
254enumerateFromToIntegral :: Monad m => Int -> Int -> m ()
255enumerateFromToIntegral size start =
255enumerateFromToIntegral size start =
256− drainGeneration (UF.enumerateFromToIntegral (size + start)) start
256+ drainGeneration (UF.enumerateFromToIntegral (size + start)) start
257
257
258{-# INLINE enumerateFromIntegral #-}
258{-# INLINE enumerateFromIntegral #-}
259enumerateFromIntegral :: Monad m => Int -> Int -> m ()
259enumerateFromIntegral :: Monad m => Int -> Int -> m ()
@@ -434,8 +434,8 @@ concatMapM value start =
434 where
434 where
435
435
436 val = nthRoot 2 value
436 val = nthRoot 2 value
437− unfoldInGen i = return (UF.enumerateFromToIntegral (i + val))
437+ unfoldInGen i = return (UF.enumerateFromToIntegral (i + val))
438− unfoldOut = UF.enumerateFromToIntegral (start + val)
438+ unfoldOut = UF.enumerateFromToIntegral (start + val)
439
439
440{-# INLINE toNull #-}
440{-# INLINE toNull #-}
441toNull :: Monad m => Int -> Int -> m ()
441toNull :: Monad m => Int -> Int -> m ()
src/Streamly/Internal/Data/Unfold.hs+126−144
@@ -141,6 +141,7 @@ module Streamly.Internal.Data.Unfold
141 , iterateM
141 , iterateM
142
142
143 -- ** Enumerations
143 -- ** Enumerations
144+ , Enumerable (..)
144 -- *** Enumerate Num
145 -- *** Enumerate Num
145 , enumerateFromStepNum
146 , enumerateFromStepNum
146 , numFrom
147 , numFrom
@@ -229,12 +230,14 @@ module Streamly.Internal.Data.Unfold
229 )
230 )
230where
231where
231
232
232−import Control.Exception (Exception, mask_)
233+import Control.Exception (Exception, fromException, mask_)
233−import Control.Monad ((>=>))
234+import Control.Monad ((>=>), when)
234import Control.Monad.Catch (MonadCatch)
235import Control.Monad.Catch (MonadCatch)
235import Control.Monad.IO.Class (MonadIO(..))
236import Control.Monad.IO.Class (MonadIO(..))
236import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
237import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
237−import Data.Void (Void)
238+import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
239+import Data.Int
240+import Data.Maybe (isNothing)
238import GHC.Types (SPEC(..))
241import GHC.Types (SPEC(..))
239import Streamly.Internal.Control.Concurrent (MonadAsync)
242import Streamly.Internal.Control.Concurrent (MonadAsync)
240import Streamly.Internal.Data.Fold.Type (Fold(..))
243import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -243,7 +246,6 @@ import Streamly.Internal.Data.IOFinalizer
243import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
246import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
244import Streamly.Internal.Data.SVar.Type (defState)
247import Streamly.Internal.Data.SVar.Type (defState)
245
248
246−import qualified Prelude
247import qualified Control.Monad.Catch as MC
249import qualified Control.Monad.Catch as MC
248import qualified Data.Tuple as Tuple
250import qualified Data.Tuple as Tuple
249import qualified Streamly.Internal.Data.Fold.Type as FL
251import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -252,6 +254,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
252import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
254import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
253
255
254import Streamly.Internal.Data.Unfold.Type
256import Streamly.Internal.Data.Unfold.Type
257+import Streamly.Internal.Data.Unfold.Enumeration
255import Prelude
258import Prelude
256 hiding (map, mapM, takeWhile, take, filter, const, zipWith
259 hiding (map, mapM, takeWhile, take, filter, const, zipWith
257 , drop, dropWhile, either)
260 , drop, dropWhile, either)
@@ -261,60 +264,6 @@ import Prelude
261-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
264-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
262-- >>> import qualified Streamly.Prelude as Stream
265-- >>> import qualified Streamly.Prelude as Stream
263
266
264−-------------------------------------------------------------------------------
265−-- Input operations
266−-------------------------------------------------------------------------------
267−
268−-- | Map an action on the input argument of the 'Unfold'.
269−--
270−-- @
271−-- lmapM f = Unfold.many (Unfold.functionM f)
272−-- @
273−--
274−-- /Since: 0.8.0/
275−{-# INLINE_NORMAL lmapM #-}
276−lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
277−lmapM f (Unfold ustep uinject) = Unfold ustep (f >=> uinject)
278−
279−-- | Supply the seed to an unfold closing the input end of the unfold.
280−--
281−-- @
282−-- supply a = Unfold.lmap (Prelude.const a)
283−-- @
284−--
285−-- /Pre-release/
286−--
287−{-# INLINE_NORMAL supply #-}
288−supply :: a -> Unfold m a b -> Unfold m Void b
289−supply a = lmap (Prelude.const a)
290−
291−-- | Supply the first component of the tuple to an unfold that accepts a tuple
292−-- as a seed resulting in a fold that accepts the second component of the tuple
293−-- as a seed.
294−--
295−-- @
296−-- supplyFirst a = Unfold.lmap (a, )
297−-- @
298−--
299−-- /Pre-release/
300−--
301−{-# INLINE_NORMAL supplyFirst #-}
302−supplyFirst :: a -> Unfold m (a, b) c -> Unfold m b c
303−supplyFirst a = lmap (a, )
304−
305−-- | Supply the second component of the tuple to an unfold that accepts a tuple
306−-- as a seed resulting in a fold that accepts the first component of the tuple
307−-- as a seed.
308−--
309−-- @
310−-- supplySecond b = Unfold.lmap (, b)
311−-- @
312−--
313−-- /Pre-release/
314−--
315−{-# INLINE_NORMAL supplySecond #-}
316−supplySecond :: b -> Unfold m (a, b) c -> Unfold m a c
317−supplySecond b = lmap (, b)
318
267
319-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
268-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
320-- using the argument of the original fold as the second element of tuple and
269-- using the argument of the original fold as the second element of tuple and
@@ -683,33 +632,6 @@ take n (Unfold step1 inject1) = Unfold step inject
683 Stop -> Stop
632 Stop -> Stop
684 step (_, _) = return Stop
633 step (_, _) = return Stop
685
634
686−-- | Same as 'takeWhile' but with a monadic predicate.
687−--
688−-- /Since: 0.8.0/
689−--
690−{-# INLINE_NORMAL takeWhileM #-}
691−takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
692−takeWhileM f (Unfold step1 inject1) = Unfold step inject1
693− where
694− {-# INLINE_LATE step #-}
695− step st = do
696− r <- step1 st
697− case r of
698− Yield x s -> do
699− b <- f x
700− return $ if b then Yield x s else Stop
701− Skip s -> return $ Skip s
702− Stop -> return Stop
703−
704−-- | End the stream generated by the 'Unfold' as soon as the predicate fails
705−-- on an element.
706−--
707−-- /Since: 0.8.0/
708−--
709−{-# INLINE takeWhile #-}
710−takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
711−takeWhile f = takeWhileM (return . f)
712−
713-- | Same as 'filter' but with a monadic predicate.
635-- | Same as 'filter' but with a monadic predicate.
714--
636--
715-- /Since: 0.8.0/
637-- /Since: 0.8.0/
@@ -810,74 +732,134 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
810dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
732dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
811dropWhile f = dropWhileM (return . f)
733dropWhile f = dropWhileM (return . f)
812
734
813−------------------------------------------------------------------------------
735+-------------------------------------------------------------------------------
814−-- Enumeration of Num
736+-- Generation from SVar
815−------------------------------------------------------------------------------
737+-------------------------------------------------------------------------------
816−
817−-- | Generate an infinite stream starting from a starting value with increments
818−-- of the given stride. The implementation is numerically stable for floating
819−-- point values.
820−--
821−-- Note 'enumerateFromStepIntegral' is faster for integrals.
822−--
823−-- /Pre-release/
824−--
825−{-# INLINE enumerateFromStepNum #-}
826−enumerateFromStepNum :: (Monad m, Num a) => a -> Unfold m a a
827−enumerateFromStepNum stride = Unfold step inject
828−
829− where
830−
831− inject !from = return (from, 0)
832
738
833− {-# INLINE_LATE step #-}
739+data FromSVarState t m a =
834− step (from, !i) = return $ (Yield $! (from + i * stride)) $! (from, i + 1)
740+ FromSVarInit (SVar t m a)
741+ | FromSVarRead (SVar t m a)
742+ | FromSVarLoop (SVar t m a) [ChildEvent a]
743+ | FromSVarDone (SVar t m a)
835
744
836−-- | @numFrom = enumerateFromStepNum 1@
745+-- | /Internal/
837−--
838−-- /Pre-release/
839--
746--
840−{-# INLINE_NORMAL numFrom #-}
747+{-# INLINE_NORMAL fromSVar #-}
841−numFrom :: (Monad m, Num a) => Unfold m a a
748+fromSVar :: MonadAsync m => Unfold m (SVar t m a) a
842−numFrom = enumerateFromStepNum 1
749+fromSVar = Unfold step (return . FromSVarInit)
843−
844−------------------------------------------------------------------------------
845−-- Enumeration of Integrals
846−------------------------------------------------------------------------------
847−
848−-- | Can be used to enumerate unbounded integrals. This does not check for
849−-- overflow or underflow for bounded integrals.
850−{-# INLINE_NORMAL enumerateFromStepIntegral #-}
851−enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
852−enumerateFromStepIntegral = Unfold step inject
853 where
750 where
854− inject (from, stride) = from `seq` stride `seq` return (from, stride)
855− {-# INLINE_LATE step #-}
856− step (x, stride) = return $ Yield x $! (x + stride, stride)
857−
858−-- We are assuming that "to" is constrained by the type to be within
859−-- max/min bounds.
860−{-# INLINE enumerateFromToIntegral #-}
861−enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
862−enumerateFromToIntegral to =
863− takeWhile (<= to) $ supplySecond 1 enumerateFromStepIntegral
864
751
865−{-# INLINE enumerateFromIntegral #-}
752+ {-# INLINE_LATE step #-}
866−enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
753+ step (FromSVarInit svar) = do
867−enumerateFromIntegral = enumerateFromToIntegral maxBound
754+ ref <- liftIO $ newIORef ()
755+ _ <- liftIO $ mkWeakIORef ref hook
756+ -- when this copy of svar gets garbage collected "ref" will get
757+ -- garbage collected and our GC hook will be called.
758+ let sv = svar{svarRef = Just ref}
759+ return $ Skip (FromSVarRead sv)
760+
761+ where
762+
763+ {-# NOINLINE hook #-}
764+ hook = do
765+ when (svarInspectMode svar) $ do
766+ r <- liftIO $ readIORef (svarStopTime (svarStats svar))
767+ when (isNothing r) $
768+ printSVar svar "SVar Garbage Collected"
769+ cleanupSVar svar
770+ -- If there are any SVars referenced by this SVar a GC will prompt
771+ -- them to be cleaned up quickly.
772+ when (svarInspectMode svar) performMajorGC
773+
774+ step (FromSVarRead sv) = do
775+ list <- readOutputQ sv
776+ -- Reversing the output is important to guarantee that we process the
777+ -- outputs in the same order as they were generated by the constituent
778+ -- streams.
779+ return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
780+
781+ step (FromSVarLoop sv []) = do
782+ done <- postProcess sv
783+ return $ Skip $ if done
784+ then FromSVarDone sv
785+ else FromSVarRead sv
786+
787+ step (FromSVarLoop sv (ev : es)) = do
788+ case ev of
789+ ChildYield a -> return $ Yield a (FromSVarLoop sv es)
790+ ChildStop tid e -> do
791+ accountThread sv tid
792+ case e of
793+ Nothing -> do
794+ stop <- shouldStop tid
795+ if stop
796+ then do
797+ liftIO (cleanupSVar sv)
798+ return $ Skip (FromSVarDone sv)
799+ else return $ Skip (FromSVarLoop sv es)
800+ Just ex ->
801+ case fromException ex of
802+ Just ThreadAbort ->
803+ return $ Skip (FromSVarLoop sv es)
804+ Nothing -> liftIO (cleanupSVar sv) >> MC.throwM ex
805+ where
806+
807+ shouldStop tid =
808+ case svarStopStyle sv of
809+ StopNone -> return False
810+ StopAny -> return True
811+ StopBy -> do
812+ sid <- liftIO $ readIORef (svarStopBy sv)
813+ return $ tid == sid
814+
815+ step (FromSVarDone sv) = do
816+ when (svarInspectMode sv) $ do
817+ t <- liftIO $ getTime Monotonic
818+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
819+ liftIO $ printSVar sv "SVar Done"
820+ return Stop
868
821
869−------------------------------------------------------------------------------
822+-------------------------------------------------------------------------------
870−-- Enumeration of Fractionals
823+-- Process events received by a fold consumer from a stream producer
871−------------------------------------------------------------------------------
824+-------------------------------------------------------------------------------
872
825
826+-- XXX Have an error for "FromSVarInit" instead of undefined. The error can
827+-- redirect the user to report the failure to the developers.
873-- | /Internal/
828-- | /Internal/
874--
829--
875−-- > enumerateFromToFractional to = takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
876−--
877−{-# INLINE_NORMAL enumerateFromToFractional #-}
830+{-# INLINE_NORMAL fromProducer #-}
878−enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> Unfold m a a
831+fromProducer :: MonadAsync m => Unfold m (SVar t m a) a
879−enumerateFromToFractional to =
832+fromProducer = Unfold step (return . FromSVarRead)
880− takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
833+ where
834+
835+ {-# INLINE_LATE step #-}
836+ step (FromSVarRead sv) = do
837+ list <- readOutputQ sv
838+ -- Reversing the output is important to guarantee that we process the
839+ -- outputs in the same order as they were generated by the constituent
840+ -- streams.
841+ return $ Skip $ FromSVarLoop sv (Prelude.reverse list)
842+
843+ step (FromSVarLoop sv []) = return $ Skip $ FromSVarRead sv
844+ step (FromSVarLoop sv (ev : es)) = do
845+ case ev of
846+ ChildYield a -> return $ Yield a (FromSVarLoop sv es)
847+ ChildStop tid e -> do
848+ accountThread sv tid
849+ case e of
850+ Nothing -> do
851+ sendStopToProducer sv
852+ return $ Skip (FromSVarDone sv)
853+ Just _ -> error "Bug: fromProducer: received exception"
854+
855+ step (FromSVarDone sv) = do
856+ when (svarInspectMode sv) $ do
857+ t <- liftIO $ getTime Monotonic
858+ liftIO $ writeIORef (svarStopTime (svarStats sv)) (Just t)
859+ liftIO $ printSVar sv "SVar Done"
860+ return Stop
861+
862+ step (FromSVarInit _) = undefined
881
863
882------------------------------------------------------------------------------
864------------------------------------------------------------------------------
883-- Exceptions
865-- Exceptions
src/Streamly/Internal/Data/Unfold/Enumeration.hsnew file+336
@@ -0,0 +1,336 @@
1+#include "inline.hs"
2+-- |
3+-- Module : Streamly.Internal.Data.Unfold.Enumeration
4+-- Copyright : (c) 2018 Composewell Technologies
5+--
6+-- License : BSD3
7+-- Maintainer : streamly@composewell.com
8+-- Stability : experimental
9+-- Portability : GHC
10+--
11+-- The functions defined in this module should be rarely needed for direct use,
12+-- try to use the operations from the 'Enumerable' type class
13+-- instances instead.
14+--
15+-- This module provides an 'Enumerable' type class to enumerate 'Enum' types
16+-- into a stream. The operations in this type class correspond to similar
17+-- perations in the 'Enum' type class, the only difference is that they produce
18+-- a stream instead of a list. These operations cannot be defined generically
19+-- based on the 'Enum' type class. We provide instances for commonly used
20+-- types. If instances for other types are needed convenience functions defined
21+-- in this module can be used to define them. Alternatively, these functions
22+-- can be used directly.
23+--
24+module Streamly.Internal.Data.Unfold.Enumeration
25+(
26+ -- * Enumerations
27+ Enumerable (..)
28+ -- ** Enumerate Num
29+ , enumerateFromStepNum
30+ , numFrom
31+
32+ -- ** Enumerating 'Bounded' 'Integral' Types
33+ , enumerateFromIntegral
34+ , enumerateFromThenIntegral
35+
36+ -- ** Enumerating 'Integral' Types
37+ , enumerateFromToIntegral
38+ , enumerateFromThenToIntegral
39+
40+ -- ** Enumerating unbounded 'Integral' Types
41+ , enumerateFromStepIntegral
42+
43+ -- ** Enumerating 'Fractional' Types
44+ , enumerateFromFractional
45+ , enumerateFromToFractional
46+ , enumerateFromThenFractional
47+ , enumerateFromThenToFractional
48+ )
49+where
50+
51+import Data.Fixed
52+import Data.Int
53+import Data.Ratio
54+import Data.Word
55+import Numeric.Natural
56+import Data.Functor.Identity (Identity(..))
57+import Streamly.Internal.Data.Unfold.Type
58+import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
59+import Prelude
60+ hiding (map, mapM, takeWhile, take, filter, const, zipWith
61+ , drop, dropWhile)
62+
63+------------------------------------------------------------------------------
64+-- Enumeration of Num
65+------------------------------------------------------------------------------
66+-- | Generate an infinite stream starting from a starting value with increments
67+-- of the given stride. The implementation is numerically stable for floating
68+-- point values.
69+--
70+-- Note 'enumerateFromStepIntegral' is faster for integrals.
71+--
72+-- /Pre-release/
73+--
74+{-# INLINE enumerateFromStepNum #-}
75+enumerateFromStepNum :: (Monad m, Num a) => a -> Unfold m a a
76+enumerateFromStepNum stride = Unfold step inject
77+
78+ where
79+
80+ inject !from = return (from, 0)
81+
82+ {-# INLINE_LATE step #-}
83+ step (from, !i) = return $ (Yield $! (from + i * stride)) $! (from, i + 1)
84+
85+-- | @numFrom = enumerateFromStepNum 1@
86+--
87+-- /Pre-release/
88+--
89+{-# INLINE_NORMAL numFrom #-}
90+numFrom :: (Monad m, Num a) => Unfold m a a
91+numFrom = enumerateFromStepNum 1
92+
93+{-# INLINE_NORMAL numFromThen #-}
94+numFromThen :: (Monad m, Num a) => a -> Unfold m a a
95+numFromThen = enumerateFromStepNum
96+
97+------------------------------------------------------------------------------
98+-- Enumeration of Integrals
99+------------------------------------------------------------------------------
100+
101+-- | Can be used to enumerate unbounded integrals. This does not check for
102+-- overflow or underflow for bounded integrals.
103+{-# INLINE_NORMAL enumerateFromStepIntegral #-}
104+enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
105+enumerateFromStepIntegral = Unfold step inject
106+ where
107+ inject (from, stride) = from `seq` stride `seq` return (from, stride)
108+ {-# INLINE_LATE step #-}
109+ step (x, stride) = return $ Yield x $! (x + stride, stride)
110+
111+-- We are assuming that "to" is constrained by the type to be within
112+-- max/min bounds.
113+{-# INLINE enumerateFromToIntegral #-}
114+enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
115+enumerateFromToIntegral to =
116+ takeWhile (<= to) $ supplySecond 1 enumerateFromStepIntegral
117+
118+{-# INLINE enumerateFromThenToIntegral #-}
119+enumerateFromThenToIntegral :: (Monad m, Integral a) => a -> a -> Unfold m a a
120+enumerateFromThenToIntegral stride to =
121+ takeWhile (<= to) $ supplySecond stride enumerateFromStepIntegral
122+
123+{-# INLINE enumerateFromIntegral #-}
124+enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
125+enumerateFromIntegral = enumerateFromToIntegral maxBound
126+
127+{-# INLINE enumerateFromThenIntegral #-}
128+enumerateFromThenIntegral :: (Monad m, Integral a, Bounded a) => a -> Unfold m a a
129+enumerateFromThenIntegral stride = enumerateFromThenToIntegral stride maxBound
130+
131+------------------------------------------------------------------------------
132+-- Enumeration of Fractionals
133+------------------------------------------------------------------------------
134+
135+-- | /Internal/
136+--
137+-- > enumerateFromToFractional to = takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
138+--
139+{-# INLINE_NORMAL enumerateFromToFractional #-}
140+enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> Unfold m a a
141+enumerateFromToFractional to =
142+ takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
143+
144+{-# INLINE_NORMAL enumerateFromFractional #-}
145+enumerateFromFractional :: (Monad m, Fractional a) => Unfold m a a
146+enumerateFromFractional = enumerateFromStepNum 1
147+
148+{-# INLINE_NORMAL enumerateFromThenToFractional #-}
149+enumerateFromThenToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> Unfold m a a
150+enumerateFromThenToFractional stride to =
151+ takeWhile (<= to + 1 / 2) $ enumerateFromStepNum stride
152+
153+{-# INLINE_NORMAL enumerateFromThenFractional #-}
154+enumerateFromThenFractional :: (Monad m, Fractional a) => a -> Unfold m a a
155+enumerateFromThenFractional = enumerateFromStepNum
156+
157+-------------------------------------------------------------------------------
158+-- Enumerable type class
159+-------------------------------------------------------------------------------
160+-- | Types that can be enumerated as a stream. The operations in this type
161+-- class are equivalent to those in the 'Enum' type class, except that these
162+-- generate a stream instead of a list. Use the functions in
163+-- "Streamly.Internal.Data.Unfold.Enumeration" module to define new instances.
164+--
165+-- /Pre-release/
166+class Enum a => Enumerable a where
167+
168+-- @enumerateFrom from@ generates a stream starting with the element
169+-- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or
170+-- generating an infinite stream when the type is not 'Bounded'.
171+--
172+-- @
173+-- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom (0 :: Int)
174+-- [0,1,2,3]
175+--
176+-- @
177+--
178+-- For 'Fractional' types, enumeration is numerically stable. However, no
179+-- overflow or underflow checks are performed.
180+--
181+-- @
182+-- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom 1.1
183+-- [1.1,2.1,3.1,4.1]
184+--
185+-- @
186+--
187+-- /Pre-release/
188+--
189+ enumerateFrom :: (Integral a, Monad m) => Unfold m a a
190+
191+-- Generate a finite stream starting with the element @from@, enumerating
192+-- the type up to the value @to@. If @to@ is smaller than @from@ then an
193+-- empty stream is returned.
194+--
195+-- @
196+-- >>> Stream.toList $ Stream.unfold (Unfold.enumerateFromTo 4) 0
197+-- [0,1,2,3,4]
198+--
199+-- @
200+--
201+-- For 'Fractional' types, the last element is equal to the specified @to@
202+-- value after rounding to the nearest integral value.
203+--
204+-- @
205+-- >>> Stream.toList $ Stream.unfold (Unfold.enumerateFromTo 4) 1.1
206+-- [1.1,2.1,3.1,4.1]
207+--
208+-- >>> Stream.toList $ Stream.unfold (Unfold.enumerateFromTo 4.6) 1.1
209+-- [1.1,2.1,3.1,4.1,5.1]
210+--
211+-- @
212+--
213+-- /Pre-release/
214+ enumerateFromTo :: (Integral a, Monad m) => a -> Unfold m a a
215+
216+-- @enumerateFromThen from then@ generates a stream whose first element
217+-- is @from@ and the successive elements are
218+-- in increments of @then@. Enumeration can occur downwards or
219+-- upwards depending on whether @then@ comes before or after @from@. For
220+-- 'Bounded' types the stream ends when 'maxBound' is reached, for
221+-- unbounded types it keeps enumerating infinitely.
222+--
223+-- @
224+-- >>> Stream.toList $ Stream.take 4 $ Stream.unfold (Unfold.enumerateFromThen 2) 0
225+-- [0,2,4,6]
226+--
227+-- >>> Stream.toList $ Stream.take 4 $ Stream.unfold (Unfold.enumerateFromThen (-2)) 0
228+-- [0,-2,-4,-6]
229+--
230+-- @
231+--
232+-- /Pre-release/
233+ enumerateFromThen :: (Integral a, Monad m) => a -> Unfold m a a
234+
235+-- @enumerateFromThenTo from then to@ generates a finite stream whose
236+-- first element is @from@ and the successive elements are in
237+-- increments of @then@ up to @to@. Enumeration can
238+-- occur downwards or upwards depending on whether @then@ comes before or
239+-- after @from@.
240+--
241+-- @
242+-- >>> Stream.toList $ Stream.unfold (Unfold.enumerateFromThenTo 2 6) 0
243+-- [0,2,4,6]
244+--
245+-- >>> Stream.toList $ Stream.unfold (Unfold.enumerateFromThenTo (-2) (-6)) 0
246+-- [0,-2,-4,-6]
247+--
248+-- @
249+--
250+-- /Pre-release/
251+ enumerateFromThenTo :: (Integral a, Monad m) => a-> a -> Unfold m a a
252+
253+-------------------------------------------------------------------------------
254+-- Enumerable Instances
255+-------------------------------------------------------------------------------
256+--
257+-- For Enum types smaller than or equal to Int size.
258+#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE) \
259+instance Enumerable SMALL_TYPE where { \
260+ {-# INLINE enumerateFrom #-}; \
261+ enumerateFrom = enumerateFromIntegral; \
262+ {-# INLINE enumerateFromTo #-}; \
263+ enumerateFromTo = enumerateFromToIntegral; \
264+ {-# INLINE enumerateFromThen #-}; \
265+ enumerateFromThen = enumerateFromThenIntegral; \
266+ {-# INLINE enumerateFromThenTo #-}; \
267+ enumerateFromThenTo = enumerateFromThenToIntegral }
268+
269+ENUMERABLE_BOUNDED_SMALL(())
270+ENUMERABLE_BOUNDED_SMALL(Bool)
271+ENUMERABLE_BOUNDED_SMALL(Ordering)
272+ENUMERABLE_BOUNDED_SMALL(Char)
273+
274+-- For bounded Integral Enum types, may be larger than Int.
275+#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE) \
276+instance Enumerable INTEGRAL_TYPE where { \
277+ {-# INLINE enumerateFrom #-}; \
278+ enumerateFrom = enumerateFromIntegral; \
279+ {-# INLINE enumerateFromTo #-}; \
280+ enumerateFromTo = enumerateFromToIntegral; \
281+ {-# INLINE enumerateFromThen #-}; \
282+ enumerateFromThen = enumerateFromThenIntegral; \
283+ {-# INLINE enumerateFromThenTo #-}; \
284+ enumerateFromThenTo = enumerateFromThenToIntegral }
285+
286+ENUMERABLE_BOUNDED_INTEGRAL(Int)
287+ENUMERABLE_BOUNDED_INTEGRAL(Int8)
288+ENUMERABLE_BOUNDED_INTEGRAL(Int16)
289+ENUMERABLE_BOUNDED_INTEGRAL(Int32)
290+ENUMERABLE_BOUNDED_INTEGRAL(Int64)
291+ENUMERABLE_BOUNDED_INTEGRAL(Word)
292+ENUMERABLE_BOUNDED_INTEGRAL(Word8)
293+ENUMERABLE_BOUNDED_INTEGRAL(Word16)
294+ENUMERABLE_BOUNDED_INTEGRAL(Word32)
295+ENUMERABLE_BOUNDED_INTEGRAL(Word64)
296+
297+-- For unbounded Integral Enum types.
298+#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE) \
299+instance Enumerable INTEGRAL_TYPE where { \
300+ {-# INLINE enumerateFrom #-}; \
301+ enumerateFrom = numFrom; \
302+ {-# INLINE enumerateFromTo #-}; \
303+ enumerateFromTo = enumerateFromToIntegral; \
304+ {-# INLINE enumerateFromThen #-}; \
305+ enumerateFromThen = numFromThen; \
306+ {-# INLINE enumerateFromThenTo #-}; \
307+ enumerateFromThenTo = enumerateFromThenToIntegral }
308+
309+ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)
310+ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)
311+
312+#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT) \
313+instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where { \
314+ {-# INLINE enumerateFrom #-}; \
315+ enumerateFrom = enumerateFromFractional; \
316+ {-# INLINE enumerateFromThen #-}; \
317+ enumerateFromThen = enumerateFromThenFractional; \
318+ {-# INLINE enumerateFromTo #-}; \
319+ enumerateFromTo = enumerateFromToFractional; \
320+ {-# INLINE enumerateFromThenTo #-}; \
321+ enumerateFromThenTo = enumerateFromThenToFractional }
322+
323+ENUMERABLE_FRACTIONAL(Float,)
324+ENUMERABLE_FRACTIONAL(Double,)
325+ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)
326+ENUMERABLE_FRACTIONAL((Ratio a),Integral a)
327+
328+instance Enumerable a => Enumerable (Identity a) where
329+ {-# INLINE enumerateFrom #-}
330+ enumerateFrom = enumerateFrom
331+ {-# INLINE enumerateFromThen #-}
332+ enumerateFromThen = enumerateFromThen
333+ {-# INLINE enumerateFromTo #-}
334+ enumerateFromTo = enumerateFromTo
335+ {-# INLINE enumerateFromThenTo #-}
336+ enumerateFromThenTo = enumerateFromThenTo
src/Streamly/Internal/Data/Unfold/Type.hs+77−1
@@ -30,7 +30,13 @@ module Streamly.Internal.Data.Unfold.Type
30
30
31 -- * Transformations
31 -- * Transformations
32 , lmap
32 , lmap
33+ , lmapM
33 , map
34 , map
35+ , supply
36+ , supplyFirst
37+ , supplySecond
38+ , takeWhileM
39+ , takeWhile
34
40
35 -- * Nesting
41 -- * Nesting
36 , ConcatState (..)
42 , ConcatState (..)
@@ -58,10 +64,12 @@ where
58
64
59-- import Control.Arrow (Arrow(..))
65-- import Control.Arrow (Arrow(..))
60-- import Control.Category (Category(..))
66-- import Control.Category (Category(..))
67+import Control.Monad ((>=>))
68+import Data.Void (Void)
61import Fusion.Plugin.Types (Fuse(..))
69import Fusion.Plugin.Types (Fuse(..))
62import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
70import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
63
71
64−import Prelude hiding (const, map, concatMap, zipWith)
72+import Prelude hiding (map, concatMap, zipWith, takeWhile)
65
73
66-- $setup
74-- $setup
67-- >>> import qualified Streamly.Prelude as Stream
75-- >>> import qualified Streamly.Prelude as Stream
@@ -545,6 +553,74 @@ zipWith :: Monad m
545 => (b -> c -> d) -> Unfold m a b -> Unfold m a c -> Unfold m a d
553 => (b -> c -> d) -> Unfold m a b -> Unfold m a c -> Unfold m a d
546zipWith f = zipWithM (\a b -> return (f a b))
554zipWith f = zipWithM (\a b -> return (f a b))
547
555
556+-------------------------------------------------------------------------------
557+-- Input operations
558+-------------------------------------------------------------------------------
559+
560+-- | Map an action on the input argument of the 'Unfold'.
561+--
562+-- @
563+-- lmapM f = Unfold.many (Unfold.functionM f)
564+-- @
565+--
566+-- /Since: 0.8.0/
567+{-# INLINE_NORMAL lmapM #-}
568+lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b
569+lmapM f (Unfold ustep uinject) = Unfold ustep (f >=> uinject)
570+
571+-- | Supply the seed to an unfold closing the input end of the unfold.
572+--
573+-- @
574+-- supply a = Unfold.lmap (Prelude.const a)
575+-- @
576+--
577+-- /Pre-release/
578+--
579+{-# INLINE_NORMAL supply #-}
580+supply :: a -> Unfold m a b -> Unfold m Void b
581+supply a = lmap (Prelude.const a)
582+
583+-- | Supply the first component of the tuple to an unfold that accepts a tuple
584+-- as a seed resulting in a fold that accepts the second component of the tuple
585+-- as a seed.
586+--
587+-- @
588+-- supplyFirst a = Unfold.lmap (a, )
589+-- @
590+--
591+-- /Pre-release/
592+--
593+{-# INLINE_NORMAL supplyFirst #-}
594+supplyFirst :: a -> Unfold m (a, b) c -> Unfold m b c
595+supplyFirst a = lmap (a, )
596+
597+{-# INLINE_NORMAL supplySecond #-}
598+supplySecond :: b -> Unfold m (a, b) c -> Unfold m a c
599+supplySecond b = lmap (, b)
600+
601+{-# INLINE_NORMAL takeWhileM #-}
602+takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
603+takeWhileM f (Unfold step1 inject1) = Unfold step inject1
604+ where
605+ {-# INLINE_LATE step #-}
606+ step st = do
607+ r <- step1 st
608+ case r of
609+ Yield x s -> do
610+ b <- f x
611+ return $ if b then Yield x s else Stop
612+ Skip s -> return $ Skip s
613+ Stop -> return Stop
614+
615+-- | End the stream generated by the 'Unfold' as soon as the predicate fails
616+-- on an element.
617+--
618+-- /Since: 0.8.0/
619+--
620+{-# INLINE takeWhile #-}
621+takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
622+takeWhile f = takeWhileM (return . f)
623+
548-------------------------------------------------------------------------------
624-------------------------------------------------------------------------------
549-- Arrow
625-- Arrow
550-------------------------------------------------------------------------------
626-------------------------------------------------------------------------------
streamly.cabal+1
@@ -422,6 +422,7 @@ library
422 -- streamly-core
422 -- streamly-core
423 , Streamly.Internal.Data.Unfold
423 , Streamly.Internal.Data.Unfold
424 , Streamly.Internal.Data.Unfold.SVar
424 , Streamly.Internal.Data.Unfold.SVar
425+ , Streamly.Internal.Data.Unfold.Enumeration
425 , Streamly.Internal.Data.Fold.Tee
426 , Streamly.Internal.Data.Fold.Tee
426 , Streamly.Internal.Data.Fold
427 , Streamly.Internal.Data.Fold
427 , Streamly.Internal.Data.Fold.SVar
428 , Streamly.Internal.Data.Fold.SVar