Skip to content
Open
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
13 changes: 0 additions & 13 deletions benchmark/Streamly/Benchmark/Data/Scanl/Combinators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ import qualified Streamly.Internal.Data.Fold as FL
import qualified Streamly.Internal.Data.Pipe as Pipe
import qualified Streamly.Internal.Data.Scanl as Scanl
import qualified Streamly.Internal.Data.Stream as Stream
import qualified Streamly.Internal.Data.Unfold as Unfold

import Streamly.Benchmark.Common
import Test.Tasty.Bench
Expand Down Expand Up @@ -562,17 +561,6 @@ inspect $ 'partition `hasNoType` ''FL.Step
inspect $ 'partition `hasNoType` ''SPEC
#endif

-------------------------------------------------------------------------------
-- Nesting
-------------------------------------------------------------------------------

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

-------------------------------------------------------------------------------
-- O(n) heap: building structures
-------------------------------------------------------------------------------
Expand Down Expand Up @@ -662,7 +650,6 @@ benchmarks value =
, benchIO "partitionByM (sum, length)" partitionByM value
, benchIO "partitionBy (sum, length)" partitionBy value
, benchIO "partition (sum, length)" partition value
, benchIO "unfoldEach" unfoldEach value
]
++ fmap (HeapO_n,)
[ benchIO "toListRev" toListRev value
Expand Down
4 changes: 2 additions & 2 deletions benchmark/Streamly/Benchmark/Data/Scanl/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ inspect $ 'scanl1M' `hasNoType` ''SPEC

{-# INLINE scant' #-}
scant' :: Int -> IO ()
scant' n = withPostscanl n (Scanl.scant' (\s a -> Scanl.Partial (s + a)) (Scanl.Partial 0) id)
scant' n = withPostscanl n (Scanl.scant' (\s a -> Scanl.Partial (s + a)) (FL.Partial 0) id)

#ifdef INSPECTION
inspect $ 'scant' `hasNoType` ''Step
Expand All @@ -138,7 +138,7 @@ scantM' n =
withPostscanl n
(Scanl.scantM'
(\s a -> return (Scanl.Partial (s + a)))
(return (Scanl.Partial 0))
(return (FL.Partial 0))
return)

#ifdef INSPECTION
Expand Down
30 changes: 26 additions & 4 deletions core/docs/Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,41 @@

## Unreleased

### Enhancements

* Filtering support for `Scanl`.
* FileSystem.Path now does not treat paths with leading "./" as rooted paths.

### Bug Fixes

* Fixed `Stream.postscanl` to omit the output of a scan that terminates without
consuming any input (e.g. `Scanl.take 0`).
* Breaking: In `FileSystem.Path` module the default for `eqPath` changed

### Breaking changes

* In Scanl module, `filter` and any other filtering operations like
`filterM`, `catMaybes`, `catLefts` etc. used to emit the previous
value of accumulator for filtered-out elements, now they do not emit
any output for those.
* `Scanl.incrScanWith` now provides the ring array reflecting the
state of the window /before/ the incoming element is inserted.
* In `FileSystem.Path` module the default for `eqPath` changed
on Windows to case-sensitive comparison.
* Breaking: A leading "." component (e.g. "." or "./x") is no longer
* A leading "." component (e.g. "." or "./x") is no longer
treated as a rooted path, making the behavior more in line with
intuitive expectation.
* Breaking: In `FileSystem.Path` module the default for `eqPath` changed
* In `FileSystem.Path` module the default for `eqPath` changed
on both Posix and Windows so that `allowRelativeEquality` is `True` by
default. Literally identical relative paths (e.g. `./x` and `./x`, or
`c:` and `c:` on Windows) now compare equal. Pass
`allowRelativeEquality False` to restore the previous strict behaviour.
* Internal: Removed deprecated module `Streamly.Internal.Data.Stream.StreamD`.

### Internal changes

* `Scanl`'s Step type is now different from Fold's `Step` type
with an additional `Continue` constructor, this change enables proper
filtering in scans.
* Removed deprecated module `Streamly.Internal.Data.Stream.StreamD`.
Use `Streamly.Internal.Data.Stream` instead.

## 0.3.1 (May 2026)
Expand Down
2 changes: 1 addition & 1 deletion core/src/Streamly/Internal/Data/Fold/Combinators.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1788,7 +1788,7 @@ distributeScan getFolds = Scanl consume initial extract final

