@@ -36,6 +36,11 @@ module Streamly.Internal.Data.Fold
40+ -- | Monadic functions useful with mapM/lmapM on folds or streams.
39 -- * Folds
44 -- * Folds
40 -- ** Identity
45 -- ** Identity
41 , fromPure
46 , fromPure
@@ -51,7 +56,6 @@ module Streamly.Internal.Data.Fold
51 -- *** Reducers
56 -- *** Reducers
57 , genericLength
61 , genericLength
@@ -63,7 +67,6 @@ module Streamly.Internal.Data.Fold
63 , rollingHashWithSalt
67 , rollingHashWithSalt
64 , rollingHashFirstN
68 , rollingHashFirstN
65 -- , rollingHashLastN
69 -- , rollingHashLastN
68 -- *** Saturating Reducers
71 -- *** Saturating Reducers
69 -- | 'product' terminates if it becomes 0. Other folds can theoretically
72 -- | 'product' terminates if it becomes 0. Other folds can theoretically
@@ -86,15 +89,41 @@ module Streamly.Internal.Data.Fold
86 , toStream
89 , toStream
87 , toStreamRev
90 , toStreamRev
97+ -- | Stateful transformation of the elements. Useful in combination with
98+ -- the 'scanMaybe' combinator. For scanners the result of the fold is
99+ -- usually a transformation of the current element rather than an
100+ -- aggregation of all elements till now.
102+ -- , nthLast -- using Ring array
109+ -- | Useful in combination with the 'scanMaybe' combinator.
92 -- ** Terminating Folds
120 -- ** Terminating Folds
93 -- Element folds. Terminate after inspecting one element. All these can be
121 -- Element folds. Terminate after inspecting one element. All these can be
94 -- implemented in terms of the 'maybe' fold.
122 -- implemented in terms of the 'maybe' fold.
100 -- Sequence folds. Terminate after inspecting a sequence of elements.
129 -- Sequence folds. Terminate after inspecting a sequence of elements.
@@ -115,6 +144,18 @@ module Streamly.Internal.Data.Fold
148+ -- | Useful in combination with the 'scanMaybe' combinator.
118 -- * Combinators
159 -- * Combinators
119 -- ** Utilities
160 -- ** Utilities
@@ -131,42 +172,35 @@ module Streamly.Internal.Data.Fold
132 --, lsequence
173 --, lsequence
176+ -- ** Sliding Window
179+ -- ** Scanning Input
135 , scanMany
181 , scanMany
136 , postscan
182 , postscan
137 , indexed
183 , indexed
140 -- ** Zipping Input
185 -- ** Zipping Input
141 , zipWithM
186 , zipWithM
144− -- ** Filtering
189+ -- ** Filtering Input
146 , filterM
195 , filterM
149 , sampleFromthen
196 , sampleFromthen
155− -- ** Mapping Filters
160 -- Either streams
198 -- Either streams
165− -- ** Scanning Filters
170 -- ** Insertion
204 -- ** Insertion
171 -- | Insertion adds more elements to the stream.
205 -- | Insertion adds more elements to the stream.
@@ -304,10 +338,10 @@ import Data.Functor.Identity (Identity(..))
304import Data.Int (Int64)
338import Data.Int (Int64)
305import Data.IORef (newIORef, readIORef, writeIORef)
339import Data.IORef (newIORef, readIORef, writeIORef)
306import Data.Map.Strict (Map)
340import Data.Map.Strict (Map)
307−import Data.Maybe (isJust, fromJust)
308import Data.Word (Word32)
341import Data.Word (Word32)
309import Foreign.Storable (peek, sizeOf)
342import Foreign.Storable (peek, sizeOf)
310import Streamly.Internal.Data.IsMap (IsMap(..))
343import Streamly.Internal.Data.IsMap (IsMap(..))
344+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)
311import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
345import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))
312import Streamly.Internal.Data.Unboxed (Unboxed)
346import Streamly.Internal.Data.Unboxed (Unboxed)
313import Streamly.Internal.Data.Unfold.Type (Unfold(..))
347import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -339,17 +373,18 @@ import Streamly.Internal.Data.Fold.Type
340-- >>> :set -package streamly
374-- >>> :set -package streamly
341-- >>> import Prelude hiding (break, map, span, splitAt)
375-- >>> import Prelude hiding (break, map, span, splitAt)
342−-- >>> import qualified Streamly.Data.Stream as Stream
376+-- >>> import qualified Streamly.Data.Array.Unboxed as Array
343-- >>> import qualified Streamly.Data.Fold as Fold
377-- >>> import qualified Streamly.Data.Fold as Fold
378+-- >>> import qualified Streamly.Data.Stream as Stream
379+-- >>> import qualified Streamly.Internal.Data.Array.Unboxed.Mut.Type as MA
344-- >>> import qualified Streamly.Internal.Data.Fold as Fold
380-- >>> import qualified Streamly.Internal.Data.Fold as Fold
345-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
381-- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold
346-- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW
382-- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW
347−-- >>> import qualified Streamly.Data.Array.Unboxed as Array
348-- >>> import qualified Streamly.Internal.Data.Parser as Parser
383-- >>> import qualified Streamly.Internal.Data.Parser as Parser
349-- >>> import qualified Streamly.Internal.Data.Stream.Type as Stream
384-- >>> import qualified Streamly.Internal.Data.Stream.Type as Stream
385+-- >>> import qualified Streamly.Internal.Data.Unfold as Unfold
350-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
386-- >>> import Streamly.Internal.Data.Stream.Type (Stream)
351-- >>> import Data.IORef (newIORef, readIORef, writeIORef)
387-- >>> import Data.IORef (newIORef, readIORef, writeIORef)
352−-- >>> import qualified Streamly.Internal.Data.Array.Unboxed.Mut.Type as MA
354------------------------------------------------------------------------------
389------------------------------------------------------------------------------
@@ -390,10 +425,20 @@ sequence = rmapM id
390mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
425mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c
391mapM = rmapM
426mapM = rmapM
429+-- >>> mapMaybeM f = Fold.lmapM f . Fold.catMaybes
431+{-# INLINE mapMaybeM #-}
432+mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Fold m b r -> Fold m a r
433+mapMaybeM f = lmapM f . catMaybes
393-- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of
435-- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of
394-- the fold, filters out 'Nothing' elements, and return the values extracted
436-- the fold, filters out 'Nothing' elements, and return the values extracted
395-- from 'Just'.
437-- from 'Just'.
439+-- >>> mapMaybe f = Fold.lmap f . Fold.catMaybes
440+-- >>> mapMaybe f = Fold.mapMaybeM (return . f)
397-- >>> f x = if even x then Just x else Nothing
442-- >>> f x = if even x then Just x else Nothing
398-- >>> fld = Fold.mapMaybe f Fold.toList
443-- >>> fld = Fold.mapMaybe f Fold.toList
399-- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)
444-- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)
@@ -401,13 +446,25 @@ mapM = rmapM
402-- @since 0.8.0
447-- @since 0.8.0
403{-# INLINE mapMaybe #-}
448{-# INLINE mapMaybe #-}
404−mapMaybe :: (Monad m) => (a -> Maybe b) -> Fold m b r -> Fold m a r
449+mapMaybe :: Monad m => (a -> Maybe b) -> Fold m b r -> Fold m a r
405−mapMaybe f = lmap f . filter isJust . lmap fromJust
450+mapMaybe f = lmap f . catMaybes
407------------------------------------------------------------------------------
452------------------------------------------------------------------------------
408-- Transformations on fold inputs
453-- Transformations on fold inputs
409------------------------------------------------------------------------------
454------------------------------------------------------------------------------
456+-- | Apply a monadic function on the input and return the input.
458+-- >>> Stream.fold (Fold.lmapM (Fold.tracing print) Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)
464+{-# INLINE tracing #-}
465+tracing :: Monad m => (a -> m b) -> (a -> m a)
466+tracing f x = void (f x) >> return x
411-- | Apply a monadic function to each element flowing through and discard the
468-- | Apply a monadic function to each element flowing through and discard the
412-- results.
469-- results.
@@ -415,10 +472,12 @@ mapMaybe f = lmap f . filter isJust . lmap fromJust
475+-- >>> trace f = Fold.lmapM (Fold.tracing f)
418-- /Pre-release/
477-- /Pre-release/
419{-# INLINE trace #-}
478{-# INLINE trace #-}
420trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r
479trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r
421−trace f = lmapM (\x -> void (f x) >> return x)
480+trace f = lmapM (tracing f)
423-- rename to lpipe?
482-- rename to lpipe?
@@ -516,82 +575,110 @@ scan = scanWith False
516scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
575scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
517scanMany = scanWith True
576scanMany = scanWith True
519−-- | Postscan the input of a 'Fold' to change it in a stateful manner using
578+------------------------------------------------------------------------------
520−-- another 'Fold'.
579+-- Filters
521−-- /Pre-release/
580+------------------------------------------------------------------------------
522−{-# INLINE postscan #-}
581+
523−postscan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c
582+-- | A filtering scan that deletes the first occurrence of the element in the
524−postscan (Fold stepL initialL extractL) (Fold stepR initialR extractR) =
583+-- stream that satisfies the given equality predicate.
525− Fold step initial extract
584+--
585+-- >>> input = Stream.unfold Unfold.fromList [1,3,3,5]
586+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.deleteBy (==) 3) input
589+{-# INLINE_NORMAL deleteBy #-}
590+deleteBy :: Monad m => (a -> a -> Bool) -> a -> Fold m a (Maybe a)
591+deleteBy eq x0 = fmap extract $ foldl' step (Tuple' False Nothing)
529− {-# INLINE runStep #-}
595+ step (Tuple' False _) x =
530− runStep actionL sR = do
596+ if eq x x0
531− rL <- actionL
597+ then Tuple' True Nothing
532− case rL of
598+ else Tuple' False (Just x)
533− Done bL -> do
599+ step (Tuple' True _) x = Tuple' True (Just x)
536− Partial sR1 -> Done <$> extractR sR1
537− Done bR -> return $ Done bR
543− Partial sR1 -> Partial (sL, sR1)
546− initial = do
601+ extract (Tuple' _ x) = x
552− Done _ -> Done <$> extractR sR
553− Partial sL -> return $ Partial (sL, sR)
554− Done b -> return $ Done b
556− step (sL, sR) x = runStep (stepL sL x) sR
603+-- | Provide a sliding window of length 2 elements.
605+-- See "Streamly.Internal.Data.Fold.Window".
607+{-# INLINE slide2 #-}
608+slide2 :: Monad m => Fold m (a, Maybe a) b -> Fold m a b
609+slide2 (Fold step1 initial1 extract1) = Fold step initial extract
558− extract = extractR . snd
611+ where
560−------------------------------------------------------------------------------
613+ initial =
561−-- Filters
614+ first (Tuple' Nothing) <$> initial1
562−------------------------------------------------------------------------------
615+
616+ step (Tuple' prev s) cur =
617+ first (Tuple' (Just cur)) <$> step1 s (cur, prev)
619+ extract (Tuple' _ s) = extract1 s
564−-- | Convert a predicate into a filtering fold.
621+-- | Drop repeated elements that are adjacent to each other using the supplied
622+-- comparison function.
566−-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
624+-- To strip duplicate path separators:
626+-- >>> input = Stream.unfold Unfold.fromList "//a//b"
627+-- >>> f x y = x == '/' && y == '/'
567−-- >>> Stream.fold f $ Stream.fromList [1..10]
628+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.uniqBy f) input
633+-- See also: 'nubBy'.
570-- /Pre-release/
635-- /Pre-release/
571−{-# INLINE satisfy #-}
637+{-# INLINE uniqBy #-}
572−satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
638+uniqBy :: Monad m => (a -> a -> Bool) -> Fold m a (Maybe a)
573−satisfy f = Fold step (return $ Partial ()) (const (return Nothing))
639+uniqBy eq = rollingMap f
577− step () a = return $ Done $ if f a then Just a else Nothing
643+ f pre curr =
645+ Nothing -> Just curr
646+ Just x -> if x `eq` curr then Nothing else Just curr
579−-- | Use a 'Maybe' returning fold as a filtering scan.
648+-- | Drop repeated elements that are adjacent to each other.
581−-- >>> f = Fold.foldFilter (Fold.satisfy (> 5)) Fold.sum
650+-- >>> uniq = Fold.uniqBy (==)
582−-- >>> Stream.fold f $ Stream.fromList [1..10]
585−-- The above snippet is equivalent to:
652+{-# INLINE uniq #-}
653+uniq :: (Monad m, Eq a) => Fold m a (Maybe a)
656+-- | Strip all leading and trailing occurrences of an element passing a
657+-- predicate and make all other consecutive occurrences uniq.
587−-- >>> f = Fold.filter (> 5) Fold.sum
659+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)
588−-- >>> Stream.fold f $ Stream.fromList [1..10]
591−-- /Pre-release/
661+-- @
662+-- > Stream.prune isSpace (Stream.fromList " hello world! ")
592−{-# INLINE foldFilter #-}
670+{-# INLINE prune #-}
593−foldFilter :: Monad m => Fold m a (Maybe b) -> Fold m b c -> Fold m a c
671+prune ::
594−foldFilter f1 f2 = many f1 (catMaybes f2)
672+ -- (Monad m, Eq a) =>
673+ (a -> Bool) -> Fold m a (Maybe a)
674+prune = error "Not implemented yet!"
676+-- | Emit only repeated elements, once.
679+repeated :: -- (Monad m, Eq a) =>
681+repeated = error "Not implemented yet!"
596-- | Used as a scan. Returns 'Just' for the first occurrence of an element,
683-- | Used as a scan. Returns 'Just' for the first occurrence of an element,
597-- returns 'Nothing' for any other occurrences.
684-- returns 'Nothing' for any other occurrences.
@@ -960,6 +1047,9 @@ rollingHash = rollingHashWithSalt defaultSalt
960rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64
1047rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64
961rollingHashFirstN n = take n rollingHash
1048rollingHashFirstN n = take n rollingHash
1050+-- XXX Compare this with the implementation in Fold.Window, preferrably use the
1051+-- latter if performance is good.
963-- | Apply a function on every two successive elements of a stream. The first
1053-- | Apply a function on every two successive elements of a stream. The first
964-- argument of the map function is the previous element and the second argument
1054-- argument of the map function is the previous element and the second argument
965-- is the current element. When processing the very first element in the
1055-- is the current element. When processing the very first element in the
@@ -983,6 +1073,10 @@ rollingMapM f = Fold step initial extract
984 extract = return . snd
1074 extract = return . snd
1076+{-# INLINE rollingMap #-}
1077+rollingMap :: Monad m => (Maybe a -> a -> b) -> Fold m a b
1078+rollingMap f = rollingMapM (\x y -> return $ f x y)
986------------------------------------------------------------------------------
1080------------------------------------------------------------------------------
987-- Monoidal left folds
1081-- Monoidal left folds
988------------------------------------------------------------------------------
1082------------------------------------------------------------------------------
@@ -1122,6 +1216,17 @@ index = genericIndex
1122maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)
1216maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)
1123maybe f = foldt' (const (Done . f)) (Partial Nothing) id
1217maybe f = foldt' (const (Done . f)) (Partial Nothing) id
1219+-- | Test if the next element satisfies the supplied predicate.
1222+{-# INLINE satisfy #-}
1223+satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1224+satisfy f = Fold step (return $ Partial ()) (const (return Nothing))
1228+ step () a = return $ Done $ if f a then Just a else Nothing
1125-- Naming notes:
1230-- Naming notes:
1127-- "head" and "next" are two alternative names for the same API. head sounds
1232-- "head" and "next" are two alternative names for the same API. head sounds
@@ -1227,6 +1332,7 @@ findIndex predicate = foldt' step (Partial 0) (const Nothing)
1227{-# INLINE findIndices #-}
1332{-# INLINE findIndices #-}
1228findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
1333findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)
1229findIndices predicate =
1334findIndices predicate =
1335+ -- XXX implement by combining indexing and filtering scans
1230 fmap (either (const Nothing) Just) $ foldl' step (Left (-1))
1336 fmap (either (const Nothing) Just) $ foldl' step (Left (-1))
@@ -1236,6 +1342,15 @@ findIndices predicate =
1236 then Right (either id id i + 1)
1342 then Right (either id id i + 1)
1237 else Left (either id id i + 1)
1343 else Left (either id id i + 1)
1345+-- | Find all the indices where the value of the element in the stream is equal
1346+-- to the given value.
1348+-- >>> elemIndices a = Fold.findIndices (== a)
1350+{-# INLINE elemIndices #-}
1351+elemIndices :: (Monad m, Eq a) => a -> Fold m a (Maybe Int)
1352+elemIndices a = findIndices (== a)
1239-- | Returns the first index where a given value is found in the stream.
1354-- | Returns the first index where a given value is found in the stream.
1241-- > elemIndex a = Fold.findIndex (== a)
1356-- > elemIndex a = Fold.findIndex (== a)
@@ -1393,6 +1508,73 @@ splitAt n fld = serialWith (,) (take n fld)
1393-- Binary APIs
1508-- Binary APIs
1394------------------------------------------------------------------------------
1509------------------------------------------------------------------------------
1511+{-# INLINE takingEndByM #-}
1512+takingEndByM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1513+takingEndByM p = Fold step initial (return . toMaybe)
1517+ initial = return $ Partial Nothing'
1524+ else Partial $ Just' a
1528+-- >>> takingEndBy p = Fold.takingEndByM (return . p)
1530+{-# INLINE takingEndBy #-}
1531+takingEndBy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1532+takingEndBy p = takingEndByM (return . p)
1534+{-# INLINE takingEndByM_ #-}
1535+takingEndByM_ :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1536+takingEndByM_ p = Fold step initial (return . toMaybe)
1540+ initial = return $ Partial Nothing'
1547+ else Partial $ Just' a
1551+-- >>> takingEndBy_ p = Fold.takingEndByM_ (return . p)
1553+{-# INLINE takingEndBy_ #-}
1554+takingEndBy_ :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1555+takingEndBy_ p = takingEndByM_ (return . p)
1557+{-# INLINE droppingWhileM #-}
1558+droppingWhileM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)
1559+droppingWhileM p = Fold step initial (return . toMaybe)
1563+ initial = return $ Partial Nothing'
1565+ step Nothing' a = do
1572+ step _ a = return $ Partial $ Just' a
1574+{-# INLINE droppingWhile #-}
1575+droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)
1576+droppingWhile p = droppingWhileM (return . p)
1396-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1578-- Note: Keep this consistent with S.splitOn. In fact we should eliminate
1397-- S.splitOn in favor of the fold.
1579-- S.splitOn in favor of the fold.
@@ -1414,6 +1596,7 @@ splitAt n fld = serialWith (,) (take n fld)
1414-- @since 0.8.0
1596-- @since 0.8.0
1415{-# INLINE takeEndBy_ #-}
1597{-# INLINE takeEndBy_ #-}
1416takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1598takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1599+-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)
1417takeEndBy_ predicate (Fold fstep finitial fextract) =
1600takeEndBy_ predicate (Fold fstep finitial fextract) =
1418 Fold step finitial fextract
1601 Fold step finitial fextract
@@ -1441,6 +1624,7 @@ takeEndBy_ predicate (Fold fstep finitial fextract) =
1441-- @since 0.8.0
1624-- @since 0.8.0
1442{-# INLINE takeEndBy #-}
1625{-# INLINE takeEndBy #-}
1443takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1626takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b
1627+-- takeEndBy predicate = scanMaybe (takingEndBy predicate)
1444takeEndBy predicate (Fold fstep finitial fextract) =
1628takeEndBy predicate (Fold fstep finitial fextract) =
1445 Fold step finitial fextract
1629 Fold step finitial fextract
@@ -2469,11 +2653,30 @@ zip = zipWithM (curry return)
2470-- | Pair each element of a fold input with its index, starting from index 0.
2654-- | Pair each element of a fold input with its index, starting from index 0.
2472−-- /Unimplemented/
2656+{-# INLINE indexingWith #-}
2657+indexingWith :: Monad m => Int -> (Int -> Int) -> Fold m a (Maybe (Int, a))
2658+indexingWith i f = fmap toMaybe $ foldl' step initial
2664+ step Nothing' a = Just' (i, a)
2665+ step (Just' (n, _)) a = Just' (f n, a)
2667+{-# INLINE indexing #-}
2668+indexing :: Monad m => Fold m a (Maybe (Int, a))
2669+indexing = indexingWith 0 (+ 1)
2671+{-# INLINE indexingRev #-}
2672+indexingRev :: Monad m => Int -> Fold m a (Maybe (Int, a))
2673+indexingRev n = indexingWith n (subtract 1)
2675+-- | Pair each element of a fold input with its index, starting from index 0.
2473{-# INLINE indexed #-}
2677{-# INLINE indexed #-}
2474−indexed :: -- forall m a b. Monad m =>
2475− Fold m (Int, a) b -> Fold m a b
2678+indexed :: Monad m => Fold m (Int, a) b -> Fold m a b
2476−indexed = undefined -- zip (Stream.enumerateFrom 0 :: Stream m Int)
2679+indexed = scanMaybe indexing
2478-- | Change the predicate function of a Fold from @a -> b@ to accept an
2681-- | Change the predicate function of a Fold from @a -> b@ to accept an
2479-- additional state input @(s, a) -> b@. Convenient to filter with an
2682-- additional state input @(s, a) -> b@. Convenient to filter with an
@@ -2493,10 +2696,12 @@ with ::
2493 -> (((s, a) -> c) -> Fold m a b -> Fold m a b)
2696 -> (((s, a) -> c) -> Fold m a b -> Fold m a b)
2494with f comb g = f . comb g . lmap snd
2697with f comb g = f . comb g . lmap snd
2699+-- XXX Implement as a filter
2700+-- sampleFromthen :: Monad m => Int -> Int -> Fold m a (Maybe a)
2496-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
2702-- | @sampleFromthen offset stride@ samples the element at @offset@ index and
2497-- then every element at strides of @stride@.
2703-- then every element at strides of @stride@.
2500{-# INLINE sampleFromthen #-}
2705{-# INLINE sampleFromthen #-}
2501sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b
2706sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b
2502sampleFromthen offset size =
2707sampleFromthen offset size =