on master help sign in
Update fold docs, implement toFold for parsers
commitbcf0ab191f11f6421d03d43d7e975652b3d5e034 authorHarendra Kumar <harendra@composewell.com> dateSun 19 Nov 2023, 03:21 (3 years ago) parent32390c5
2 files changed+36−24
core/src/Streamly/Internal/Data/Fold/Type.hs+31−16 core/src/Streamly/Internal/Data/Parser.hs+5−8
core/src/Streamly/Internal/Data/Fold/Type.hs+31−16
@@ -493,20 +493,35 @@ import Streamly.Internal.Data.Fold.Step
493493-- The "final" function could be (s -> m (Step s b)), like in parsers so that
494494-- it can be called in a loop to drain the fold.
495495
496−-- | The type @Fold m a b@ having constructor @Fold step initial extract
497−-- final@ represents a fold over an input stream of values of type @a@ to a
498−-- final value of type @b@ in 'Monad' @m@.
499−--
500−-- The fold uses an intermediate state @s@ as accumulator, the type @s@ is
501−-- internal to the specific fold definition. The initial value of the fold
502−-- state @s@ is returned by @initial@. The @step@ function consumes an input
503−-- and either returns the final result @b@ if the fold is done or the next
504−-- intermediate state (see 'Step'). At any point the fold driver can extract
505−-- the result from the intermediate state using the @extract@ function. The
506−-- "final" function is used to finalize the fold, the driver can call it
507−-- whenever it holds a valid fold state which it will not be using anymore. The
508−-- state should not be used after finalization. Note that if the fold
509−-- terminates itself we won't have a valid fold state.
496+-- | The type @Fold m a b@ represents a consumer of an input stream of values
497+-- of type @a@ and returning a final value of type @b@ in 'Monad' @m@. The
498+-- constructor of a fold is @Fold step initial extract final@.
499+--
500+-- The fold uses an internal state of type @s@. The initial value of the state
501+-- @s@ is created by @initial@. This function is called once and only once
502+-- before the fold starts consuming input. Any resource allocation can be done
503+-- in this function.
504+--
505+-- The @step@ function is called on each input, it consumes an input and
506+-- returns the next intermediate state (see 'Step') or the final result @b@ if
507+-- the fold terminates.
508+--
509+-- If the fold is used as a scan, the @extract@ function is used by the scan
510+-- driver to map the current state @s@ of the fold to the fold result. Thus
511+-- @extract@ can be called multiple times. In some folds, where scanning does
512+-- not make sense, this function is left unimplemented; such folds cannot be
513+-- used as scans.
514+--
515+-- Before a fold terminates, @final@ is called once and only once (unless the
516+-- fold terminated in @initial@ itself). Any resources allocated by @initial@
517+-- can be released in @final@. In folds that do not require any cleanup
518+-- @extract@ and @final@ are typically the same.
519+--
520+-- When implementing fold combinators, care should be taken to cleanup any
521+-- state of the argument folds held by the fold by calling the respective
522+-- @final@ at all exit points of the fold. Also, @final@ should not be called
523+-- more than once. Note that if a fold terminates by 'Done' constructor, there
524+-- is no state to cleanup.
510525--
511526-- NOTE: The constructor is not yet released, smart constructors are provided
512527-- to create folds.
@@ -858,8 +873,8 @@ splitWith func
858873 step (SeqFoldR f st) a = runR (stepR st a) f
859874
860875 -- XXX splitWith should not be used for scanning
861− -- It would rarely make sense and resource cleanup would be expensive.
862− -- especially when multiple splitWith are chained.
876+ -- It would rarely make sense and resource tracking and cleanup would be
877+ -- expensive. especially when multiple splitWith are chained.
863878 extract _ = error "splitWith: cannot be used for scanning"
864879
865880 final (SeqFoldR f sR) = fmap f (finalR sR)
core/src/Streamly/Internal/Data/Parser.hs+5−8
@@ -21,8 +21,8 @@ module Streamly.Internal.Data.Parser
2121 , Step (..)
2222 , Initial (..)
2323
24− -- -- * Downgrade to Fold
25− -- , toFold
24+ -- * Downgrade to Fold
25+ , toFold
2626
2727 -- First order parsers
2828 -- * Accumulators
@@ -270,10 +270,6 @@ import Prelude hiding
270270-- Downgrade a parser to a Fold
271271-------------------------------------------------------------------------------
272272
273−-- XXX Parsers cannot be converted to folds, because they do not have a
274−-- scanning function. Can we move the applicative folds to parsers instead?
275−-- need to measure the performance.
276−{-
277273-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the
278274-- parser fails or tries to backtrack.
279275--
@@ -313,7 +309,9 @@ toFold (Parser pstep pinitial pextract) = Fold step initial extract final
313309 Done n _ -> derror n
314310 Error err -> eerror err
315311
316− extract st = do
312+ extract = error "toFold: parser cannot be used for scanning"
313+
314+ final st = do
317315 r <- pextract st
318316 case r of
319317 Done 0 b -> return b
@@ -321,7 +319,6 @@ toFold (Parser pstep pinitial pextract) = Fold step initial extract final
321319 Continue n _ -> cerror n
322320 Done n _ -> derror n
323321 Error err -> eerror err
324−-}
325322
326323-------------------------------------------------------------------------------
327324-- Upgrade folds to parses