initial = return $ Partial (Tuple' [] [])

run st [] _ = return $ Partial st
run st [] _ = return $ Scanl.Partial st
run (Tuple' ys zs) (Fold step init extr fin : xs) a = do
res <- init
case res of
Expand Down
25 changes: 21 additions & 4 deletions core/src/Streamly/Internal/Data/Fold/Container.hs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))
import qualified Data.Set as Set
import qualified Streamly.Internal.Data.IsMap as IsMap
import qualified Streamly.Internal.Data.Scanl.Container as Scanl
import qualified Streamly.Internal.Data.Scanl.Type as Scanl

import Prelude hiding (Foldable(..))
import Streamly.Internal.Data.Fold.Type
Expand Down Expand Up @@ -395,7 +396,11 @@ demuxScanGeneric :: (Monad m, IsMap f, Traversable f) =>
-> (Key f -> m (Maybe (Fold m a b)))
-> Scanl m a (m (f b), Maybe (Key f, b))
demuxScanGeneric getKey getFold =
Scanl (\s a -> Partial <$> step s a) (Partial <$> initial) extract final
Scanl
(\s a -> Scanl.Partial <$> step s a)
(Partial <$> initial)
extract
final

where

Expand Down Expand Up @@ -666,7 +671,11 @@ demuxScanGenericIO :: (MonadIO m, IsMap f, Traversable f) =>
-> (Key f -> m (Maybe (Fold m a b)))
-> Scanl m a (m (f b), Maybe (Key f, b))
demuxScanGenericIO getKey getFold =
Scanl (\s a -> Partial <$> step s a) (Partial <$> initial) extract final
Scanl
(\s a -> Scanl.Partial <$> step s a)
(Partial <$> initial)
extract
final

where

Expand Down Expand Up @@ -1017,7 +1026,11 @@ classifyScanGeneric :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
-- that the downstream consumers can choose to process or discard it.
(a -> Key f) -> Fold m a b -> Scanl m a (m (f b), Maybe (Key f, b))
classifyScanGeneric f (Fold step1 initial1 extract1 final1) =
Scanl (\s a -> Partial <$> step s a) (Partial <$> initial) extract final
Scanl
(\s a -> Scanl.Partial <$> step s a)
(Partial <$> initial)
extract
final

where

Expand Down Expand Up @@ -1223,7 +1236,11 @@ toContainerIO f (Fold step1 initial1 _ final1) =
classifyScanGenericIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>
(a -> Key f) -> Fold m a b -> Scanl m a (m (f b), Maybe (Key f, b))
classifyScanGenericIO f (Fold step1 initial1 extract1 final1) =
Scanl (\s a -> Partial <$> step s a) (Partial <$> initial) extract final
Scanl
(\s a -> Scanl.Partial <$> step s a)
(Partial <$> initial)
extract
final

where

Expand Down
46 changes: 37 additions & 9 deletions core/src/Streamly/Internal/Data/Fold/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,7 @@ module Streamly.Internal.Data.Fold.Type
-- XXX Do refold ops belong to Scanl or Fold?
, fromRefold
, fromScanl
, fromFold
, drain
, toList
, toListRev
Expand Down Expand Up @@ -625,8 +626,26 @@ rmapM f (Fold step initial extract final) =

