Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions benchmark/Streamly/Benchmark/Data/Fold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -808,10 +808,10 @@ unzipWithMinM n = withStream n $ Stream.fold (FL.unzipWithMinM f FL.sum FL.lengt
-- Nested
-------------------------------------------------------------------------------

{-# INLINE unfoldMany #-}
unfoldMany :: Int -> IO ()
unfoldMany n =
Stream.fold (FL.unfoldMany Unfold.replicateM FL.drain)
{-# INLINE unfoldEach #-}
unfoldEach :: Int -> IO ()
unfoldEach n =
Stream.fold (FL.unfoldEach Unfold.replicateM FL.drain)
$ Stream.fromPure (n, randomRIO (1, 1 :: Int))

-------------------------------------------------------------------------------
Expand Down Expand Up @@ -1114,7 +1114,7 @@ benchmarks env value =
, benchIO "split_ (all, any)" $ split_ value
, benchIO "tee (all, any)" $ teeAllAny value
, benchIO "many drain (take 1)" $ many value
, benchIO "unfoldMany" $ unfoldMany value
, benchIO "unfoldEach" $ unfoldEach value
, benchIO "shortest (sum, length)" $ shortest value
, benchIO "longest (sum, length)" $ longest value
, benchIO "tee (sum, length)" $ teeSumLength value
Expand Down
16 changes: 9 additions & 7 deletions core/src/Streamly/Data/Fold.hs
Original file line number Diff line number Diff line change
Expand Up @@ -136,26 +136,28 @@
-- Streams are producers, transformations on streams happen on the output side:
--
-- >>> :{
-- f stream =
-- Stream.filter odd stream
-- f fold =
-- Stream.fromList [1..100 :: Int]
-- & Stream.filter odd
-- & fmap (+1)
-- & Stream.fold Fold.sum
-- & Stream.fold fold
-- :}
--
-- >>> f $ Stream.fromList [1..100 :: Int]
-- >>> f Fold.sum
-- 2550
--
-- Folds are stream consumers with an input stream and an output value, stream
-- transformations on folds happen on the input side:
--
-- >>> :{
-- f =
-- Fold.filter odd
-- f stream =
-- Fold.drive stream
-- $ Fold.filter odd
-- $ Fold.lmap (+1)
-- $ Fold.sum
-- :}
--
-- >>> Stream.fold f $ Stream.fromList [1..100 :: Int]
-- >>> f $ Stream.fromList [1..100 :: Int]
-- 2550
--
-- Notice the similiarity in the definition of @f@ in both cases, the only
Expand Down
35 changes: 19 additions & 16 deletions core/src/Streamly/Internal/Data/Fold/Combinators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -201,10 +201,11 @@ module Streamly.Internal.Data.Fold.Combinators
, intersperseWithQuotes

-- ** Nesting
, unfoldMany
, unfoldEach
, concatSequence

-- * Deprecated
, unfoldMany
, drainBy
, head
, sequence
Expand Down Expand Up @@ -592,8 +593,7 @@ repeated = error "Not implemented yet!"
-- >>> drainMapM f = Fold.lmapM f Fold.drain
-- >>> drainMapM f = Fold.foldMapM (void . f)
--
-- Drain all input after passing it through a monadic function. This is the
-- dual of mapM_ on stream producers.
-- Drain all input after passing it through a monadic function.
--
{-# INLINE drainMapM #-}
drainMapM :: Monad m => (a -> m b) -> Fold m a ()
Expand Down Expand Up @@ -1741,7 +1741,7 @@ tee = teeWith (,)
-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large
-- number of consumers? For polymorphic case a vector could be helpful. For
-- Unboxs we can use arrays. Will need separate APIs for those.
--

-- | Distribute one copy of the stream to each fold and collect the results in
-- a container.
--
Expand All @@ -1759,8 +1759,6 @@ tee = teeWith (,)
--
-- >>> distribute = Prelude.foldr (Fold.teeWith (:)) (Fold.fromPure [])
--
-- This is the consumer side dual of the producer side 'sequence' operation.
--
-- Stops when all the folds stop.
--
{-# INLINE distribute #-}
Expand Down Expand Up @@ -1876,8 +1874,6 @@ partitionByMUsing t f fld1 fld2 =
-- (67,33)
--
--
-- This is the consumer side dual of the producer side 'mergeBy' operation.
--
-- When one fold is done, any input meant for it is ignored until the other
-- fold is also done.
--
Expand Down Expand Up @@ -1921,7 +1917,9 @@ partitionByMinM = partitionByMUsing teeWithMin
-- :}
-- ("Even 50","Odd 50")
--
-- /Pre-release/
-- NOTE: This is the exact analogue of the @choose@ method of the Contravariant
-- functor typeclass Decidable.
--
{-# INLINE partitionBy #-}
partitionBy :: Monad m
=> (a -> Either b c) -> Fold m b x -> Fold m c y -> Fold m a (x, y)
Expand Down Expand Up @@ -2004,7 +2002,9 @@ unzipWithMinM = unzipWithMUsing teeWithMin
--
-- This fold terminates when both the input folds terminate.
--
-- /Pre-release/
-- NOTE: This is the exact analogue of the @divide@ method of the Contravariant
-- functor typeclass Divisible.
--
{-# INLINE unzipWith #-}
unzipWith :: Monad m
=> (a -> (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)
Expand All @@ -2025,8 +2025,6 @@ unzipWith f = unzipWithM (return . f)
--
-- >>> unzip = Fold.unzipWith id
--
-- This is the consumer side dual of the producer side 'zip' operation.
--
{-# INLINE unzip #-}
unzip :: Monad m => Fold m a x -> Fold m b y -> Fold m (a,b) (x,y)
unzip = unzipWith id
Expand Down Expand Up @@ -2199,13 +2197,13 @@ toStreamRev = fromScanl Scanl.toStreamRev
-- | Unfold and flatten the input stream of a fold.
--
-- @
-- Stream.fold (unfoldMany u f) = Stream.fold f . Stream.unfoldMany u
-- Stream.fold (unfoldEach u f) = Stream.fold f . Stream.unfoldEach u
-- @
--
-- /Pre-release/
{-# INLINE unfoldMany #-}
unfoldMany :: Monad m => Unfold m a b -> Fold m b c -> Fold m a c
unfoldMany (Unfold ustep inject) (Fold fstep initial extract final) =
{-# INLINE unfoldEach #-}
unfoldEach :: Monad m => Unfold m a b -> Fold m b c -> Fold m a c
unfoldEach (Unfold ustep inject) (Fold fstep initial extract final) =
Fold consume initial extract final

where
Expand All @@ -2226,6 +2224,11 @@ unfoldMany (Unfold ustep inject) (Fold fstep initial extract final) =
{-# INLINE_LATE consume #-}
consume s a = inject a >>= produce s

-- {-# DEPRECATED unfoldMany "Use unfoldEach instead." #-}
{-# INLINE unfoldMany #-}
unfoldMany :: Monad m => Unfold m a b -> Fold m b c -> Fold m a c
unfoldMany = unfoldEach

-- | Get the bottom most @n@ elements using the supplied comparison function.
--
{-# INLINE bottomBy #-}
Expand Down
85 changes: 57 additions & 28 deletions core/src/Streamly/Internal/Data/Fold/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,16 @@
-- can be expressed using 'mconcat' and a suitable 'Monoid'. Instead of
-- writing folds we can write Monoids and turn them into folds.
--
-- = Initial, Final Duality
--
-- Currently there is a discrepancy between folds and streams, folds have an
-- "initial" to initialize the fold without any input. A producer side dual
-- would be a finalizer called whenever the stream stops.
--
-- However, the initial action in folds creates a discrepancy with the CPS
-- folds, and the same may be the case if we have a stop/cleanup operation in
-- streams.

module Streamly.Internal.Data.Fold.Type
(
module Streamly.Internal.Data.Fold.Step
Expand Down Expand Up @@ -816,6 +826,8 @@ fromRefold (Refold step inject extract) c =
-- Basic Folds
------------------------------------------------------------------------------

-- drain is a dual of infinite streams. This is the /dev/null of folds.

-- | A fold that drains all its input, running the effects and discarding the
-- results.
--
Expand All @@ -830,6 +842,8 @@ drain = fromScanl Scanl.drain
-- To Containers
------------------------------------------------------------------------------

-- toList is a dual of Stream.fromList.

-- | Folds the input stream to a list.
--
-- /Warning!/ working on large lists accumulated as buffers in memory could be
Expand Down Expand Up @@ -876,6 +890,8 @@ toListRev = foldl' (flip (:)) []
toStreamKRev :: Monad m => Fold m a (K.StreamK n a)
toStreamKRev = fromScanl Scanl.toStreamKRev

-- toStreamK is a dual of Stream.fromStreamK

-- | A fold that buffers its input to a pure stream.
--
-- >>> toStreamK = foldr StreamK.cons StreamK.nil
Expand Down Expand Up @@ -909,6 +925,9 @@ genericLength = fromScanl Scanl.genericLength
length :: Monad m => Fold m a Int
length = fromScanl Scanl.length

-- Naming: "last" makes better sense for folds whereas "latest" makes better
-- sense for scans.

-- | Returns the latest element of the input stream, if any.
--
-- >>> latest = Fold.foldl1' (\_ x -> x)
Expand Down Expand Up @@ -939,39 +958,27 @@ instance Functor m => Functor (Fold m a) where
step s b = fmap2 f (step1 s b)
fmap2 g = fmap (fmap g)

-- XXX These are singleton folds that are closed for input. The correspondence
-- to a nil stream would be a nil fold that returns "Done" in "initial" i.e. it
-- does not produce any accumulator value. However, we do not have a
-- representation of an empty value in folds, because the Done constructor
-- always produces a value (Done b). We can potentially use "Partial s b" and
-- "Done" to make the type correspond to the stream type. That may be possible
-- if we introduce the "Skip" constructor as well because after the last
-- "Partial s b" we have to emit a "Skip to Done" state to keep cranking the
-- fold until it is done.
--
-- There is also the asymmetry between folds and streams because folds have an
-- "initial" to initialize the fold without any input. A similar concept is
-- possible in streams as well to stop the stream. That would be a "closing"
-- operation for the stream which can be called even without consuming any item
-- from the stream or when we are done consuming.
-- NOTE: Canonical producer side dual of this is "Stream.nil" which generates a
-- nil stream regardless of the seed. This folds a nil stream to some known
-- value, reversing the producer side process.
--
-- However, the initial action in folds creates a discrepancy with the CPS
-- folds, and the same may be the case if we have a stop/cleanup operation in
-- streams.
-- Alternate names: const

-- | Make a fold that yields the supplied value without consuming any further
-- input.
--
-- /Pre-release/
-- | Make a fold that immediately returns a constant output without consuming
-- any input.
--
{-# INLINE fromPure #-}
fromPure :: Applicative m => b -> Fold m a b
fromPure b = Fold undefined (pure $ Done b) pure pure

-- | Make a fold that yields the result of the supplied effectful action
-- without consuming any further input.
-- NOTE: Canonical producer side dual of this is "Stream.nilM" which generates
-- a nil stream and an effect. This folds a nil stream to some known value and
-- an effect, reverse of the producer.
--
-- /Pre-release/
-- Alternative names: constM

-- | Make a fold that immediately returns a constant output along with an
-- effect without consuming any input.
--
{-# INLINE fromEffect #-}
fromEffect :: Applicative m => m b -> Fold m a b
Expand All @@ -980,6 +987,9 @@ fromEffect b = Fold undefined (Done <$> b) pure pure
{-# ANN type SeqFoldState Fuse #-}
data SeqFoldState sl f sr = SeqFoldL !sl | SeqFoldR !f !sr

-- dual of Stream.append
-- Alternative names: appendWith, serialWith.

-- | Sequential fold application. Apply two folds sequentially to an input
-- stream. The input is provided to the first fold, when it is done - the
-- remaining input is provided to the second fold. When the second fold is done
Expand Down Expand Up @@ -1426,7 +1436,22 @@ concatMap f (Fold stepa initiala _ finala) =
-- Mapping on input
------------------------------------------------------------------------------

-- | @lmap f fold@ maps the function @f@ on the input of the fold.
-- | Transform the input of a fold using a pure function.
--
-- @lmap f fold@ applies @f@ to each input element before passing it to
-- @fold@.
--
-- Think of @lmap f fold@ as plugging @f@ into the input socket of
-- @fold@. The input of the resulting fold is the input of the plug f.
-- The output of the plug must match the input of the fold.
--
-- @
-- +-----------+ +-----------------+
-- a -> | f | -> b -> | Fold m b c | -> c
-- +-----------+ +-----------------+
-- \\_______________________/
-- Fold m a c
-- @
--
-- Definition:
--
Expand All @@ -1439,13 +1464,15 @@ concatMap f (Fold stepa initiala _ finala) =
-- 338350
--
{-# INLINE lmap #-}
lmap :: (a -> b) -> Fold m b r -> Fold m a r
lmap :: (a -> b) -> Fold m b c -> Fold m a c
lmap f (Fold step begin done final) = Fold step' begin done final
where
step' x a = step x (f a)

-- | @lmapM f fold@ maps the monadic function @f@ on the input of the fold.
--
-- See 'lmap' for detailed documentation.
--
{-# INLINE lmapM #-}
lmapM :: Monad m => (a -> m b) -> Fold m b r -> Fold m a r
lmapM f (Fold step begin done final) = Fold step' begin done final
Expand Down Expand Up @@ -2037,6 +2064,8 @@ ifThen predicate
-- 'duplicate' essentially appends a stream to the fold without finishing the
-- fold. Compare with 'snoc' which appends a singleton value to the fold.
--
-- See 'Fold.addStream' as well.
--
-- /Pre-release/
{-# INLINE duplicate #-}
duplicate :: Monad m => Fold m a b -> Fold m a (Fold m a b)
Expand Down Expand Up @@ -2069,7 +2098,7 @@ reduce (Fold step initial extract final) = do
i <- initial
return $ Fold step (return i) extract final

-- This is the dual of Stream @cons@.
-- This is a fold builder equivalent of the stream builder @cons@.

-- | Append an effect to the fold lazily, in other words run a single
-- step of the fold.
Expand Down
32 changes: 26 additions & 6 deletions core/src/Streamly/Internal/Data/Parser/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1503,15 +1503,35 @@ instance (MonadIO m) => MonadIO (Parser a m) where
-- Mapping on input
------------------------------------------------------------------------------

-- | @lmap f parser@ maps the function @f@ on the input of the parser.
-- | Transform the input of a parser using a pure function.
--
-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)
-- Right 338350
-- @lmap f parser@ applies @f@ to each input element before passing it to
-- @parser@.
--
-- Think of @lmap f parser@ as plugging @f@ into the input socket of
-- @parser@. The input of the resulting parser is the input of the plug f.
-- The output of the plug must match the input of the fold.
--
-- @
-- +-----------+ +-----------------+
-- a -> | f | -> b -> | Parser b m c | -> c
-- +-----------+ +-----------------+
-- \\_______________________/
-- Parser a m c
-- @
--
-- Definition:
--
-- > lmap = Parser.lmapM return
-- >>> lmap = Fold.lmapM return
--
-- Example:
--
-- >>> sumSquared = Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)
-- >>> Stream.parse sumSquared (Stream.enumerateFromTo 1 100)
-- Right 338350
--
{-# INLINE lmap #-}
lmap :: (a -> b) -> Parser b m r -> Parser a m r
lmap :: (a -> b) -> Parser b m c -> Parser a m c
lmap f (Parser step begin done) = Parser step1 begin done

where
Expand All @@ -1521,7 +1541,7 @@ lmap f (Parser step begin done) = Parser step1 begin done
-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.
--
{-# INLINE lmapM #-}
lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r
lmapM :: Monad m => (a -> m b) -> Parser b m c -> Parser a m c
lmapM f (Parser step begin done) = Parser step1 begin done

where
Expand Down
Loading
Loading