@@ -202,6 +202,7 @@ module Streamly.Internal.Prelude
202202 -- ** Left scans
203203 , scanl'
204204 , scanlM'
205+ , scanlMAfter'
205206 , postscanl'
206207 , postscanlM'
207208 , prescanl'
@@ -2125,6 +2126,23 @@ scanx = P.scanlx'
21252126scanlM' :: (IsStream t, Monad m) => (b -> a -> m b) -> b -> t m a -> t m b
21262127scanlM' step begin m = fromStreamD $ D.scanlM' step begin $ toStreamD m
21272128
2129+-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except
2130+-- that it provides an additional @done@ function to be applied on the
2131+-- accumulator when the stream stops. The result of @done@ is also emitted in
2132+-- the stream.
2133+--
2134+-- This function can be used to allocate a resource in the beginning of the
2135+-- scan and release it when the stream ends or to flush the internal state of
2136+-- the scan at the end.
2137+--
2138+-- /Internal/
2139+--
2140+{-# INLINE scanlMAfter' #-}
2141+scanlMAfter' :: (IsStream t, Monad m)
2142+ => (b -> a -> m b) -> m b -> (b -> m b) -> t m a -> t m b
2143+scanlMAfter' step initial done stream =
2144+ fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream
2145+
21282146-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,
21292147-- however it adds an extra element.
21302148--
@@ -4507,8 +4525,8 @@ classifySessionsBy
45074525 -> t m (k, b) -- ^ session key, fold result
45084526classifySessionsBy tick tmout reset ejectPred
45094527 (Fold step initial extract) str =
4510− concatMap sessionOutputStream
4511− $ scanlM' sstep szero stream
4528+ concatMap sessionOutputStream $
4529+ scanlMAfter' sstep (return szero) flush stream
45124530
45134531 where
45144532
@@ -4613,6 +4631,21 @@ classifySessionsBy tick tmout reset ejectPred
46134631 let curTime = addToAbsTime sessionCurTime tickMs
46144632 in ejectExpired sessionState curTime
46154633
4634+ flush session@SessionState{..} = do
4635+ (hp', mp', out, count) <-
4636+ ejectAll
4637+ ( sessionTimerHeap
4638+ , sessionKeyValueMap
4639+ , K.nil
4640+ , sessionCount
4641+ )
4642+ return $ session
4643+ { sessionCount = count
4644+ , sessionTimerHeap = hp'
4645+ , sessionKeyValueMap = mp'
4646+ , sessionOutputStream = out
4647+ }
4648+
46164649 fromEither e =
46174650 case e of
46184651 Left x -> x
@@ -4625,6 +4658,18 @@ classifySessionsBy tick tmout reset ejectPred
46254658 let mp1 = Map.delete key mp
46264659 return (hp, mp1, out1, cnt - 1)
46274660
4661+ ejectAll (hp, mp, out, !cnt) = do
4662+ let hres = H.uncons hp
4663+ case hres of
4664+ Just (Entry _ key, hp1) -> do
4665+ r <- case Map.lookup key mp of
4666+ Nothing -> return (hp1, mp, out, cnt)
4667+ Just (Tuple' _ acc) -> ejectEntry hp1 mp out cnt acc key
4668+ ejectAll r
4669+ Nothing -> do
4670+ assert (Map.null mp) (return ())
4671+ return (hp, mp, out, cnt)
4672+
46284673 ejectOne (hp, mp, out, !cnt) = do
46294674 let hres = H.uncons hp
46304675 case hres of