-- | Convert a left scan to a fold.
{-# INLINE fromScanl #-}
fromScanl :: Scanl m a b -> Fold m a b
fromScanl (Scanl step initial extract final) = Fold step initial extract final
fromScanl :: Functor m => Scanl m a b -> Fold m a b
fromScanl (Scanl step initial extract final) =
Fold (\s a -> fmap Scanl.toFoldStep (step s a))
initial
extract
final

-- XXX This belongs in a Scanl module. It lives here for now only because
-- 'Fold.Type' imports 'Scanl.Type' (Folds are built on top of scans); move it
-- once that dependency is inverted. We should move fromScanl as well to the
-- Scanl module and rename it to "toFold".

-- | Convert a fold to a left scan.
{-# INLINE fromFold #-}
fromFold :: Functor m => Fold m a b -> Scanl m a b
fromFold (Fold step initial extract final) =
Scanl (\s a -> fmap Scanl.fromFoldStep (step s a))
initial
extract
final

-- | Make a fold from a left fold style pure step function and initial value of
-- the accumulator.
Expand Down Expand Up @@ -793,7 +812,12 @@ foldrM' g = fromScanl . Scanl.mkScanrM g
--
{-# INLINE foldt' #-}
foldt' :: Monad m => (s -> a -> Step s b) -> Step s b -> (s -> b) -> Fold m a b
foldt' step initial = fromScanl . Scanl.scant' step initial
foldt' step initial extract =
Fold
(\s a -> pure $ step s a)
(pure initial)
(pure . extract)
(pure . extract)

-- | Make a terminating fold with an effectful step function and initial state,
-- and a state extraction function.
Expand Down Expand Up @@ -1595,17 +1619,18 @@ postscanl
runStep actionL sR = do
rL <- actionL
case rL of
Done bL -> do
Scanl.Done bL -> do
rR <- stepR sR bL
case rR of
Partial sR1 -> Done <$> finalR sR1
Done bR -> return $ Done bR
Partial sL -> do
Scanl.Partial sL -> do
!b <- extractL sL
rR <- stepR sR b
case rR of
Partial sR1 -> return $ Partial (sL, sR1)
Done bR -> finalL sL >> return (Done bR)
Scanl.Continue sL -> return $ Partial (sL, sR)

initial = do
rR <- initialR
Expand Down Expand Up @@ -1704,11 +1729,13 @@ scanlWith isMany

where

initialL_ = Scanl.fromFoldStep <$> initialL

{-# INLINE runStep #-}
runStep actionL sR = do
rL <- actionL
case rL of
Done bL -> do
Scanl.Done bL -> do
rR <- stepR sR bL
case rR of
Partial sR1 ->
Expand All @@ -1717,20 +1744,21 @@ scanlWith isMany
-- will not terminate. In that case we should return
-- error in the beginning itself. And we should remove
-- this recursion, assuming it won't return Done.
then runStep initialL sR1
then runStep initialL_ sR1
else Done <$> finalR sR1
Done bR -> return $ Done bR
Partial sL -> do
Scanl.Partial sL -> do
!b <- extractL sL
rR <- stepR sR b
case rR of
Partial sR1 -> return $ Partial (sL, sR1)
Done bR -> finalL sL >> return (Done bR)
Scanl.Continue sL -> return $ Partial (sL, sR)

initial = do
r <- initialR
case r of
Partial sR -> runStep initialL sR
Partial sR -> runStep initialL_ sR
Done b -> return $ Done b

step (sL, sR) x = runStep (stepL sL x) sR
Expand Down
5 changes: 3 additions & 2 deletions core/src/Streamly/Internal/Data/MutArray/Type.hs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ import Streamly.Internal.Data.Unfold.Type (Unfold(..))
import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)

import qualified Streamly.Internal.Data.Fold.Type as FL
import qualified Streamly.Internal.Data.Scanl.Type as Scanl
import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed
import qualified Streamly.Internal.Data.Parser.Type as Parser
-- import qualified Streamly.Internal.Data.Fold.Type as Fold
Expand Down Expand Up @@ -4074,8 +4075,8 @@ scanCompactMinAs ps minElems =
runInner len buf =
if len >= minBytes
then do
return $ FL.Partial $ CompactMinComplete buf
else return $ FL.Partial $ CompactMinIncomplete buf
return $ Scanl.Partial $ CompactMinComplete buf
else return $ Scanl.Partial $ CompactMinIncomplete buf

step CompactMinInit arr =
runInner (byteLength arr) arr
Expand Down
4 changes: 2 additions & 2 deletions core/src/Streamly/Internal/Data/RingArray.hs
Original file line number Diff line number Diff line change
Expand Up @@ -531,12 +531,12 @@ scanRingsOf n = Scanl step initial extract extract
then error "scanRingsOf: window size must be > 0"
else do
mba <- liftIO $ MutByteArray.new rSize
return $ Partial $ Tuple3Fused' mba 0 0
return $ Fold.Partial $ Tuple3Fused' mba 0 0

step (Tuple3Fused' mba rh offset) a = do
RingArray _ _ rh1 <- replace_ (RingArray mba rSize rh) a
let offset1 = offset + SIZE_OF(a)
return $ Partial $ Tuple3Fused' mba rh1 offset1
return $ Scanl.Partial $ Tuple3Fused' mba rh1 offset1

-- XXX exitify optimization causes a problem here when modular folds are
-- used. Sometimes inlining "extract" is helpful.
Expand Down
Loading
Loading