@@ -8,6 +8,34 @@
8-- Stability : experimental
8-- Stability : experimental
9-- Portability : GHC
9-- Portability : GHC
11+-- = Unfolds and Streams
13+-- An 'Unfold' type is the same as the direct style 'Stream' type except that
14+-- it uses an inject function to determine the initial state of the stream
15+-- based on an input. A stream is a special case of Unfold when the static
16+-- input is unit or Void.
18+-- This allows an important optimization to occur in several cases, making the
19+-- 'Unfold' a more efficient abstraction. Consider the 'concatMap' and
20+-- 'concatUnfold' operations, the latter is more efficient. 'concatMap'
21+-- generates a new stream object from each element in the stream by applying
22+-- the supplied function to the element, the stream object includes the "step"
23+-- function as well as the initial "state" of the stream. Since the stream is
24+-- generated dynamically the compiler does not know the step function or the
25+-- state type statically at compile time, therefore, it cannot inline it. On
26+-- the other hand in case of 'concatUnfold' the compiler has visibility into
27+-- the unfold's state generation function, therefore, the compiler knows all
28+-- the types statically and it can inline the inject as well as the step
29+-- functions, generating efficient code. Essentially, the stream is not opaque
30+-- to the consumer in case of unfolds, the consumer knows how to generate the
31+-- stream from a seed using a known "inject" and "step" functions.
33+-- A Stream is like a data object whereas unfold is like a function. Being
34+-- function like, an Unfold is an instance of 'Category' and 'Arrow' type
37+-- = Unfolds and Folds
11-- Streams forcing a closed control flow loop can be categorized under
39-- Streams forcing a closed control flow loop can be categorized under
12-- two types, unfolds and folds, both of these are duals of each other.
40-- two types, unfolds and folds, both of these are duals of each other.
@@ -149,15 +177,12 @@ import Control.Monad.Trans.Control (MonadBaseControl, liftBaseOp_)
149import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
177import Data.IORef (newIORef, readIORef, mkWeakIORef, writeIORef)
150import Data.Maybe (isNothing)
178import Data.Maybe (isNothing)
151import Data.Void (Void)
179import Data.Void (Void)
152−import Fusion.Plugin.Types (Fuse(..))
153import GHC.Types (SPEC(..))
180import GHC.Types (SPEC(..))
154import Streamly.Internal.Data.Fold.Types (Fold(..))
181import Streamly.Internal.Data.Fold.Types (Fold(..))
155import Streamly.Internal.Data.IOFinalizer
182import Streamly.Internal.Data.IOFinalizer
156 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
183 (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)
157import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
184import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))
158import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
185import Streamly.Internal.Data.Time.Clock (Clock(Monotonic), getTime)
159−import Streamly.Internal.Data.Unfold.Types
160− (Unfold(..), lmap, map, const, concatMapM)
161import System.Mem (performMajorGC)
186import System.Mem (performMajorGC)
163import qualified Prelude
188import qualified Prelude
@@ -169,6 +194,7 @@ import qualified Streamly.Internal.Data.Stream.StreamK as K (uncons)
169import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
194import qualified Streamly.Internal.Data.Stream.StreamK.Type as K
171import Streamly.Internal.Data.SVar
196import Streamly.Internal.Data.SVar
197+import Streamly.Internal.Data.Unfold.Types
172import Prelude
198import Prelude
173 hiding (concat, map, mapM, takeWhile, take, filter, const, zipWith
199 hiding (concat, map, mapM, takeWhile, take, filter, const, zipWith
174 , drop, dropWhile)
200 , drop, dropWhile)
@@ -399,34 +425,6 @@ effect eff = Unfold step inject
399 step True = eff >>= \r -> return $ Yield r False
425 step True = eff >>= \r -> return $ Yield r False
400 step False = return Stop
426 step False = return Stop
402−-- XXX change it to yieldM or change yieldM in Prelude to singletonM
404−-- | Lift a monadic function into an unfold generating a singleton stream.
406−{-# INLINE singletonM #-}
407−singletonM :: Monad m => (a -> m b) -> Unfold m a b
408−singletonM f = Unfold step inject
410− inject x = return $ Just x
411− {-# INLINE_LATE step #-}
412− step (Just x) = f x >>= \r -> return $ Yield r Nothing
413− step Nothing = return Stop
415−-- | Lift a pure function into an unfold generating a singleton stream.
417−{-# INLINE singleton #-}
418−singleton :: Monad m => (a -> b) -> Unfold m a b
419−singleton f = singletonM $ return . f
421−-- | Identity unfold. Generates a singleton stream with the seed as the only
422−-- element in the stream.
424−-- > identity = singletonM return
426−{-# INLINE identity #-}
427−identity :: Monad m => Unfold m a a
428−identity = singletonM return
430-- | Convert a list of pure values to a 'Stream'
428-- | Convert a list of pure values to a 'Stream'
431{-# INLINE_LATE fromList #-}
429{-# INLINE_LATE fromList #-}
432fromList :: Monad m => Unfold m [a] a
430fromList :: Monad m => Unfold m [a] a
@@ -842,53 +840,6 @@ fromProducer = Unfold step (return . FromSVarRead)
842-- Zipping
840-- Zipping
843-------------------------------------------------------------------------------
841-------------------------------------------------------------------------------
845−{-# INLINE_NORMAL zipWithM #-}
847− => (a -> b -> m c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
848−zipWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
855− return (s1, s2, Nothing)
857− {-# INLINE_LATE step #-}
858− step (s1, s2, Nothing) = do
862− Yield x s -> Skip (s, s2, Just x)
863− Skip s -> Skip (s, s2, Nothing)
866− step (s1, s2, Just x) = do
871− return $ Yield z (s1, s, Nothing)
872− Skip s -> return $ Skip (s1, s, Just x)
875−-- | Divide the input into two unfolds and then zip the outputs to a single
880−-- $ S.concatUnfold (UF.zipWith (,) UF.identity (UF.singleton sqrt))
881−-- $ S.map (\x -> (x,x))
882−-- $ S.fromList [1..10]
887−{-# INLINE zipWith #-}
889− => (a -> b -> c) -> Unfold m x a -> Unfold m y b -> Unfold m (x, y) c
890−zipWith f = zipWithM (\a b -> return (f a b))
892-- | Distribute the input to two unfolds and then zip the outputs to a single
843-- | Distribute the input to two unfolds and then zip the outputs to a single
893-- stream.
844-- stream.
@@ -907,39 +858,6 @@ teeZipWith f unf1 unf2 = lmap (\x -> (x,x)) $ zipWith f unf1 unf2
908-------------------------------------------------------------------------------
859-------------------------------------------------------------------------------
910−{-# ANN type ConcatState Fuse #-}
911−data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2
913−-- | Apply the second unfold to each output element of the first unfold and
914−-- flatten the output in a single stream.
918−{-# INLINE_NORMAL concat #-}
919−concat :: Monad m => Unfold m a b -> Unfold m b c -> Unfold m a c
920−concat (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject
924− return $ ConcatOuter s
926− {-# INLINE_LATE step #-}
927− step (ConcatOuter st) = do
931− innerSt <- inject2 x
932− return $ Skip (ConcatInner s innerSt)
933− Skip s -> return $ Skip (ConcatOuter s)
936− step (ConcatInner ost ist) = do
939− Yield x s -> Yield x (ConcatInner ost s)
940− Skip s -> Skip (ConcatInner ost s)
941− Stop -> Skip (ConcatOuter ost)
943data OuterProductState s1 s2 sy x y =
861data OuterProductState s1 s2 sy x y =
944 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x
862 OuterProductOuter s1 y | OuterProductInner s1 sy s2 x