@@ -1,31 +1,29 @@
11-- |
2−-- Module : Streamly.Internal.Data.Unfold.Resume
2+-- Module : Streamly.Internal.Data.Producer
33-- Copyright : (c) 2021 Composewell Technologies
44-- License : BSD-3-Clause
55-- Maintainer : streamly@composewell.com
66-- Stability : experimental
77-- Portability : GHC
88--
9−-- Unfolds in "Streamly.Internal.Data.Unfold" module run to completion, they
10−-- cannot be stopped and then resumed. In contrast, the unfolds in this module
11−-- allow extraction of the intermediate state which can be used to resume the
12−-- unfold later on.
9+-- A 'Producer' is an 'Unfold' with an 'extract' function added to to extract
10+-- the state. It is more powerful but less general than an Unfold.
1311--
14−-- For simple cases the seed itself can be used to represent the intermediate
15−-- state as well. For example, if we are iterating through a list or an array
16−-- we can return the remaining list or the remaining array slice as the
17−-- state and resume from it later.
12+-- A 'Producer' represents steps of a loop generating a sequence of elements.
13+-- While unfolds are closed representation of imperative loops with some opaque
14+-- internal state, producers are open loops with the state being accesible to
15+-- the user.
1816--
19−-- In general, the intermediate state could be different than the seed. Also,
20−-- when unfolds are nested, for example using 'concat', then each nesting layer
21−-- would have its own state.
22−--
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.
17+-- Unlike an unfold, which runs a loop till completion, a producer can be
18+-- stopped in the middle, its state can be extracted, examined, changed, and
19+-- then it can be resumed later from the stopped state.
2620--
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.
2725
28−module Streamly.Internal.Data.Unfold.Resume
26+module Streamly.Internal.Data.Producer
2927 ( Unfold (..)
3028
3129 -- * Converting
@@ -54,43 +52,43 @@ import Control.Monad.Catch (MonadThrow, throwM)
5452import GHC.Exts (SpecConstrAnnotation(..))
5553import GHC.Types (SPEC(..))
5654import Streamly.Internal.Data.Parser.ParserD (ParseError(..), Step(..))
55+import Streamly.Internal.Data.Producer.Source (Source)
5756import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))
5857import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..))
5958import Streamly.Internal.Data.SVar (defState)
60−import Streamly.Internal.Data.Unfold.Source (Source)
59+import Streamly.Internal.Data.Unfold.Types (Unfold(..))
6160
6261import qualified Streamly.Internal.Data.Parser.ParserD as ParserD
6362import qualified Streamly.Internal.Data.Parser.ParserK.Types as ParserK
64−import qualified Streamly.Internal.Data.Unfold.Types as Unfold
65−import qualified Streamly.Internal.Data.Unfold.Source as Source
63+import qualified Streamly.Internal.Data.Producer.Source as Source
6664
67−import Streamly.Internal.Data.Unfold.Resume.Type
65+import Streamly.Internal.Data.Producer.Type
6866import Prelude hiding (concat)
6967
70−-- XXX We should write unfolds as resumable unfolds where possible and define
71−-- the regular unfolds using "simplify".
68+-- XXX We should write unfolds as producers where possible and define
69+-- unfolds using "simplify".
7270--
7371-------------------------------------------------------------------------------
74−-- Converting to regular unfolds
72+-- Converting to unfolds
7573-------------------------------------------------------------------------------
7674
77−-- | Simplify a resumable unfold to a regular unfold.
75+-- | Simplify a producer to an unfold.
7876--
7977-- /Internal/
8078{-# INLINE simplify #-}
81−simplify :: Unfold m a b -> Unfold.Unfold m a b
82−simplify (Unfold step inject _) = Unfold.Unfold step inject
79+simplify :: Producer m a b -> Unfold m a b
80+simplify (Producer step inject _) = Unfold step inject
8381
8482-------------------------------------------------------------------------------
8583-- Unfolds
8684-------------------------------------------------------------------------------
8785
88−-- | Convert a StreamD stream into an Unfold.
86+-- | Convert a StreamD stream into a producer.
8987--
9088-- /Internal/
9189{-# INLINE_NORMAL fromStreamD #-}
92−fromStreamD :: Monad m => Unfold m (Stream m a) a
93−fromStreamD = Unfold step return return
90+fromStreamD :: Monad m => Producer m (Stream m a) a
91+fromStreamD = Producer step return return
9492
9593 where
9694
@@ -106,6 +104,8 @@ fromStreamD = Unfold step return return
106104-- Parsing
107105-------------------------------------------------------------------------------
108106
107+-- XXX This should probably be moved to the Source module?
108+--
109109-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need
110110-- to make a newtype.
111111{-# ANN type List NoSpecConstr #-}
@@ -115,12 +115,12 @@ newtype List a = List {getList :: [a]}
115115parseD
116116 :: MonadThrow m
117117 => ParserD.Parser m a b
118− -> Unfold m (Source s a) a
118+ -> Producer m (Source s a) a
119119 -> Source s a
120120 -> m (b, Source s a)
121121parseD
122122 (ParserD.Parser pstep initial extract)
123− (Unfold ustep uinject uextract)
123+ (Producer ustep uinject uextract)
124124 seed = do
125125
126126 state <- uinject seed
@@ -197,7 +197,7 @@ parseD
197197parse
198198 :: MonadThrow m
199199 => ParserK.Parser m a b
200− -> Unfold m (Source s a) a
200+ -> Producer m (Source s a) a
201201 -> Source s a
202202 -> m (b, Source s a)
203203parse = parseD . ParserK.fromParserK
@@ -208,8 +208,10 @@ parse = parseD . ParserK.fromParserK
208208
209209{-# INLINE parseManyD #-}
210210parseManyD :: MonadThrow m =>
211− ParserD.Parser m a b -> Unfold m (Source x a) a -> Unfold m (Source x a) b
212−parseManyD parser reader = Unfold step return return
211+ ParserD.Parser m a b
212+ -> Producer m (Source x a) a
213+ -> Producer m (Source x a) b
214+parseManyD parser reader = Producer step return return
213215
214216 where
215217
@@ -227,5 +229,7 @@ parseManyD parser reader = Unfold step return return
227229-- /Internal/
228230{-# INLINE parseMany #-}
229231parseMany :: 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
231235parseMany parser = parseManyD (ParserK.fromParserK parser)