on master help sign in
Add a "foldFilter" combinator for folds
8dabc2eHarendra Kumar
benchmark/Streamly/Benchmark/Data/Fold.hs+24−2
@@ -27,7 +27,7 @@ import qualified Streamly.Prelude as S
27
27
28import Gauge
28import Gauge
29import Streamly.Benchmark.Common
29import Streamly.Benchmark.Common
30−import Prelude hiding (all, any, take, unzip, sequence_)
30+import Prelude hiding (all, any, take, unzip, sequence_, filter)
31
31
32-- We need a monadic bind here to make sure that the function f does not get
32-- We need a monadic bind here to make sure that the function f does not get
33-- completely optimized out by the compiler in some cases.
33-- completely optimized out by the compiler in some cases.
@@ -78,6 +78,25 @@ sequence_ value =
78 {-# INLINE f #-}
78 {-# INLINE f #-}
79 f m k = FL.concatMap (const k) m
79 f m k = FL.concatMap (const k) m
80
80
81+-------------------------------------------------------------------------------
82+-- Filter
83+-------------------------------------------------------------------------------
84+
85+{-# INLINE filter #-}
86+filter :: Monad m => Int -> SerialT m Int -> m ()
87+filter _ = IP.fold (FL.filter even FL.drain)
88+
89+{-# INLINE foldFilter #-}
90+foldFilter :: Monad m => Int -> SerialT m Int -> m ()
91+foldFilter _ = IP.fold (FL.foldFilter (FL.satisfy even) FL.drain)
92+
93+{-# INLINE foldFilter2 #-}
94+foldFilter2 :: Monad m => Int -> SerialT m Int -> m ()
95+foldFilter2 _ =
96+ IP.fold
97+ $ FL.foldFilter (FL.satisfy even)
98+ $ FL.foldFilter (FL.satisfy odd) FL.drain
99+
81-------------------------------------------------------------------------------
100-------------------------------------------------------------------------------
82-- Splitting by serial application
101-- Splitting by serial application
83-------------------------------------------------------------------------------
102-------------------------------------------------------------------------------
@@ -323,7 +342,10 @@ o_1_space_serial_composition :: Int -> [Benchmark]
323o_1_space_serial_composition value =
342o_1_space_serial_composition value =
324 [ bgroup
343 [ bgroup
325 "composition"
344 "composition"
345+ [ benchIOSink value "filter even" $ filter value
346+ , benchIOSink value "foldFilter even" $ foldFilter value
347+ , benchIOSink value "foldFilter even, odd" $ foldFilter2 value
326− [ benchIOSink value "serialWith (all, any)" $ splitAllAny value
348+ , benchIOSink value "serialWith (all, any)" $ splitAllAny value
327 , benchIOSink value "serial_ (all, any)" $ serial_ value
349 , benchIOSink value "serial_ (all, any)" $ serial_ value
328 , benchIOSink value "tee (all, any)" $ teeAllAny value
350 , benchIOSink value "tee (all, any)" $ teeAllAny value
329 , benchIOSink value "many drain (take 1)" many
351 , benchIOSink value "many drain (take 1)" many
core/src/Streamly/Internal/Data/Fold.hs+38
@@ -132,6 +132,8 @@ module Streamly.Internal.Data.Fold
132 -- ** Filtering
132 -- ** Filtering
133 , filter
133 , filter
134 , filterM
134 , filterM
135+ , foldFilter
136+ , satisfy
135 , sampleFromthen
137 , sampleFromthen
136 -- , ldeleteBy
138 -- , ldeleteBy
137 -- , luniq
139 -- , luniq
@@ -477,6 +479,42 @@ postscan (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
477
479
478 extract = extractR . snd
480 extract = extractR . snd
479
481
482+------------------------------------------------------------------------------
483+-- Filters
484+------------------------------------------------------------------------------
485+
486+-- | Convert a predicate into a filtering fold.
487+--
488+-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
489+-- >>> Stream.fold f $ Stream.fromList [1..10]
490+-- 40
491+--
492+-- /Pre-release/
493+{-# INLINE satisfy #-}
494+satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
495+satisfy f = Fold step (return $ Partial ()) (const (return Nothing))
496+
497+ where
498+
499+ step () a = return $ Done $ if f a then Just a else Nothing
500+
501+-- | Use a 'Maybe' returning fold as a filtering scan.
502+--
503+-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
504+-- >>> Stream.fold f $ Stream.fromList [1..10]
505+-- 40
506+--
507+-- The above snippet is equivalent to:
508+--
509+-- >>> f = Fold.filter (> 5) Fold.sum
510+-- >>> Stream.fold f $ Stream.fromList [1..10]
511+-- 40
512+--
513+-- /Pre-release/
514+{-# INLINE foldFilter #-}
515+foldFilter :: Monad m => Fold m a (Maybe b) -> Fold m b c -> Fold m a c
516+foldFilter f1 f2 = many f1 (catMaybes f2)
517+
480------------------------------------------------------------------------------
518------------------------------------------------------------------------------
481-- Left folds
519-- Left folds
482------------------------------------------------------------------------------
520------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Fold/Type.hs+2−1
@@ -393,6 +393,7 @@ module Streamly.Internal.Data.Fold.Type
393 )
393 )
394where
394where
395
395
396+#include "inline.hs"
396import Control.Monad ((>=>))
397import Control.Monad ((>=>))
397import Data.Bifunctor (Bifunctor(..))
398import Data.Bifunctor (Bifunctor(..))
398import Data.Maybe (isJust, fromJust)
399import Data.Maybe (isJust, fromJust)
@@ -1167,7 +1168,7 @@ filterM f (Fold step begin done) = Fold step' begin done
1167-- and sent to the original fold, 'Nothing' values are discarded.
1168-- and sent to the original fold, 'Nothing' values are discarded.
1168--
1169--
1169-- @since 0.8.0
1170-- @since 0.8.0
1170−{-# INLINE catMaybes #-}
1171+{-# INLINE_NORMAL catMaybes #-}
1171catMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b
1172catMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b
1172catMaybes = filter isJust . lmap fromJust
1173catMaybes = filter isJust . lmap fromJust
1173
1174