@@ -39,6 +39,14 @@
39-- these are stream consumers that reduce the stream to a single value, these
39-- these are stream consumers that reduce the stream to a single value, these
40-- consumers can be composed such that a stream can be split over multiple
40-- consumers can be composed such that a stream can be split over multiple
41-- consumers.
41-- consumers.
45+-- Composing a tree or graph of computations with unfolds can be much more
46+-- efficient compared to composing with the Monad instance. The reason is that
47+-- unfolds allow the compiler to statically know the state and optimize it
48+-- using stream fusion whereas it is not possible with the monad bind because
49+-- the state is determined dynamically.
43-- Open control flow style streams can also have two representations. StreamK
51-- Open control flow style streams can also have two representations. StreamK
44-- is a producer style representation. We can also have a consumer style
52-- is a producer style representation. We can also have a consumer style
@@ -55,22 +63,55 @@ module Streamly.Internal.Data.Unfold
67+ -- * Operations on Input
75+ -- * Operations on Output
60− -- ** Unfolds
79+ -- * Unfolds
61 , singleton
80 , singleton
62 , replicateM
81 , replicateM
63 , fromList
82 , fromList
83+ , enumerateFromStepIntegral
84+ , enumerateFromToIntegral
85+ , enumerateFromIntegral
104+import Data.Void (Void)
67import GHC.Types (SPEC(..))
105import GHC.Types (SPEC(..))
106+import Prelude hiding (concat, map, takeWhile, take, filter)
68import Streamly.Streams.StreamD.Type (Stream(..), Step(..))
108import Streamly.Streams.StreamD.Type (Stream(..), Step(..))
69#if __GLASGOW_HASKELL__ < 800
109#if __GLASGOW_HASKELL__ < 800
70import Streamly.Streams.StreamD.Type (pattern Stream)
110import Streamly.Streams.StreamD.Type (pattern Stream)
72import Streamly.Internal.Data.Unfold.Types (Unfold(..))
112import Streamly.Internal.Data.Unfold.Types (Unfold(..))
73import Streamly.Internal.Data.Fold.Types (Fold(..))
113import Streamly.Internal.Data.Fold.Types (Fold(..))
114+import Streamly.Internal.Data.SVar (defState)
75-------------------------------------------------------------------------------
116-------------------------------------------------------------------------------
76-- Running unfolds
117-- Running unfolds
@@ -94,6 +135,48 @@ unfold seed (Unfold ustep inject) = Stream step Nothing
94 Skip s -> Skip (Just s)
135 Skip s -> Skip (Just s)
95 Stop -> Stop
136 Stop -> Stop
138+-------------------------------------------------------------------------------
140+-------------------------------------------------------------------------------
142+-- | Supply the seed to an unfold closing the input end of the unfold.
146+{-# INLINE_NORMAL close #-}
147+close :: Unfold m a b -> a -> Unfold m Void b
148+close (Unfold ustep uinject) a = Unfold ustep (const $ uinject a)
150+-- XXX rename to closeFst/closeSnd
152+-- | Supply the first component of the tuple to an unfold that accepts a tuple
153+-- as a seed resulting in a fold that accepts the second component of the tuple
158+{-# INLINE_NORMAL first #-}
159+first :: Unfold m (a, b) c -> a -> Unfold m b c
160+first (Unfold ustep uinject) a = Unfold ustep (\b -> uinject (a, b))
162+-- | Supply the second component of the tuple to an unfold that accepts a tuple
163+-- as a seed resulting in a fold that accepts the first component of the tuple
168+{-# INLINE_NORMAL second #-}
169+second :: Unfold m (a, b) c -> b -> Unfold m a c
170+second (Unfold ustep uinject) b = Unfold ustep (\a -> uinject (a, b))
172+{-# INLINE_NORMAL lmap #-}
173+lmap :: (a -> c) -> Unfold m c b -> Unfold m a b
174+lmap f (Unfold ustep uinject) = Unfold ustep (\x -> uinject $ f x)
176+-------------------------------------------------------------------------------
178+-------------------------------------------------------------------------------
97-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
180-- | Compose an 'Unfold' and a 'Fold'. Given an @Unfold m a b@ and a
98-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
181-- @Fold m b c@, returns a monadic action @a -> m c@ representing the
99-- application of the fold on the unfolded stream.
182-- application of the fold on the unfolded stream.
@@ -116,6 +199,18 @@ fold (Unfold ustep inject) (Fold fstep initial extract) a =
116 Skip s -> go SPEC acc s
199 Skip s -> go SPEC acc s
117 Stop -> extract acc
200 Stop -> extract acc
202+{-# INLINE_NORMAL map #-}
203+map :: Monad m => (b -> c) -> Unfold m a b -> Unfold m a c
204+map f (Unfold ustep uinject) = Unfold step uinject
206+ {-# INLINE_LATE step #-}
210+ Yield x s -> Yield (f x) s
119-------------------------------------------------------------------------------
214-------------------------------------------------------------------------------
120-- Unfolds
215-- Unfolds
121-------------------------------------------------------------------------------
216-------------------------------------------------------------------------------
@@ -156,3 +251,176 @@ fromList = Unfold step inject
156 {-# INLINE_LATE step #-}
251 {-# INLINE_LATE step #-}
157 step (x:xs) = return $ Yield x xs
252 step (x:xs) = return $ Yield x xs
158 step [] = return Stop
253 step [] = return Stop
255+-------------------------------------------------------------------------------
257+-------------------------------------------------------------------------------
259+{-# INLINE_NORMAL take #-}
260+take :: Monad m => Int -> Unfold m a b -> Unfold m a b
261+take n (Unfold step1 inject1) = Unfold step inject
266+ {-# INLINE_LATE step #-}
267+ step (st, i) | i < n = do
270+ Yield x s -> Yield x (s, i + 1)
271+ Skip s -> Skip (s, i)
273+ step (_, _) = return Stop
275+{-# INLINE_NORMAL takeWhileM #-}
276+takeWhileM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
277+takeWhileM f (Unfold step1 inject1) = Unfold step inject1
279+ {-# INLINE_LATE step #-}
285+ return $ if b then Yield x s else Stop
286+ Skip s -> return $ Skip s
289+{-# INLINE takeWhile #-}
290+takeWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
291+takeWhile f = takeWhileM (return . f)
293+{-# INLINE_NORMAL filterM #-}
294+filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b
295+filterM f (Unfold step1 inject1) = Unfold step inject1
297+ {-# INLINE_LATE step #-}
303+ return $ if b then Yield x s else Skip s
304+ Skip s -> return $ Skip s
307+{-# INLINE filter #-}
308+filter :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
309+filter f = filterM (return . f)
311+-------------------------------------------------------------------------------
313+-------------------------------------------------------------------------------
315+-- | Can be used to enumerate unbounded integrals. This does not check for
316+-- overflow or underflow for bounded integrals.
317+{-# INLINE_NORMAL enumerateFromStepIntegral #-}
318+enumerateFromStepIntegral :: (Integral a, Monad m) => Unfold m (a, a) a
319+enumerateFromStepIntegral = Unfold step inject
321+ inject (from, stride) = from `seq` stride `seq` return (from, stride)
322+ {-# INLINE_LATE step #-}
323+ step !(x, stride) = return $ Yield x $! (x + stride, stride)
325+-- We are assuming that "to" is constrained by the type to be within
327+{-# INLINE enumerateFromToIntegral #-}
328+enumerateFromToIntegral :: (Monad m, Integral a) => a -> Unfold m a a
329+enumerateFromToIntegral to =
330+ takeWhile (<= to) $ second enumerateFromStepIntegral 1
332+{-# INLINE enumerateFromIntegral #-}
333+enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => Unfold m a a
334+enumerateFromIntegral = enumerateFromToIntegral maxBound
336+-------------------------------------------------------------------------------
338+-------------------------------------------------------------------------------
340+data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
342+{-# INLINE_NORMAL concat #-}
343+concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
344+concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
348+ return $ ConcatOuter s
350+ {-# INLINE_LATE step #-}
351+ step (ConcatOuter st) = do
355+ innerSt <- inject2 x
356+ return $ Skip (ConcatInner s innerSt)
357+ Skip s -> return $ Skip (ConcatOuter s)
360+ step (ConcatInner ost ist) = do
363+ Yield x s -> Yield x (ConcatInner ost s)
364+ Skip s -> Skip (ConcatInner ost s)
365+ Stop -> Skip (ConcatOuter ost)
367+data OuterProductState s1 s2 sy x y =
368+ OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
370+{-# INLINE_NORMAL outerProduct #-}
371+outerProduct :: Monad m
372+ => Unfold m a b -> Unfold m c d -> Unfold m (a, c) (b, d)
373+outerProduct (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
377+ return $ OuterProductOuter s1 y
379+ {-# INLINE_LATE step #-}
380+ step (OuterProductOuter st1 sy) = do
385+ return $ Skip (OuterProductInner s sy s2 x)
386+ Skip s -> return $ Skip (OuterProductOuter s sy)
389+ step (OuterProductInner ost sy ist x) = do
392+ Yield y s -> Yield (x, y) (OuterProductInner ost sy s x)
393+ Skip s -> Skip (OuterProductInner ost sy s x)
394+ Stop -> Skip (OuterProductOuter ost sy)
396+-- XXX This can be used to implement a Monad instance for "Unfold m ()".
398+data ConcatMapState s1 s2 = ConcatMapOuter s1 | ConcatMapInner s1 s2
400+{-# INLINE_NORMAL concatMapM #-}
401+concatMapM :: Monad m
402+ => (b -> m (Unfold m () c)) -> Unfold m a b -> Unfold m a c
403+concatMapM f (Unfold step1 inject1) = Unfold step inject
407+ return $ ConcatMapOuter s
409+ {-# INLINE_LATE step #-}
410+ step (ConcatMapOuter st) = do
414+ Unfold step2 inject2 <- f x
415+ innerSt <- inject2 ()
416+ return $ Skip (ConcatMapInner s (Stream (\_ ss -> step2 ss)
418+ Skip s -> return $ Skip (ConcatMapOuter s)
421+ step (ConcatMapInner ost (Stream istep ist)) = do
422+ r <- istep defState ist
424+ Yield x s -> Yield x (ConcatMapInner ost (Stream istep s))
425+ Skip s -> Skip (ConcatMapInner ost (Stream istep s))
426+ Stop -> Skip (ConcatMapOuter ost)