on master help sign in
Add Enumerable typeclass for Unfold
commit6d98f4e1356e276d909830c07aaa7d5cb2ca5e8a authorRanjeet Kumar Ranjan <ranjeetr@composewell.com> dateTue 18 May 2021, 17:50 (5 years ago) parent263da09
5 files changed+545−150
benchmark/Streamly/Benchmark/Data/Unfold.hs+5−5 src/Streamly/Internal/Data/Unfold.hs+126−144 src/Streamly/Internal/Data/Unfold/Enumeration.hs+336 src/Streamly/Internal/Data/Unfold/Type.hs+77−1 streamly.cabal+1
benchmark/Streamly/Benchmark/Data/Unfold.hs+5−5
@@ -73,7 +73,7 @@ drainTransformation unf f seed = drainGeneration (f unf) seed
7373drainTransformationDefault ::
7474 Monad m => Int -> (Unfold m Int Int -> Unfold m c d) -> c -> m ()
7575drainTransformationDefault to =
76− drainTransformation (UF.enumerateFromToIntegral to)
76+ drainTransformation (UF.enumerateFromToIntegral to)
7777
7878{-# INLINE drainProduct #-}
7979drainProduct ::
@@ -96,7 +96,7 @@ drainProductDefault to = drainProduct src src
9696
9797 where
9898
99− src = UF.enumerateFromToIntegral to
99+ src = UF.enumerateFromToIntegral to
100100
101101-------------------------------------------------------------------------------
102102-- Operations on input
@@ -253,7 +253,7 @@ enumerateFromStepIntegral size start =
253253{-# INLINE enumerateFromToIntegral #-}
254254enumerateFromToIntegral :: Monad m => Int -> Int -> m ()
255255enumerateFromToIntegral size start =
256− drainGeneration (UF.enumerateFromToIntegral (size + start)) start
256+ drainGeneration (UF.enumerateFromToIntegral (size + start)) start
257257
258258{-# INLINE enumerateFromIntegral #-}
259259enumerateFromIntegral :: Monad m => Int -> Int -> m ()
@@ -434,8 +434,8 @@ concatMapM value start =
434434 where
435435
436436 val = nthRoot 2 value
437− unfoldInGen i = return (UF.enumerateFromToIntegral (i + val))
438− unfoldOut = UF.enumerateFromToIntegral (start + val)
437+ unfoldInGen i = return (UF.enumerateFromToIntegral (i + val))
438+ unfoldOut = UF.enumerateFromToIntegral (start + val)
439439
440440{-# INLINE toNull #-}
441441toNull :: Monad m => Int -> Int -> m ()
src/Streamly/Internal/Data/Unfold.hs+126−144
@@ -141,6 +141,7 @@ module Streamly.Internal.Data.Unfold
141141 , iterateM
142142
143143 -- ** Enumerations
144+ , Enumerable (..)
144145 -- *** Enumerate Num
145146 , enumerateFromStepNum
146147 , numFrom
@@ -229,12 +230,14 @@ module Streamly.Internal.Data.Unfold
229230 )
230231where
231232
232−import Control.Exception (Exception, mask_)
233−import Control.Monad ((>=>))
233+import Control.Exception (Exception, fromException, mask_)
234+import Control.Monad ((>=>), when)
234235import Control.Monad.Catch (MonadCatch)
235236import Control.Monad.IO.Class (MonadIO(..))
236237import 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)
238241import GHC.Types (SPEC(..))
239242import Streamly.Internal.Control.Concurrent (MonadAsync)
240243import Streamly.Internal.Data.Fold.Type (Fold(..))
@@ -243,7 +246,6 @@ import Streamly.Internal.Data.IOFinalizer
243246import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
244247import Streamly.Internal.Data.SVar.Type (defState)
245248
246−import qualified Prelude
247249import qualified Control.Monad.Catch as MC
248250import qualified Data.Tuple as Tuple
249251import qualified Streamly.Internal.Data.Fold.Type as FL
@@ -252,6 +254,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
252254import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
253255
254256import Streamly.Internal.Data.Unfold.Type
257+import Streamly.Internal.Data.Unfold.Enumeration
255258import Prelude
256259 hiding (map, mapM, takeWhile, take, filter, const, zipWith
257260 , drop, dropWhile, either)
@@ -261,60 +264,6 @@ import Prelude
261264-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
262265-- >>> import qualified Streamly.Prelude as Stream
263266
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)
318267
319268-- | Convert an 'Unfold' into an unfold accepting a tuple as an argument,
320269-- 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
683632 Stop -> Stop
684633 step (_, _) = return Stop
685634
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−
713635-- | Same as 'filter' but with a monadic predicate.
714636--
715637-- /Since: 0.8.0/
@@ -810,74 +732,134 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
810732dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
811733dropWhile f = dropWhileM (return . f)
812734
813−------------------------------------------------------------------------------
814−-- Enumeration of Num
815−------------------------------------------------------------------------------
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)
735+-------------------------------------------------------------------------------
736+-- Generation from SVar
737+-------------------------------------------------------------------------------
832738
833− {-# INLINE_LATE step #-}
834− step (from, !i) = return $ (Yield $! (from + i * stride)) $! (from, i + 1)
739+data FromSVarState t m a =
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)
835744
836−-- | @numFrom = enumerateFromStepNum 1@
837−--
838−-- /Pre-release/
745+-- | /Internal/
839746--
840−{-# INLINE_NORMAL numFrom #-}
841−numFrom :: (Monad m, Num a) => Unfold m a a
842−numFrom = enumerateFromStepNum 1
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
747+{-# INLINE_NORMAL fromSVar #-}
748+fromSVar :: MonadAsync m => Unfold m (SVar t m a) a
749+fromSVar = Unfold step (return . FromSVarInit)
853750 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
864751
865−{-# INLINE enumerateFromIntegral #-}
866−enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
867−enumerateFromIntegral = enumerateFromToIntegral maxBound
752+ {-# INLINE_LATE step #-}
753+ step (FromSVarInit svar) = do
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
868821
869−------------------------------------------------------------------------------
870−-- Enumeration of Fractionals
871−------------------------------------------------------------------------------
822+-------------------------------------------------------------------------------
823+-- Process events received by a fold consumer from a stream producer
824+-------------------------------------------------------------------------------
872825
826+-- XXX Have an error for "FromSVarInit" instead of undefined. The error can
827+-- redirect the user to report the failure to the developers.
873828-- | /Internal/
874829--
875−-- > enumerateFromToFractional to = takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
876−--
877−{-# INLINE_NORMAL enumerateFromToFractional #-}
878−enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> Unfold m a a
879−enumerateFromToFractional to =
880− takeWhile (<= to + 1 / 2) $ enumerateFromStepNum 1
830+{-# INLINE_NORMAL fromProducer #-}
831+fromProducer :: MonadAsync m => Unfold m (SVar t m a) a
832+fromProducer = Unfold step (return . FromSVarRead)
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
881863
882864------------------------------------------------------------------------------
883865-- 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
3030
3131 -- * Transformations
3232 , lmap
33+ , lmapM
3334 , map
35+ , supply
36+ , supplyFirst
37+ , supplySecond
38+ , takeWhileM
39+ , takeWhile
3440
3541 -- * Nesting
3642 , ConcatState (..)
@@ -58,10 +64,12 @@ where
5864
5965-- import Control.Arrow (Arrow(..))
6066-- import Control.Category (Category(..))
67+import Control.Monad ((>=>))
68+import Data.Void (Void)
6169import Fusion.Plugin.Types (Fuse(..))
6270import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
6371
64−import Prelude hiding (const, map, concatMap, zipWith)
72+import Prelude hiding (map, concatMap, zipWith, takeWhile)
6573
6674-- $setup
6775-- >>> import qualified Streamly.Prelude as Stream
@@ -545,6 +553,74 @@ zipWith :: Monad m
545553 => (b -> c -> d) -> Unfold m a b -> Unfold m a c -> Unfold m a d
546554zipWith f = zipWithM (\a b -> return (f a b))
547555
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+
548624-------------------------------------------------------------------------------
549625-- Arrow
550626-------------------------------------------------------------------------------
streamly.cabal+1
@@ -422,6 +422,7 @@ library
422422 -- streamly-core
423423 , Streamly.Internal.Data.Unfold
424424 , Streamly.Internal.Data.Unfold.SVar
425+ , Streamly.Internal.Data.Unfold.Enumeration
425426 , Streamly.Internal.Data.Fold.Tee
426427 , Streamly.Internal.Data.Fold
427428 , Streamly.Internal.Data.Fold.SVar