@@ -1,31 +1,29 @@
2−-- Module : Streamly.Internal.Data.Unfold.Resume
2+-- Module : Streamly.Internal.Data.Producer
3-- Copyright : (c) 2021 Composewell Technologies
3-- Copyright : (c) 2021 Composewell Technologies
4-- License : BSD-3-Clause
4-- License : BSD-3-Clause
5-- Maintainer : streamly@composewell.com
5-- Maintainer : streamly@composewell.com
6-- Stability : experimental
6-- Stability : experimental
7-- Portability : GHC
7-- Portability : GHC
9−-- Unfolds in "Streamly.Internal.Data.Unfold" module run to completion, they
9+-- A 'Producer' is an 'Unfold' with an 'extract' function added to to extract
10−-- cannot be stopped and then resumed. In contrast, the unfolds in this module
10+-- the state. It is more powerful but less general than an Unfold.
11−-- allow extraction of the intermediate state which can be used to resume the
14−-- For simple cases the seed itself can be used to represent the intermediate
12+-- A 'Producer' represents steps of a loop generating a sequence of elements.
15−-- state as well. For example, if we are iterating through a list or an array
13+-- While unfolds are closed representation of imperative loops with some opaque
16−-- we can return the remaining list or the remaining array slice as the
14+-- internal state, producers are open loops with the state being accesible to
17−-- state and resume from it later.
15+-- the user.
19−-- In general, the intermediate state could be different than the seed. Also,
17+-- Unlike an unfold, which runs a loop till completion, a producer can be
20−-- when unfolds are nested, for example using 'concat', then each nesting layer
18+-- stopped in the middle, its state can be extracted, examined, changed, and
21−-- would have its own state.
19+-- then it can be resumed later from the stopped state.
23−-- In imperative terms, an unfold is like a for loop (or an iterator), the
24−-- state of the unfold is the state of the loop. The state of the 'concat' of
25−-- unfolds is like the state of each loop in the nesting of loops.
21+-- A producer can be used in places where a CPS stream would otherwise be
22+-- needed, because the state of the loop can be passed around. However, it can
23+-- be much more efficient than CPS because it allows stream fusion and
24+-- unecessary function calls can be avoided.
28−module Streamly.Internal.Data.Unfold.Resume
26+module Streamly.Internal.Data.Producer
29 ( Unfold (..)
27 ( Unfold (..)
31 -- * Converting
29 -- * Converting
@@ -54,43 +52,43 @@ import Control.Monad.Catch (MonadThrow, throwM)
54import GHC.Exts (SpecConstrAnnotation(..))
52import GHC.Exts (SpecConstrAnnotation(..))
55import GHC.Types (SPEC(..))
53import GHC.Types (SPEC(..))
56import Streamly.Internal.Data.Parser.ParserD (ParseError(..), Step(..))
54import Streamly.Internal.Data.Parser.ParserD (ParseError(..), Step(..))
55+import Streamly.Internal.Data.Producer.Source (Source)
57import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
56import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
58import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..))
57import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..))
59import Streamly.Internal.Data.SVar (defState)
58import Streamly.Internal.Data.SVar (defState)
60−import Streamly.Internal.Data.Unfold.Source (Source)
59+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
62import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
61import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
63import qualified Streamly.Internal.Data.Parser.ParserK.Types as ParserK
62import qualified Streamly.Internal.Data.Parser.ParserK.Types as ParserK
64−import qualified Streamly.Internal.Data.Unfold.Types as Unfold
63+import qualified Streamly.Internal.Data.Producer.Source as Source
65−import qualified Streamly.Internal.Data.Unfold.Source as Source
67−import Streamly.Internal.Data.Unfold.Resume.Type
65+import Streamly.Internal.Data.Producer.Type
68import Prelude hiding (concat)
66import Prelude hiding (concat)
70−-- XXX We should write unfolds as resumable unfolds where possible and define
68+-- XXX We should write unfolds as producers where possible and define
71−-- the regular unfolds using "simplify".
69+-- unfolds using "simplify".
73-------------------------------------------------------------------------------
71-------------------------------------------------------------------------------
74−-- Converting to regular unfolds
72+-- Converting to unfolds
75-------------------------------------------------------------------------------
73-------------------------------------------------------------------------------
77−-- | Simplify a resumable unfold to a regular unfold.
75+-- | Simplify a producer to an unfold.
79-- /Internal/
77-- /Internal/
80{-# INLINE simplify #-}
78{-# INLINE simplify #-}
81−simplify :: Unfold m a b -> Unfold.Unfold m a b
79+simplify :: Producer m a b -> Unfold m a b
82−simplify (Unfold step inject _) = Unfold.Unfold step inject
80+simplify (Producer step inject _) = Unfold step inject
84-------------------------------------------------------------------------------
82-------------------------------------------------------------------------------
86-------------------------------------------------------------------------------
84-------------------------------------------------------------------------------
88−-- | Convert a StreamD stream into an Unfold.
86+-- | Convert a StreamD stream into a producer.
90-- /Internal/
88-- /Internal/
91{-# INLINE_NORMAL fromStreamD #-}
89{-# INLINE_NORMAL fromStreamD #-}
92−fromStreamD :: Monad m => Unfold m (Stream m a) a
90+fromStreamD :: Monad m => Producer m (Stream m a) a
93−fromStreamD = Unfold step return return
91+fromStreamD = Producer step return return
@@ -106,6 +104,8 @@ fromStreamD = Unfold step return return
106-- Parsing
104-- Parsing
107-------------------------------------------------------------------------------
105-------------------------------------------------------------------------------
107+-- XXX This should probably be moved to the Source module?
109-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
109-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
110-- to make a newtype.
110-- to make a newtype.
111{-# ANN type List NoSpecConstr #-}
111{-# ANN type List NoSpecConstr #-}
@@ -115,12 +115,12 @@ newtype List a = List {getList :: [a]}
116 :: MonadThrow m
116 :: MonadThrow m
117 => ParserD.Parser m a b
117 => ParserD.Parser m a b
118− -> Unfold m (Source s a) a
118+ -> Producer m (Source s a) a
119 -> Source s a
119 -> Source s a
120 -> m (b, Source s a)
120 -> m (b, Source s a)
122 (ParserD.Parser pstep initial extract)
122 (ParserD.Parser pstep initial extract)
123− (Unfold ustep uinject uextract)
123+ (Producer ustep uinject uextract)
124 seed = do
124 seed = do
126 state <- uinject seed
126 state <- uinject seed
@@ -197,7 +197,7 @@ parseD
198 :: MonadThrow m
198 :: MonadThrow m
199 => ParserK.Parser m a b
199 => ParserK.Parser m a b
200− -> Unfold m (Source s a) a
200+ -> Producer m (Source s a) a
201 -> Source s a
201 -> Source s a
202 -> m (b, Source s a)
202 -> m (b, Source s a)
203parse = parseD . ParserK.fromParserK
203parse = parseD . ParserK.fromParserK
@@ -208,8 +208,10 @@ parse = parseD . ParserK.fromParserK
209{-# INLINE parseManyD #-}
209{-# INLINE parseManyD #-}
210parseManyD :: MonadThrow m =>
210parseManyD :: MonadThrow m =>
211− ParserD.Parser m a b -> Unfold m (Source x a) a -> Unfold m (Source x a) b
211+ ParserD.Parser m a b
212+ -> Producer m (Source x a) a
213+ -> Producer m (Source x a) b
212−parseManyD parser reader = Unfold step return return
214+parseManyD parser reader = Producer step return return
@@ -227,5 +229,7 @@ parseManyD parser reader = Unfold step return return
227-- /Internal/
229-- /Internal/
228{-# INLINE parseMany #-}
230{-# INLINE parseMany #-}
229parseMany :: MonadThrow m =>
231parseMany :: MonadThrow m =>
230− ParserK.Parser m a b -> Unfold m (Source x a) a -> Unfold m (Source x a) b
232+ ParserK.Parser m a b
233+ -> Producer m (Source x a) a
234+ -> Producer m (Source x a) b
231parseMany parser = parseManyD (ParserK.fromParserK parser)
235parseMany parser = parseManyD (ParserK.fromParserK parser)