From c420344c3188cf44416061901354a267606d257e Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 17:18:09 +0530 Subject: [PATCH 01/20] Make Unfold inject pure Change Unfold's inject from (a -> m s) to (a -> s). Improves fusion, Stream.unfold no longer needs a PreInit state, eliminating one state constructor and one Skip on the first pull for unfolds with a pure inject. Also fixes a latent bug in Unfold.before where `action >> inject` resolved through the Reader (((->) a)) monad's >> and silently dropped the action. --- .hlint.yaml | 1 + core/docs/Changelog.md | 20 ++ core/src/Streamly/Internal/Data/Array/Type.hs | 2 +- .../Internal/Data/Fold/Combinators.hs | 2 +- .../Streamly/Internal/Data/MutArray/Type.hs | 4 +- core/src/Streamly/Internal/Data/Producer.hs | 6 +- core/src/Streamly/Internal/Data/RingArray.hs | 4 +- .../Internal/Data/Scanl/Combinators.hs | 2 +- .../Streamly/Internal/Data/Stream/Nesting.hs | 198 +++++++++--------- .../src/Streamly/Internal/Data/Stream/Type.hs | 121 +++++------ core/src/Streamly/Internal/Data/Unfold.hs | 105 ++++------ .../Internal/Data/Unfold/Enumeration.hs | 4 +- .../src/Streamly/Internal/Data/Unfold/Type.hs | 128 ++++++----- .../src/Streamly/Internal/FileSystem/DirIO.hs | 2 +- .../Streamly/Internal/FileSystem/Handle.hs | 4 +- .../Internal/Syscall/Posix/ReadDir.hsc | 2 +- .../Internal/Syscall/Windows/ReadDir.hsc | 2 +- core/src/Streamly/Internal/Unicode/Encode.hs | 4 +- src/Streamly/Internal/Data/SmallArray.hs | 2 +- .../Internal/Data/Unfold/Exception.hs | 22 +- src/Streamly/Internal/Data/Unfold/SVar.hs | 4 +- src/Streamly/Internal/Network/Socket.hs | 6 +- 22 files changed, 309 insertions(+), 336 deletions(-) diff --git a/.hlint.yaml b/.hlint.yaml index 4e5ca270de..a7633e77a9 100644 --- a/.hlint.yaml +++ b/.hlint.yaml @@ -48,6 +48,7 @@ [ --cpp-include=src , --cpp-include=src/Streamly/Internal/Data/Stream , --cpp-include=core/src/Streamly/Internal/Data/Array + , --cpp-include=core/src/doctest , --cpp-include=test , --cpp-define=CABAL_OS_LINUX , --cpp-define=linux_HOST_OS diff --git a/core/docs/Changelog.md b/core/docs/Changelog.md index 5ff672c43e..08c4f8d280 100644 --- a/core/docs/Changelog.md +++ b/core/docs/Changelog.md @@ -2,6 +2,8 @@ ## Unreleased +### Breaking + * Breaking: 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 @@ -12,7 +14,25 @@ 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. + +### Enhancements + +* `Streamly.Data.Stream.unfold` no longer requires an `Applicative` constraint + and produces a smaller fused loop (one fewer state constructor and Skip on + the first pull) for unfolds with a pure inject. + +### Bug Fixes + * Bug fix: Fixed `followSymlinks` option not working correctly on macOS. +* `Streamly.Internal.Data.Unfold.before` previously did not actually run the + supplied action due to the wrong `>>` instance being resolved; it now runs + as documented. + +### Internal + +* Unfold constructor's inject changed from monadic to pure. Public APIs + unaffected. Direct users of the `Unfold` constructor with a monadic + inject should switch to `mkUnfoldM` in `Streamly.Internal.Data.Unfold.Type`. ## 0.3.0 diff --git a/core/src/Streamly/Internal/Data/Array/Type.hs b/core/src/Streamly/Internal/Data/Array/Type.hs index df6dbc89c2..d08b74b58d 100644 --- a/core/src/Streamly/Internal/Data/Array/Type.hs +++ b/core/src/Streamly/Internal/Data/Array/Type.hs @@ -1016,7 +1016,7 @@ unsafeReader = Unfold step inject where inject (Array contents start end) = - return (MA.ArrayUnsafe contents end start) + MA.ArrayUnsafe contents end start {-# INLINE_LATE step #-} step (MA.ArrayUnsafe contents end p) = do diff --git a/core/src/Streamly/Internal/Data/Fold/Combinators.hs b/core/src/Streamly/Internal/Data/Fold/Combinators.hs index 8bb6c5b15e..afeed4bb26 100644 --- a/core/src/Streamly/Internal/Data/Fold/Combinators.hs +++ b/core/src/Streamly/Internal/Data/Fold/Combinators.hs @@ -2224,7 +2224,7 @@ unfoldMany (Unfold ustep inject) (Fold fstep initial extract final) = StreamD.Stop -> return $ Partial fs {-# INLINE_LATE consume #-} - consume s a = inject a >>= produce s + consume s a = produce s (inject a) -- | Get the bottom most @n@ elements using the supplied comparison function. -- diff --git a/core/src/Streamly/Internal/Data/MutArray/Type.hs b/core/src/Streamly/Internal/Data/MutArray/Type.hs index fe2e8b18a9..ddfc5b2575 100644 --- a/core/src/Streamly/Internal/Data/MutArray/Type.hs +++ b/core/src/Streamly/Internal/Data/MutArray/Type.hs @@ -1628,7 +1628,7 @@ indexReaderWith liftio (D.Stream stepi sti) = Unfold step inject where inject (MutArray contents start end _) = - return $ GetIndicesState contents start end sti + GetIndicesState contents start end sti {-# INLINE_LATE step #-} step (GetIndicesState contents start end st) = do @@ -2215,7 +2215,7 @@ readerRevWith liftio = Unfold step inject inject (MutArray contents start end _) = let p = INDEX_PREV(end,a) - in return $ ArrayUnsafe contents start p + in ArrayUnsafe contents start p {-# INLINE_LATE step #-} step (ArrayUnsafe _ start p) | p < start = return D.Stop diff --git a/core/src/Streamly/Internal/Data/Producer.hs b/core/src/Streamly/Internal/Data/Producer.hs index 87c3f14c34..928323837a 100644 --- a/core/src/Streamly/Internal/Data/Producer.hs +++ b/core/src/Streamly/Internal/Data/Producer.hs @@ -39,7 +39,7 @@ where import Streamly.Internal.Data.Stream.Step (Step(..)) import Streamly.Internal.Data.Stream.Type (Stream(..)) import Streamly.Internal.Data.SVar.Type (defState) -import Streamly.Internal.Data.Unfold.Type (Unfold(..)) +import Streamly.Internal.Data.Unfold.Type (Unfold, mkUnfoldM) import Streamly.Internal.Data.Producer.Source import Streamly.Internal.Data.Producer.Type @@ -56,8 +56,8 @@ import Prelude hiding (concat) -- -- /Pre-release/ {-# INLINE simplify #-} -simplify :: Producer m a b -> Unfold m a b -simplify (Producer step inject _) = Unfold step inject +simplify :: Functor m => Producer m a b -> Unfold m a b +simplify (Producer step inject _) = mkUnfoldM step inject ------------------------------------------------------------------------------- -- Unfolds diff --git a/core/src/Streamly/Internal/Data/RingArray.hs b/core/src/Streamly/Internal/Data/RingArray.hs index dac99a92be..c74d7aa925 100644 --- a/core/src/Streamly/Internal/Data/RingArray.hs +++ b/core/src/Streamly/Internal/Data/RingArray.hs @@ -465,7 +465,7 @@ reader = Unfold step inject where - inject rb = return (rb, ringSize rb) + inject rb = (rb, ringSize rb) step (rb, n) = do if n <= 0 @@ -483,7 +483,7 @@ readerRev = Unfold step inject where - inject rb = return (moveReverse rb, ringSize rb) + inject rb = (moveReverse rb, ringSize rb) step (rb, n) = do if n <= 0 diff --git a/core/src/Streamly/Internal/Data/Scanl/Combinators.hs b/core/src/Streamly/Internal/Data/Scanl/Combinators.hs index 64f3ec91c5..ce01efcf8d 100644 --- a/core/src/Streamly/Internal/Data/Scanl/Combinators.hs +++ b/core/src/Streamly/Internal/Data/Scanl/Combinators.hs @@ -2207,7 +2207,7 @@ unfoldMany (Unfold ustep inject) (Scanl fstep initial extract final) = StreamD.Stop -> return $ Partial fs {-# INLINE_LATE consume #-} - consume s a = inject a >>= produce s + consume s a = produce s (inject a) -- | Get the bottom most @n@ elements using the supplied comparison function. -- diff --git a/core/src/Streamly/Internal/Data/Stream/Nesting.hs b/core/src/Streamly/Internal/Data/Stream/Nesting.hs index d7d70156e8..d73b64191b 100644 --- a/core/src/Streamly/Internal/Data/Stream/Nesting.hs +++ b/core/src/Streamly/Internal/Data/Stream/Nesting.hs @@ -296,7 +296,7 @@ appendUnfoldLast (Unfold ustep inject) (Stream ostep ost) = ) <$> ostep (adaptState gst) o step _ (AppendUnfoldLastInject lst) = - Skip . AppendUnfoldLastOutput <$> inject lst + pure (Skip (AppendUnfoldLastOutput (inject lst))) step _ (AppendUnfoldLastOutput i) = (\case @@ -874,10 +874,10 @@ unfoldFirst (Unfold ustep inject) (Stream ostep ost) = ) <$> ostep (adaptState gst) o step _ UnfoldFirstInjectEmpty = - Skip . UnfoldFirstOutputEmpty <$> inject Nothing + pure (Skip (UnfoldFirstOutputEmpty (inject Nothing))) step _ (UnfoldFirstInjectSome x o) = - (\i -> Skip (UnfoldFirstOutputSome i o)) <$> inject (Just x) + pure (Skip (UnfoldFirstOutputSome (inject (Just x)) o)) step _ (UnfoldFirstOutputEmpty i) = (\case @@ -943,7 +943,7 @@ unfoldLast (Unfold ustep inject) (Stream ostep ost) = ) <$> ostep (adaptState gst) o step _ (UnfoldLastInject lst) = - Skip . UnfoldLastOutput <$> inject lst + pure (Skip (UnfoldLastOutput (inject lst))) step _ (UnfoldLastOutput i) = (\case @@ -1122,12 +1122,12 @@ bfsUnfoldEach (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (BfsUnfoldEachOuter o ls) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (BfsUnfoldEachOuter o' (ls . (i :)))) - Skip o' -> return $ Skip (BfsUnfoldEachOuter o' ls) - Stop -> return $ Skip (BfsUnfoldEachInner (ls []) id) + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (BfsUnfoldEachOuter o1 (ls . (i :))) + Skip o1 -> Skip (BfsUnfoldEachOuter o1 ls) + Stop -> Skip (BfsUnfoldEachInner (ls []) id) step _ (BfsUnfoldEachInner [] rs) = case rs [] of @@ -1169,12 +1169,12 @@ altBfsUnfoldEach (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (ConcatUnfoldInterleaveOuter o ls) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (ConcatUnfoldInterleaveInner o' (i : ls))) - Skip o' -> return $ Skip (ConcatUnfoldInterleaveOuter o' ls) - Stop -> return $ Skip (ConcatUnfoldInterleaveInnerL ls []) + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (ConcatUnfoldInterleaveInner o1 (i : ls)) + Skip o1 -> Skip (ConcatUnfoldInterleaveOuter o1 ls) + Stop -> Skip (ConcatUnfoldInterleaveInnerL ls []) step _ (ConcatUnfoldInterleaveInner _ []) = undefined step _ (ConcatUnfoldInterleaveInner o (st:ls)) = do @@ -1240,12 +1240,12 @@ unfoldSched (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (BfsUnfoldEachOuter o ls) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (BfsUnfoldEachOuter o' (ls . (i :)))) - Skip o' -> return $ Skip (BfsUnfoldEachOuter o' ls) - Stop -> return $ Skip (BfsUnfoldEachInner (ls []) id) + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (BfsUnfoldEachOuter o1 (ls . (i :))) + Skip o1 -> Skip (BfsUnfoldEachOuter o1 ls) + Stop -> Skip (BfsUnfoldEachInner (ls []) id) step _ (BfsUnfoldEachInner [] rs) = case rs [] of @@ -1364,12 +1364,12 @@ fairUnfoldSched (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (FairUnfoldInit o ls) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (FairUnfoldNext o' id (ls [i]))) - Skip o' -> return $ Skip (FairUnfoldNext o' id (ls [])) - Stop -> return $ Skip (FairUnfoldDrain id (ls [])) + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (FairUnfoldNext o1 id (ls [i])) + Skip o1 -> Skip (FairUnfoldNext o1 id (ls [])) + Stop -> Skip (FairUnfoldDrain id (ls [])) step _ (FairUnfoldNext o ys []) = return $ Skip (FairUnfoldInit o ys) @@ -1415,12 +1415,12 @@ fairUnfoldEach (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (FairUnfoldInit o ls) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (FairUnfoldNext o' id (ls [i]))) - Skip o' -> return $ Skip (FairUnfoldInit o' ls) - Stop -> return $ Skip (FairUnfoldDrain id (ls [])) + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (FairUnfoldNext o1 id (ls [i])) + Skip o1 -> Skip (FairUnfoldInit o1 ls) + Stop -> Skip (FairUnfoldDrain id (ls [])) step _ (FairUnfoldNext o ys []) = return $ Skip (FairUnfoldInit o ys) @@ -1783,13 +1783,13 @@ unfoldEachEndByM {-# INLINE_LATE step #-} step gst (InterposeSuffixFirst s1) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (InterposeSuffixFirstInner s i)) - -- i `seq` return (Skip (InterposeSuffixFirstYield s i)) - Skip s -> return $ Skip (InterposeSuffixFirst s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (InterposeSuffixFirstInner s i) + -- i `seq` Skip (InterposeSuffixFirstYield s i) + Skip s -> Skip (InterposeSuffixFirst s) + Stop -> Stop {- step _ (InterposeSuffixFirstYield s1 i1) = do @@ -1864,13 +1864,13 @@ unfoldEachSepByM {-# INLINE_LATE step #-} step gst (InterposeFirst s1) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (InterposeFirstInner s i)) - -- i `seq` return (Skip (InterposeFirstYield s i)) - Skip s -> return $ Skip (InterposeFirst s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (InterposeFirstInner s i) + -- i `seq` Skip (InterposeFirstYield s i) + Skip s -> Skip (InterposeFirst s) + Stop -> Stop {- step _ (InterposeFirstYield s1 i1) = do @@ -1890,13 +1890,13 @@ unfoldEachSepByM step gst (InterposeFirstInject s1) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - -- i `seq` return (Skip (InterposeFirstBuf s i)) - i `seq` return (Skip (InterposeSecondYield s i)) - Skip s -> return $ Skip (InterposeFirstInject s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + -- in i `seq` Skip (InterposeFirstBuf s i) + in i `seq` Skip (InterposeSecondYield s i) + Skip s -> Skip (InterposeFirstInject s) + Stop -> Stop {- step _ (InterposeFirstBuf s1 i1) = do @@ -1979,21 +1979,21 @@ intercalateEndBy {-# INLINE_LATE step #-} step gst (ICUFirst s1 s2) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (ICUFirstInner s s2 i)) - Skip s -> return $ Skip (ICUFirst s s2) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (ICUFirstInner s s2 i) + Skip s -> Skip (ICUFirst s s2) + Stop -> Stop step gst (ICUFirstOnly s1) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (ICUFirstOnlyInner s i)) - Skip s -> return $ Skip (ICUFirstOnly s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (ICUFirstOnlyInner s i) + Skip s -> Skip (ICUFirstOnly s) + Stop -> Stop step _ (ICUFirstInner s1 s2 i1) = do r <- istep1 i1 @@ -2011,12 +2011,12 @@ intercalateEndBy step gst (ICUSecond s1 s2) = do r <- step2 (adaptState gst) s2 - case r of - Yield a s -> do - i <- inject2 a - i `seq` return (Skip (ICUSecondInner s1 s i)) - Skip s -> return $ Skip (ICUSecond s1 s) - Stop -> return $ Skip (ICUFirstOnly s1) + return $ case r of + Yield a s -> + let i = inject2 a + in i `seq` Skip (ICUSecondInner s1 s i) + Skip s -> Skip (ICUSecond s1 s) + Stop -> Skip (ICUFirstOnly s1) step _ (ICUSecondInner s1 s2 i2) = do r <- istep2 i2 @@ -2089,13 +2089,13 @@ intercalateSepBy {-# INLINE_LATE step #-} step gst (ICALFirst s1 s2) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (ICALFirstInner s s2 i)) - -- i `seq` return (Skip (ICALFirstYield s s2 i)) - Skip s -> return $ Skip (ICALFirst s s2) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (ICALFirstInner s s2 i) + -- in i `seq` Skip (ICALFirstYield s s2 i) + Skip s -> Skip (ICALFirst s s2) + Stop -> Stop {- step _ (ICALFirstYield s1 s2 i1) = do @@ -2115,12 +2115,12 @@ intercalateSepBy step gst (ICALFirstOnly s1) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (ICALFirstOnlyInner s i)) - Skip s -> return $ Skip (ICALFirstOnly s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (ICALFirstOnlyInner s i) + Skip s -> Skip (ICALFirstOnly s) + Stop -> Stop step _ (ICALFirstOnlyInner s1 i1) = do r <- istep1 i1 @@ -2135,22 +2135,22 @@ intercalateSepBy -- machine a bit simpler though. step gst (ICALSecondInject s1 s2) = do r <- step2 (adaptState gst) s2 - case r of - Yield a s -> do - i <- inject2 a - i `seq` return (Skip (ICALFirstInject s1 s i)) - Skip s -> return $ Skip (ICALSecondInject s1 s) - Stop -> return $ Skip (ICALFirstOnly s1) + return $ case r of + Yield a s -> + let i = inject2 a + in i `seq` Skip (ICALFirstInject s1 s i) + Skip s -> Skip (ICALSecondInject s1 s) + Stop -> Skip (ICALFirstOnly s1) step gst (ICALFirstInject s1 s2 i2) = do r <- step1 (adaptState gst) s1 - case r of - Yield a s -> do - i <- inject1 a - i `seq` return (Skip (ICALSecondInner s s2 i i2)) - -- i `seq` return (Skip (ICALFirstBuf s s2 i i2)) - Skip s -> return $ Skip (ICALFirstInject s s2 i2) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject1 a + in i `seq` Skip (ICALSecondInner s s2 i i2) + -- in i `seq` Skip (ICALFirstBuf s s2 i i2) + Skip s -> Skip (ICALFirstInject s s2 i2) + Stop -> Stop {- step _ (ICALFirstBuf s1 s2 i1 i2) = do diff --git a/core/src/Streamly/Internal/Data/Stream/Type.hs b/core/src/Streamly/Internal/Data/Stream/Type.hs index a43b33ca47..d0515f677c 100644 --- a/core/src/Streamly/Internal/Data/Stream/Type.hs +++ b/core/src/Streamly/Internal/Data/Stream/Type.hs @@ -341,13 +341,6 @@ uncons (UnStream step state) = go SPEC state -- From 'Unfold' ------------------------------------------------------------------------------ -data UnfoldState s = UnfoldNothing | UnfoldJust s - --- XXX Because the inject function is monadic we need a separate state for --- inject. If we had a pure Unfold type then conversion to stream is trivial. --- We can possibly have Unfold and UnfoldM or Unfold_ (pure). Which use cases --- require the monadic inject? - -- | Convert an 'Unfold' into a stream by supplying it an input seed. -- -- >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello") @@ -357,18 +350,8 @@ data UnfoldState s = UnfoldNothing | UnfoldJust s -- hello -- {-# INLINE_NORMAL unfold #-} -unfold :: Applicative m => Unfold m a b -> a -> Stream m b -unfold (Unfold ustep inject) seed = Stream step UnfoldNothing - - where - - {-# INLINE_LATE step #-} - step _ UnfoldNothing = Skip . UnfoldJust <$> inject seed - step _ (UnfoldJust st) = do - (\case - Yield x s -> Yield x (UnfoldJust s) - Skip s -> Skip (UnfoldJust s) - Stop -> Stop) <$> ustep st +unfold :: Unfold m a b -> a -> Stream m b +unfold (Unfold ustep inject) seed = Stream (\_ st -> ustep st) (inject seed) ------------------------------------------------------------------------------ -- From Values @@ -1599,18 +1582,18 @@ unfoldEach (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (UnfoldEachOuter o) = do r <- ostep (adaptState gst) o - case r of - Yield a o' -> do - i <- inject a - i `seq` return (Skip (UnfoldEachInner o' i)) - Skip o' -> return $ Skip (UnfoldEachOuter o') - Stop -> return Stop + return $ case r of + Yield a o1 -> + let i = inject a + in i `seq` Skip (UnfoldEachInner o1 i) + Skip o1 -> Skip (UnfoldEachOuter o1) + Stop -> Stop step _ (UnfoldEachInner o i) = do r <- istep i return $ case r of - Yield x i' -> Yield x (UnfoldEachInner o i') - Skip i' -> Skip (UnfoldEachInner o i') + Yield x i1 -> Yield x (UnfoldEachInner o i1) + Skip i1 -> Skip (UnfoldEachInner o i1) Stop -> Skip (UnfoldEachOuter o) RENAME(unfoldMany,unfoldEach) @@ -2054,24 +2037,24 @@ unfoldIterate (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (IterateUnfoldOuter o) = do r <- ostep (adaptState gst) o - case r of - Yield a s -> do - i <- inject a - i `seq` return (Yield a (IterateUnfoldInner s i [])) - Skip s -> return $ Skip (IterateUnfoldOuter s) - Stop -> return Stop + return $ case r of + Yield a s -> + let i = inject a + in i `seq` Yield a (IterateUnfoldInner s i []) + Skip s -> Skip (IterateUnfoldOuter s) + Stop -> Stop step _ (IterateUnfoldInner o i ii) = do r <- istep i - case r of - Yield x s -> do - i1 <- inject x - i1 `seq` return $ Yield x (IterateUnfoldInner o i1 (s:ii)) - Skip s -> return $ Skip (IterateUnfoldInner o s ii) + return $ case r of + Yield x s -> + let i1 = inject x + in i1 `seq` Yield x (IterateUnfoldInner o i1 (s:ii)) + Skip s -> Skip (IterateUnfoldInner o s ii) Stop -> case ii of - (y:ys) -> return $ Skip (IterateUnfoldInner o y ys) - [] -> return $ Skip (IterateUnfoldOuter o) + (y:ys) -> Skip (IterateUnfoldInner o y ys) + [] -> Skip (IterateUnfoldOuter o) RENAME(unfoldIterateDfs,unfoldIterate) @@ -2097,27 +2080,27 @@ altBfsUnfoldIterate (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (IterateUnfoldBFSRevOuter o ii) = do r <- ostep (adaptState gst) o - case r of - Yield a s -> do - i <- inject a - i `seq` return (Yield a (IterateUnfoldBFSRevOuter s (i:ii))) - Skip s -> return $ Skip (IterateUnfoldBFSRevOuter s ii) + return $ case r of + Yield a s -> + let i = inject a + in i `seq` Yield a (IterateUnfoldBFSRevOuter s (i:ii)) + Skip s -> Skip (IterateUnfoldBFSRevOuter s ii) Stop -> case ii of - (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys) - [] -> return Stop + (y:ys) -> Skip (IterateUnfoldBFSRevInner y ys) + [] -> Stop step _ (IterateUnfoldBFSRevInner i ii) = do r <- istep i - case r of - Yield x s -> do - i1 <- inject x - i1 `seq` return $ Yield x (IterateUnfoldBFSRevInner s (i1:ii)) - Skip s -> return $ Skip (IterateUnfoldBFSRevInner s ii) + return $ case r of + Yield x s -> + let i1 = inject x + in i1 `seq` Yield x (IterateUnfoldBFSRevInner s (i1:ii)) + Skip s -> Skip (IterateUnfoldBFSRevInner s ii) Stop -> case ii of - (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys) - [] -> return Stop + (y:ys) -> Skip (IterateUnfoldBFSRevInner y ys) + [] -> Stop RENAME(unfoldIterateBfsRev,altBfsUnfoldIterate) @@ -2142,30 +2125,30 @@ bfsUnfoldIterate (Unfold istep inject) (Stream ostep ost) = {-# INLINE_LATE step #-} step gst (IterateUnfoldBFSOuter o rii) = do r <- ostep (adaptState gst) o - case r of - Yield a s -> do - i <- inject a - i `seq` return (Yield a (IterateUnfoldBFSOuter s (i:rii))) - Skip s -> return $ Skip (IterateUnfoldBFSOuter s rii) + return $ case r of + Yield a s -> + let i = inject a + in i `seq` Yield a (IterateUnfoldBFSOuter s (i:rii)) + Skip s -> Skip (IterateUnfoldBFSOuter s rii) Stop -> case reverse rii of - (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys []) - [] -> return Stop + (y:ys) -> Skip (IterateUnfoldBFSInner y ys []) + [] -> Stop step _ (IterateUnfoldBFSInner i ii rii) = do r <- istep i - case r of - Yield x s -> do - i1 <- inject x - i1 `seq` return $ Yield x (IterateUnfoldBFSInner s ii (i1:rii)) - Skip s -> return $ Skip (IterateUnfoldBFSInner s ii rii) + return $ case r of + Yield x s -> + let i1 = inject x + in i1 `seq` Yield x (IterateUnfoldBFSInner s ii (i1:rii)) + Skip s -> Skip (IterateUnfoldBFSInner s ii rii) Stop -> case ii of - (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys rii) + (y:ys) -> Skip (IterateUnfoldBFSInner y ys rii) [] -> case reverse rii of - (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys []) - [] -> return Stop + (y:ys) -> Skip (IterateUnfoldBFSInner y ys []) + [] -> Stop RENAME(unfoldIterateBfs,bfsUnfoldIterate) diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index 2feccc9e56..b795154662 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -213,7 +213,7 @@ fold :: Monad m => Fold m b c -> Unfold m a b -> a -> m c fold (Fold fstep initial _ final) (Unfold ustep inject) a = do res <- initial case res of - FL.Partial x -> inject a >>= go SPEC x + FL.Partial x -> go SPEC x (inject a) FL.Done b -> return b where @@ -248,9 +248,7 @@ foldMany (Fold fstep initial _ final) (Unfold ustep inject1) = where - inject x = do - r <- inject1 x - return (FoldManyStart r) + inject x = FoldManyStart (inject1 x) {-# INLINE consume #-} consume x s fs = do @@ -300,8 +298,8 @@ either (Unfold stepL injectL) (Unfold stepR injectR) = Unfold step inject where - inject (Left x) = Left <$> injectL x - inject (Right x) = Right <$> injectR x + inject (Left x) = Left (injectL x) + inject (Right x) = Right (injectR x) {-# INLINE_LATE step #-} step (Left st) = do @@ -323,14 +321,14 @@ either (Unfold stepL injectL) (Unfold stepR injectR) = Unfold step inject {-# INLINE_NORMAL postscan #-} postscan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c postscan (Fold stepF initial extract final) (Unfold stepU injectU) = - Unfold step inject + mkUnfoldM step inject where - inject a = do + inject a = do r <- initial case r of - FL.Partial fs -> Just . (fs,) <$> injectU a + FL.Partial fs -> return $ Just (fs, injectU a) FL.Done _ -> return Nothing {-# INLINE_LATE step #-} @@ -358,7 +356,7 @@ scanWith restart (Scanl fstep initial extract final) (Unfold stepU injectU) = where - inject a = ScanInit <$> injectU a + inject a = ScanInit (injectU a) {-# INLINE runStep #-} runStep us action = do @@ -432,7 +430,7 @@ postscanlM' f z = postscan (FL.foldlM' f z) {-# INLINE_NORMAL fromStreamD #-} fromStreamD :: Applicative m => Unfold m (Stream m a) a -fromStreamD = Unfold step pure +fromStreamD = Unfold step id where @@ -445,7 +443,7 @@ fromStreamD = Unfold step pure {-# INLINE_NORMAL fromStreamK #-} fromStreamK :: Applicative m => Unfold m (K.StreamK m a) a -fromStreamK = Unfold step pure +fromStreamK = Unfold step id where @@ -468,7 +466,7 @@ fromStream = fromStreamD -- {-# INLINE nilM #-} nilM :: Applicative m => (a -> m c) -> Unfold m a b -nilM f = Unfold step pure +nilM f = Unfold step id where @@ -478,7 +476,7 @@ nilM f = Unfold step pure -- | An empty stream. {-# INLINE nil #-} nil :: Applicative m => Unfold m a b -nil = Unfold (Prelude.const (pure Stop)) pure +nil = Unfold (Prelude.const (pure Stop)) id -- | Prepend a monadic single element generator function to an 'Unfold'. The -- same seed is used in the action as well as the unfold. @@ -490,7 +488,7 @@ consM action unf = Unfold step inject where - inject = pure . Left + inject = Left {-# INLINE_LATE step #-} step (Left a) = (`Yield` Right (D.unfold unf a)) <$> action a @@ -504,7 +502,7 @@ consM action unf = Unfold step inject -- {-# INLINE_LATE fromListM #-} fromListM :: Applicative m => Unfold m [m a] a -fromListM = Unfold step pure +fromListM = Unfold step id where @@ -514,7 +512,7 @@ fromListM = Unfold step pure {-# INLINE fromPtr #-} fromPtr :: forall m a. (MonadIO m, Storable a) => Unfold m (Ptr a) a -fromPtr = Unfold step return +fromPtr = Unfold step id where @@ -536,7 +534,7 @@ replicateM = Unfold step inject where - inject = pure + inject = id {-# INLINE_LATE step #-} step (i, action) = @@ -548,7 +546,7 @@ replicateM = Unfold step inject -- {-# INLINE repeatM #-} repeatM :: Applicative m => Unfold m (m a) a -repeatM = Unfold step pure +repeatM = Unfold step id where @@ -568,7 +566,7 @@ repeat = lmap pure repeatM {-# INLINE_NORMAL zipRepeat #-} zipRepeat :: Functor m => Unfold m a b -> Unfold m (c,a) (c,b) -- zipRepeat = zipArrowWith (,) repeat -zipRepeat (Unfold ustep uinject) = Unfold step (\(c,a) -> (c,) <$> uinject a) +zipRepeat (Unfold ustep uinject) = Unfold step (fmap uinject) where @@ -585,8 +583,8 @@ zipRepeat (Unfold ustep uinject) = Unfold step (\(c,a) -> (c,) <$> uinject a) -- given function repeatedly. -- {-# INLINE iterateM #-} -iterateM :: Applicative m => (a -> m a) -> Unfold m (m a) a -iterateM f = Unfold step id +iterateM :: Functor m => (a -> m a) -> Unfold m (m a) a +iterateM f = mkUnfoldM step id where @@ -604,7 +602,7 @@ iterateM f = Unfold step id -- {-# INLINE_NORMAL fromIndicesM #-} fromIndicesM :: Applicative m => (Int -> m a) -> Unfold m Int a -fromIndicesM gen = Unfold step pure +fromIndicesM gen = Unfold step id where @@ -626,7 +624,7 @@ take n (Unfold step1 inject1) = Unfold step inject where - inject x = (, 0) <$> inject1 x + inject x = (inject1 x, 0) {-# INLINE_LATE step #-} step (st, i) | i < n = do @@ -666,7 +664,7 @@ drop n (Unfold step inject) = Unfold step' inject' where - inject' a = (, n) <$> inject a + inject' a = (inject a, n) {-# INLINE_LATE step' #-} step' (st, i) @@ -690,9 +688,7 @@ dropWhileM f (Unfold step inject) = Unfold step' inject' where - inject' a = do - b <- inject a - return $ Left b + inject' a = Left (inject a) {-# INLINE_LATE step' #-} step' (Left st) = do @@ -751,14 +747,13 @@ gbracket_ -> Unfold m c b -- ^ unfold to run -> Unfold m a b gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) = - Unfold step inject + mkUnfoldM step inject where inject x = do r <- bef x - s <- inject1 r - return $ Right (s, r) + return $ Right (inject1 r, r) {-# INLINE_LATE step #-} step (Right (st, v)) = do @@ -769,9 +764,7 @@ gbracket_ bef exc aft (Unfold estep einject) (Unfold step1 inject1) = Skip s -> return $ Skip (Right (s, v)) Stop -> aft v >> return Stop -- XXX Do not handle async exceptions, just rethrow them. - Left e -> do - r <- einject (v, e) - return $ Skip (Left r) + Left e -> return $ Skip (Left (einject (v, e))) step (Left st) = do res <- estep st return $ case res of @@ -806,7 +799,7 @@ gbracketIO -> Unfold m c b -- ^ unfold to run -> Unfold m a b gbracketIO bef aft onExc (Unfold estep einject) ftry (Unfold step1 inject1) = - Unfold step inject + mkUnfoldM step inject where @@ -817,8 +810,7 @@ gbracketIO bef aft onExc (Unfold estep einject) ftry (Unfold step1 inject1) = r <- bef x ref <- newIOFinalizer (aft r) return (r, ref) - s <- inject1 r - return $ Right (s, r, ref) + return $ Right (inject1 r, r, ref) {-# INLINE_LATE step #-} step (Right (st, v, ref)) = do @@ -837,8 +829,7 @@ gbracketIO bef aft onExc (Unfold estep einject) ftry (Unfold step1 inject1) = -- the finalizer and have not run the exception handler then we -- may leak the resource. liftIO $ clearingIOFinalizer ref (onExc v) - r <- einject e - return $ Skip (Left r) + return $ Skip (Left (einject e)) step (Left st) = do res <- estep st return $ case res of @@ -853,8 +844,8 @@ gbracketIO bef aft onExc (Unfold estep einject) ftry (Unfold step1 inject1) = -- -- /Pre-release/ {-# INLINE_NORMAL before #-} -before :: (a -> m c) -> Unfold m a b -> Unfold m a b -before action (Unfold step inject) = Unfold step (action >> inject) +before :: Functor m => (a -> m c) -> Unfold m a b -> Unfold m a b +before action (Unfold step inject) = mkUnfoldM step (\a -> inject a <$ action a) -- The custom implementation of "after_" is slightly faster (5-7%) than -- "_after". This is just to document and make sure that we can always use @@ -877,9 +868,7 @@ after_ action (Unfold step1 inject1) = Unfold step inject where - inject x = do - s <- inject1 x - return (s, x) + inject x = (inject1 x, x) {-# INLINE_LATE step #-} step (st, v) = do @@ -902,14 +891,13 @@ after_ action (Unfold step1 inject1) = Unfold step inject {-# INLINE_NORMAL afterIO #-} afterIO :: MonadIO m => (a -> IO c) -> Unfold m a b -> Unfold m a b -afterIO action (Unfold step1 inject1) = Unfold step inject +afterIO action (Unfold step1 inject1) = mkUnfoldM step inject where inject x = do - s <- inject1 x ref <- liftIO $ newIOFinalizer (action x) - return (s, ref) + return (inject1 x, ref) {-# INLINE_LATE step #-} step (st, ref) = do @@ -940,9 +928,7 @@ onException action (Unfold step1 inject1) = Unfold step inject where - inject x = do - s <- inject1 x - return (s, x) + inject x = (inject1 x, x) {-# INLINE_LATE step #-} step (st, v) = do @@ -972,9 +958,7 @@ finally_ action (Unfold step1 inject1) = Unfold step inject where - inject x = do - s <- inject1 x - return (s, x) + inject x = (inject1 x, x) {-# INLINE_LATE step #-} step (st, v) = do @@ -1003,14 +987,13 @@ finally_ action (Unfold step1 inject1) = Unfold step inject {-# INLINE_NORMAL finallyIO #-} finallyIO :: (MonadIO m, MonadCatch m) => (a -> IO c) -> Unfold m a b -> Unfold m a b -finallyIO action (Unfold step1 inject1) = Unfold step inject +finallyIO action (Unfold step1 inject1) = mkUnfoldM step inject where inject x = do - s <- inject1 x ref <- liftIO $ newIOFinalizer (action x) - return (s, ref) + return (inject1 x, ref) {-# INLINE_LATE step #-} step (st, ref) = do @@ -1041,14 +1024,13 @@ _bracket bef aft = {-# INLINE_NORMAL bracket_ #-} bracket_ :: MonadCatch m => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b -bracket_ bef aft (Unfold step1 inject1) = Unfold step inject +bracket_ bef aft (Unfold step1 inject1) = mkUnfoldM step inject where inject x = do r <- bef x - s <- inject1 r - return (s, r) + return (inject1 r, r) {-# INLINE_LATE step #-} step (st, v) = do @@ -1083,7 +1065,7 @@ bracket_ bef aft (Unfold step1 inject1) = Unfold step inject {-# INLINE_NORMAL bracketIO #-} bracketIO :: (MonadIO m, MonadCatch m) => (a -> IO c) -> (c -> IO d) -> Unfold m c b -> Unfold m a b -bracketIO bef aft (Unfold step1 inject1) = Unfold step inject +bracketIO bef aft (Unfold step1 inject1) = mkUnfoldM step inject where @@ -1094,8 +1076,7 @@ bracketIO bef aft (Unfold step1 inject1) = Unfold step inject r <- bef x ref <- newIOFinalizer (aft r) return (r, ref) - s <- inject1 r - return (s, ref) + return (inject1 r, ref) {-# INLINE_LATE step #-} step (st, ref) = do diff --git a/core/src/Streamly/Internal/Data/Unfold/Enumeration.hs b/core/src/Streamly/Internal/Data/Unfold/Enumeration.hs index ce75c44b62..f089b2eeff 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Enumeration.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Enumeration.hs @@ -104,7 +104,7 @@ enumerateFromStepNum = Unfold step inject where - inject (!from, !stride) = return (from, stride, 0) + inject (!from, !stride) = (from, stride, 0) -- Note that the counter "i" is the same type as the type being enumerated. -- It may overflow, for example, if we are enumerating Word8, after 255 the @@ -186,7 +186,7 @@ enumerateFromStepIntegral = Unfold step inject where - inject (from, stride) = from `seq` stride `seq` return (from, stride) + inject (from, stride) = from `seq` stride `seq` (from, stride) {-# INLINE_LATE step #-} step (x, stride) = return $ Yield x $! (x + stride, stride) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index d94822a04d..9b4705546f 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -124,7 +124,6 @@ where -- import Control.Arrow (Arrow(..)) -- import Control.Category (Category(..)) -import Control.Monad ((>=>)) import Data.Void (Void) import Fusion.Plugin.Types (Fuse(..)) import Streamly.Internal.Data.Stream.Step (Step(..)) @@ -226,20 +225,34 @@ import Prelude hiding (map, mapM, concatMap, zipWith, takeWhile) -- data Unfold m a b = -- | @Unfold step inject@ - forall s. Unfold (s -> m (Step s b)) (a -> m s) + forall s. Unfold (s -> m (Step s b)) (a -> s) ------------------------------------------------------------------------------ -- Basic constructors ------------------------------------------------------------------------------ +-- | State used by 'mkUnfoldM' to defer a monadic inject effect to the first +-- step. +{-# ANN type MkUnfoldMState Fuse #-} +data MkUnfoldMState a s = MkUnfoldMPre a | MkUnfoldMRun s + -- XXX unfoldWith? --- | Make an unfold from @step@ and @inject@ functions. +-- | Make an unfold from @step@ and a /monadic/ @inject@ function. The inject +-- effect is deferred to the first step. -- -- /Pre-release/ -{-# INLINE mkUnfoldM #-} -mkUnfoldM :: (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b -mkUnfoldM = Unfold +{-# INLINE_NORMAL mkUnfoldM #-} +mkUnfoldM :: Functor m => (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b +mkUnfoldM ustep uinject = Unfold step MkUnfoldMPre + where + {-# INLINE_LATE step #-} + step (MkUnfoldMPre a) = Skip . MkUnfoldMRun <$> uinject a + step (MkUnfoldMRun s) = + (\r -> case r of + Yield x s1 -> Yield x (MkUnfoldMRun s1) + Skip s1 -> Skip (MkUnfoldMRun s1) + Stop -> Stop) <$> ustep s -- | Make an unfold from a step function. -- @@ -247,8 +260,8 @@ mkUnfoldM = Unfold -- -- /Pre-release/ {-# INLINE mkUnfoldrM #-} -mkUnfoldrM :: Applicative m => (a -> m (Step a b)) -> Unfold m a b -mkUnfoldrM step = Unfold step pure +mkUnfoldrM :: (a -> m (Step a b)) -> Unfold m a b +mkUnfoldrM step = Unfold step id -- The type 'Step' is isomorphic to 'Maybe'. Ideally unfoldrM should be the -- same as mkUnfoldrM, this is for compatibility with traditional Maybe based @@ -259,8 +272,8 @@ mkUnfoldrM step = Unfold step pure -- value. When it is done it returns 'Nothing' and the stream ends. -- {-# INLINE unfoldrM #-} -unfoldrM :: Applicative m => (a -> m (Maybe (b, a))) -> Unfold m a b -unfoldrM next = Unfold step pure +unfoldrM :: Functor m => (a -> m (Maybe (b, a))) -> Unfold m a b +unfoldrM next = Unfold step id where {-# INLINE_LATE step #-} step st = @@ -307,8 +320,8 @@ lmap f (Unfold ustep uinject) = Unfold ustep (uinject Prelude.. f) -- lmapM f = Unfold.unfoldEach (Unfold.functionM f) -- {-# INLINE_NORMAL lmapM #-} -lmapM :: Monad m => (a -> m c) -> Unfold m c b -> Unfold m a b -lmapM f (Unfold ustep uinject) = Unfold ustep (f >=> uinject) +lmapM :: Functor m => (a -> m c) -> Unfold m c b -> Unfold m a b +lmapM f (Unfold ustep uinject) = mkUnfoldM ustep (fmap uinject Prelude.. f) -- | Supply the seed to an unfold closing the input end of the unfold. -- @@ -461,7 +474,7 @@ mapM f (Unfold ustep uinject) = Unfold step uinject -- {-# INLINE_NORMAL carry #-} carry :: Functor m => Unfold m a b -> Unfold m a (a,b) -carry (Unfold ustep uinject) = Unfold step (\a -> (a,) <$> uinject a) +carry (Unfold ustep uinject) = Unfold step (\a -> (a, uinject a)) where @@ -488,7 +501,7 @@ consInputWith f (Unfold ustep uinject) = Unfold step inject where - inject a = ConsInputFirst a <$> uinject a + inject a = ConsInputFirst a (uinject a) next r = case r of Yield x s1 -> Yield x (ConsInputRest s1) @@ -547,7 +560,7 @@ fromEffect m = Unfold step inject where - inject _ = pure False + inject _ = False step False = (`Yield` True) <$> m step True = pure Stop @@ -570,7 +583,7 @@ data TupleState a = TupleBoth a a | TupleOne a | TupleNone -- {-# INLINE_LATE fromTuple #-} fromTuple :: Applicative m => Unfold m (a,a) a -fromTuple = Unfold step (\(x,y) -> pure $ TupleBoth x y) +fromTuple = Unfold step (\(x,y) -> TupleBoth x y) where @@ -586,7 +599,7 @@ fromTuple = Unfold step (\(x,y) -> pure $ TupleBoth x y) -- {-# INLINE_LATE fromList #-} fromList :: Applicative m => Unfold m [a] a -fromList = Unfold step pure +fromList = Unfold step id where @@ -666,19 +679,15 @@ crossWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inject where - inject a = do - s1 <- inject1 a - return $ CrossOuter a s1 + inject a = CrossOuter a (inject1 a) {-# INLINE_LATE step #-} step (CrossOuter a s1) = do r <- step1 s1 - case r of - Yield b s -> do - s2 <- inject2 a - return $ Skip (CrossInner a s b s2) - Skip s -> return $ Skip (CrossOuter a s) - Stop -> return Stop + return $ case r of + Yield b s -> Skip (CrossInner a s b (inject2 a)) + Skip s -> Skip (CrossOuter a s) + Stop -> Stop step (CrossInner a s1 b s2) = do r <- step2 s2 @@ -700,19 +709,17 @@ fairCrossWithM f (Unfold step1 inject1) (Unfold step2 inject2) = where - inject a = do - s1 <- inject1 a - return $ FairUnfoldInit a s1 id + inject a = FairUnfoldInit a (inject1 a) id {-# INLINE_LATE step #-} step (FairUnfoldInit a o ls) = do r <- step1 o - case r of - Yield b o' -> do - i <- inject2 a - i `seq` return (Skip (FairUnfoldNext a o' id (ls [(b,i)]))) - Skip o' -> return $ Skip (FairUnfoldInit a o' ls) - Stop -> return $ Skip (FairUnfoldDrain id (ls [])) + return $ case r of + Yield b o1 -> + let i = inject2 a + in i `seq` Skip (FairUnfoldNext a o1 id (ls [(b,i)])) + Skip o1 -> Skip (FairUnfoldInit a o1 ls) + Stop -> Skip (FairUnfoldDrain id (ls [])) step (FairUnfoldNext a o ys []) = return $ Skip (FairUnfoldInit a o ys) @@ -833,9 +840,7 @@ concatMapM f (Unfold step1 inject1) = Unfold step inject where - inject x = do - s <- inject1 x - return $ ConcatMapOuter x s + inject x = ConcatMapOuter x (inject1 x) {-# INLINE_LATE step #-} step (ConcatMapOuter seed st) = do @@ -843,8 +848,7 @@ concatMapM f (Unfold step1 inject1) = Unfold step inject case r of Yield x s -> do Unfold step2 inject2 <- f x - innerSt <- inject2 seed - return $ Skip (ConcatMapInner seed s innerSt step2) + return $ Skip (ConcatMapInner seed s (inject2 seed) step2) Skip s -> return $ Skip (ConcatMapOuter seed s) Stop -> return Stop @@ -908,7 +912,7 @@ functionM f = Unfold step inject where - inject x = pure $ Just x + inject x = Just x {-# INLINE_LATE step #-} step (Just x) = (`Yield` Nothing) <$> f x @@ -932,7 +936,7 @@ functionMaybeM f = Unfold step inject where - inject a = return (Just a) + inject a = Just a {-# INLINE_LATE step #-} step (Just a) = do @@ -966,19 +970,15 @@ unfoldEach (Unfold step2 inject2) (Unfold step1 inject1) = Unfold step inject where - inject x = do - s <- inject1 x - return $ ConcatOuter s + inject x = ConcatOuter (inject1 x) {-# INLINE_LATE step #-} step (ConcatOuter st) = do r <- step1 st - case r of - Yield x s -> do - innerSt <- inject2 x - return $ Skip (ConcatInner s innerSt) - Skip s -> return $ Skip (ConcatOuter s) - Stop -> return Stop + return $ case r of + Yield x s -> Skip (ConcatInner s (inject2 x)) + Skip s -> Skip (ConcatOuter s) + Stop -> Stop step (ConcatInner ost ist) = do r <- step2 ist @@ -1017,10 +1017,7 @@ zipArrowWithM f (Unfold step1 inject1) (Unfold step2 inject2) = Unfold step inje where - inject (x,y) = do - s1 <- inject1 x - s2 <- inject2 y - return (s1, s2, Nothing) + inject (x,y) = (inject1 x, inject2 y, Nothing) {-# INLINE_LATE step #-} step (s1, s2, Nothing) = do @@ -1156,10 +1153,7 @@ interleave (Unfold step1 inject1) (Unfold step2 inject2) = where - inject (a,b) = do - s1 <- inject1 a - s2 <- inject2 b - return (InterleaveFirst s1 s2) + inject (a,b) = InterleaveFirst (inject1 a) (inject2 b) {-# INLINE_LATE step #-} step (InterleaveFirst st1 st2) = do @@ -1208,19 +1202,17 @@ unfoldEachInterleave (Unfold istep iinject) (Unfold ostep oinject) = where - inject x = do - ost <- oinject x - return (ManyInterleaveOuter ost []) + inject x = ManyInterleaveOuter (oinject x) [] {-# INLINE_LATE step #-} step (ManyInterleaveOuter o ls) = do r <- ostep o - case r of - Yield a o' -> do - i <- iinject a - i `seq` return (Skip (ManyInterleaveInner o' (i : ls))) - Skip o' -> return $ Skip (ManyInterleaveOuter o' ls) - Stop -> return $ Skip (ManyInterleaveInnerL ls []) + return $ case r of + Yield a o1 -> + let i = iinject a + in i `seq` Skip (ManyInterleaveInner o1 (i : ls)) + Skip o1 -> Skip (ManyInterleaveOuter o1 ls) + Stop -> Skip (ManyInterleaveInnerL ls []) step (ManyInterleaveInner _ []) = undefined step (ManyInterleaveInner o (st:ls)) = do diff --git a/core/src/Streamly/Internal/FileSystem/DirIO.hs b/core/src/Streamly/Internal/FileSystem/DirIO.hs index 02c519e9b1..d3a738e1bc 100644 --- a/core/src/Streamly/Internal/FileSystem/DirIO.hs +++ b/core/src/Streamly/Internal/FileSystem/DirIO.hs @@ -157,7 +157,7 @@ toChunksWithBufferOf size h = D.fromStreamD (D.Stream step ()) -- @since 0.7.0 {-# INLINE_NORMAL readChunksWithBufferOf #-} readChunksWithBufferOf :: MonadIO m => Unfold m (Int, Handle) (Array Word8) -readChunksWithBufferOf = Unfold step return +readChunksWithBufferOf = Unfold step id where {-# INLINE_LATE step #-} step (size, h) = do diff --git a/core/src/Streamly/Internal/FileSystem/Handle.hs b/core/src/Streamly/Internal/FileSystem/Handle.hs index 9b5d10cc01..2c2a752a10 100644 --- a/core/src/Streamly/Internal/FileSystem/Handle.hs +++ b/core/src/Streamly/Internal/FileSystem/Handle.hs @@ -130,7 +130,7 @@ import Prelude hiding (read) import Streamly.Internal.Data.Fold (Fold) import Streamly.Internal.Data.Refold.Type (Refold(..)) -import Streamly.Internal.Data.Unfold.Type (Unfold(..)) +import Streamly.Internal.Data.Unfold.Type (Unfold(..), mkUnfoldM) import Streamly.Internal.Data.Array.Type (Array(..), unsafeFreezeWithShrink, byteLength) import Streamly.Internal.Data.Stream.Type (Stream) @@ -264,7 +264,7 @@ readChunksWithBufferOf = chunkReaderWith {-# INLINE_NORMAL chunkReaderFromToWith #-} chunkReaderFromToWith :: MonadIO m => Unfold m (Int, Int, Int, Handle) (Array Word8) -chunkReaderFromToWith = Unfold step inject +chunkReaderFromToWith = mkUnfoldM step inject where diff --git a/core/src/Streamly/Internal/Syscall/Posix/ReadDir.hsc b/core/src/Streamly/Internal/Syscall/Posix/ReadDir.hsc index b293621505..a6428cbbc8 100644 --- a/core/src/Streamly/Internal/Syscall/Posix/ReadDir.hsc +++ b/core/src/Streamly/Internal/Syscall/Posix/ReadDir.hsc @@ -325,7 +325,7 @@ readDirStreamEither confMod (curdir, (DirStream dirp)) = loop streamEitherReader :: MonadIO m => (ReadOptions -> ReadOptions) -> Unfold m (PosixPath, DirStream) (Either Path Path) -streamEitherReader confMod = Unfold step return +streamEitherReader confMod = Unfold step id where step s = do diff --git a/core/src/Streamly/Internal/Syscall/Windows/ReadDir.hsc b/core/src/Streamly/Internal/Syscall/Windows/ReadDir.hsc index 0517c2025b..96fd05d833 100644 --- a/core/src/Streamly/Internal/Syscall/Windows/ReadDir.hsc +++ b/core/src/Streamly/Internal/Syscall/Windows/ReadDir.hsc @@ -277,7 +277,7 @@ readDirStreamEither _ (DirStream (h, ref, fdata)) = streamEitherReader :: MonadIO m => (ReadOptions -> ReadOptions) -> Unfold m DirStream (Either Path Path) -streamEitherReader f = Unfold step return +streamEitherReader f = Unfold step id where step strm = do diff --git a/core/src/Streamly/Internal/Unicode/Encode.hs b/core/src/Streamly/Internal/Unicode/Encode.hs index df33732d7f..82b2df3619 100644 --- a/core/src/Streamly/Internal/Unicode/Encode.hs +++ b/core/src/Streamly/Internal/Unicode/Encode.hs @@ -211,7 +211,7 @@ readCharUtf8With surr = Unfold step inject where inject c = - return $ case ord c of + case ord c of x | x <= 0x7F -> fromIntegral x `WCons` WNil | x <= 0x7FF -> ord2 c | x <= 0xFFFF -> if isSurrogate c then surr else ord3 c @@ -303,7 +303,7 @@ readCharUtf16With invalidReplacement = Unfold step inject where inject c = - return $ case ord c of + case ord c of x | x < 0xD800 -> fromIntegral x `WCons` WNil | x > 0xDFFF && x <= 0xFFFF -> fromIntegral x `WCons` WNil | x >= 0x10000 && x <= 0x10FFFF -> diff --git a/src/Streamly/Internal/Data/SmallArray.hs b/src/Streamly/Internal/Data/SmallArray.hs index 549c984578..be665461bb 100644 --- a/src/Streamly/Internal/Data/SmallArray.hs +++ b/src/Streamly/Internal/Data/SmallArray.hs @@ -195,7 +195,7 @@ streamFold f arr = f (read arr) reader :: Monad m => Unfold m (SmallArray a) a reader = Unfold step inject where - inject arr = return (arr, 0) + inject arr = (arr, 0) step (arr, i) | i == length arr = return D.Stop | otherwise = diff --git a/src/Streamly/Internal/Data/Unfold/Exception.hs b/src/Streamly/Internal/Data/Unfold/Exception.hs index ba5f82d874..8b91b4b378 100644 --- a/src/Streamly/Internal/Data/Unfold/Exception.hs +++ b/src/Streamly/Internal/Data/Unfold/Exception.hs @@ -55,7 +55,7 @@ gbracket -> Unfold m c b -- ^ unfold to run -> Unfold m a b gbracket bef aft (Unfold estep einject) ftry (Unfold step1 inject1) = - Unfold step inject + mkUnfoldM step inject where @@ -66,8 +66,7 @@ gbracket bef aft (Unfold estep einject) ftry (Unfold step1 inject1) = r <- bef x ref <- newIOFinalizer (aft r) return (r, ref) - s <- inject1 r - return $ Right (s, r, ref) + return $ Right (inject1 r, r, ref) {-# INLINE_LATE step #-} step (Right (st, v, ref)) = do @@ -85,7 +84,7 @@ gbracket bef aft (Unfold estep einject) ftry (Unfold step1 inject1) = -- be atomic wrt async exceptions. Otherwise if we have cleared -- the finalizer and have not run the exception handler then we -- may leak the resource. - r <- clearingIOFinalizer ref (einject (v, e)) + r <- clearingIOFinalizer ref (return (einject (v, e))) return $ Skip (Left r) step (Left st) = do res <- estep st @@ -107,14 +106,13 @@ gbracket bef aft (Unfold estep einject) ftry (Unfold step1 inject1) = {-# INLINE_NORMAL after #-} after :: MonadRunInIO m => (a -> m c) -> Unfold m a b -> Unfold m a b -after action (Unfold step1 inject1) = Unfold step inject +after action (Unfold step1 inject1) = mkUnfoldM step inject where inject x = do - s <- inject1 x ref <- newIOFinalizer (action x) - return (s, ref) + return (inject1 x, ref) {-# INLINE_LATE step #-} step (st, ref) = do @@ -145,14 +143,13 @@ after action (Unfold step1 inject1) = Unfold step inject {-# INLINE_NORMAL finally #-} finally :: (MonadAsync m, MonadCatch m) => (a -> m c) -> Unfold m a b -> Unfold m a b -finally action (Unfold step1 inject1) = Unfold step inject +finally action (Unfold step1 inject1) = mkUnfoldM step inject where inject x = do - s <- inject1 x ref <- newIOFinalizer (action x) - return (s, ref) + return (inject1 x, ref) {-# INLINE_LATE step #-} step (st, ref) = do @@ -189,7 +186,7 @@ finally action (Unfold step1 inject1) = Unfold step inject {-# INLINE_NORMAL bracket #-} bracket :: (MonadAsync m, MonadCatch m) => (a -> m c) -> (c -> m d) -> Unfold m c b -> Unfold m a b -bracket bef aft (Unfold step1 inject1) = Unfold step inject +bracket bef aft (Unfold step1 inject1) = mkUnfoldM step inject where @@ -200,8 +197,7 @@ bracket bef aft (Unfold step1 inject1) = Unfold step inject r <- bef x ref <- newIOFinalizer (aft r) return (r, ref) - s <- inject1 r - return (s, ref) + return (inject1 r, ref) {-# INLINE_LATE step #-} step (st, ref) = do diff --git a/src/Streamly/Internal/Data/Unfold/SVar.hs b/src/Streamly/Internal/Data/Unfold/SVar.hs index e873afee1f..b005246797 100644 --- a/src/Streamly/Internal/Data/Unfold/SVar.hs +++ b/src/Streamly/Internal/Data/Unfold/SVar.hs @@ -46,7 +46,7 @@ data FromSVarState t m a = -- {-# INLINE_NORMAL fromSVar #-} fromSVar :: MonadAsync m => Unfold m (SVar t m a) a -fromSVar = Unfold step (return . FromSVarInit) +fromSVar = Unfold step FromSVarInit where {-# INLINE_LATE step #-} @@ -129,7 +129,7 @@ fromSVar = Unfold step (return . FromSVarInit) -- {-# INLINE_NORMAL fromProducer #-} fromProducer :: MonadAsync m => Unfold m (SVar t m a) a -fromProducer = Unfold step (return . FromSVarRead) +fromProducer = Unfold step FromSVarRead where {-# INLINE_LATE step #-} diff --git a/src/Streamly/Internal/Network/Socket.hs b/src/Streamly/Internal/Network/Socket.hs index 52880b769e..9676fc0540 100644 --- a/src/Streamly/Internal/Network/Socket.hs +++ b/src/Streamly/Internal/Network/Socket.hs @@ -89,7 +89,7 @@ import qualified Network.Socket as Net import Streamly.Internal.Data.Array (Array(..)) import Streamly.Data.Fold (Fold) import Streamly.Data.Stream (Stream) -import Streamly.Internal.Data.Unfold (Unfold(..)) +import Streamly.Internal.Data.Unfold (Unfold(..), mkUnfoldM) -- import Streamly.String (encodeUtf8, decodeUtf8, foldLines) import Streamly.Internal.System.IO (defaultChunkSize) @@ -167,7 +167,7 @@ initListener listenQLen SockSpec{..} addr = {-# INLINE listenTuples #-} listenTuples :: MonadIO m => Unfold m (Int, SockSpec, SockAddr) (Socket, SockAddr) -listenTuples = Unfold step inject +listenTuples = mkUnfoldM step inject where inject (listenQLen, spec, addr) = liftIO $ initListener listenQLen spec addr @@ -365,7 +365,7 @@ readChunks = readChunksWith defaultChunkSize -- {-# INLINE_NORMAL chunkReaderWith #-} chunkReaderWith :: MonadIO m => Unfold m (Int, Socket) (Array Word8) -chunkReaderWith = Unfold step return +chunkReaderWith = Unfold step id where {-# INLINE_LATE step #-} step (size, h) = do From a248f7cab33499abd38dab34e083515ccdb5c65f Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 17:47:52 +0530 Subject: [PATCH 02/20] Downgrade functionMaybeM constraint to Applicative --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 9b4705546f..7fea418557 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -931,7 +931,7 @@ function f = functionM $ pure Prelude.. f -- generates a singleton stream. -- {-# INLINE functionMaybeM #-} -functionMaybeM :: Monad m => (a -> m (Maybe b)) -> Unfold m a b +functionMaybeM :: Applicative m => (a -> m (Maybe b)) -> Unfold m a b functionMaybeM f = Unfold step inject where @@ -939,12 +939,10 @@ functionMaybeM f = Unfold step inject inject a = Just a {-# INLINE_LATE step #-} - step (Just a) = do - result <- f a - case result of - Just b -> pure $ Yield b Nothing - Nothing -> pure Stop - + step (Just a) = + (\case + Just b -> Yield b Nothing + Nothing -> Stop) <$> f a step Nothing = pure Stop -- | Identity unfold. The unfold generates a singleton stream having the input From 02e860e22af2cfd9fe69e51cc9d062b4d478699a Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 18:19:29 +0530 Subject: [PATCH 03/20] Define mkUnfoldM in terms of lmapM --- .../src/Streamly/Internal/Data/Unfold/Type.hs | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 7fea418557..5be4897acf 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -231,29 +231,8 @@ data Unfold m a b = -- Basic constructors ------------------------------------------------------------------------------ --- | State used by 'mkUnfoldM' to defer a monadic inject effect to the first --- step. -{-# ANN type MkUnfoldMState Fuse #-} -data MkUnfoldMState a s = MkUnfoldMPre a | MkUnfoldMRun s - -- XXX unfoldWith? --- | Make an unfold from @step@ and a /monadic/ @inject@ function. The inject --- effect is deferred to the first step. --- --- /Pre-release/ -{-# INLINE_NORMAL mkUnfoldM #-} -mkUnfoldM :: Functor m => (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b -mkUnfoldM ustep uinject = Unfold step MkUnfoldMPre - where - {-# INLINE_LATE step #-} - step (MkUnfoldMPre a) = Skip . MkUnfoldMRun <$> uinject a - step (MkUnfoldMRun s) = - (\r -> case r of - Yield x s1 -> Yield x (MkUnfoldMRun s1) - Skip s1 -> Skip (MkUnfoldMRun s1) - Stop -> Stop) <$> ustep s - -- | Make an unfold from a step function. -- -- See also: 'unfoldrM' @@ -313,6 +292,11 @@ unfoldr step = unfoldrM (pure . step) lmap :: (a -> c) -> Unfold m c b -> Unfold m a b lmap f (Unfold ustep uinject) = Unfold ustep (uinject Prelude.. f) +-- | State used by 'lmapM' to defer the monadic input transformation to the +-- first step. +{-# ANN type LMapMState Fuse #-} +data LMapMState a s = LMapMPre a | LMapMRun s + -- | Map an action on the input argument of the 'Unfold'. -- -- Definition: @@ -321,7 +305,23 @@ lmap f (Unfold ustep uinject) = Unfold ustep (uinject Prelude.. f) -- {-# INLINE_NORMAL lmapM #-} lmapM :: Functor m => (a -> m c) -> Unfold m c b -> Unfold m a b -lmapM f (Unfold ustep uinject) = mkUnfoldM ustep (fmap uinject Prelude.. f) +lmapM f (Unfold ustep uinject) = Unfold step LMapMPre + where + {-# INLINE_LATE step #-} + step (LMapMPre a) = Skip . LMapMRun . uinject <$> f a + step (LMapMRun s) = + (\r -> case r of + Yield x s1 -> Yield x (LMapMRun s1) + Skip s1 -> Skip (LMapMRun s1) + Stop -> Stop) <$> ustep s + +-- | Make an unfold from @step@ and a /monadic/ @inject@ function. The inject +-- effect is deferred to the first step. +-- +-- /Pre-release/ +{-# INLINE mkUnfoldM #-} +mkUnfoldM :: Functor m => (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b +mkUnfoldM step inject = lmapM inject (Unfold step id) -- | Supply the seed to an unfold closing the input end of the unfold. -- From f5ef111c46d42a5f3fab28258110492e67a12faf Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 13:36:55 +0530 Subject: [PATCH 04/20] Fix hlint in Unfold/function --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 5be4897acf..05bbb69327 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -310,7 +310,7 @@ lmapM f (Unfold ustep uinject) = Unfold step LMapMPre {-# INLINE_LATE step #-} step (LMapMPre a) = Skip . LMapMRun . uinject <$> f a step (LMapMRun s) = - (\r -> case r of + (\case Yield x s1 -> Yield x (LMapMRun s1) Skip s1 -> Skip (LMapMRun s1) Stop -> Stop) <$> ustep s @@ -908,12 +908,10 @@ instance Monad m => Monad (Unfold m a) where -- {-# INLINE functionM #-} functionM :: Applicative m => (a -> m b) -> Unfold m a b -functionM f = Unfold step inject +functionM f = Unfold step Just where - inject x = Just x - {-# INLINE_LATE step #-} step (Just x) = (`Yield` Nothing) <$> f x step Nothing = pure Stop @@ -932,12 +930,10 @@ function f = functionM $ pure Prelude.. f -- {-# INLINE functionMaybeM #-} functionMaybeM :: Applicative m => (a -> m (Maybe b)) -> Unfold m a b -functionMaybeM f = Unfold step inject +functionMaybeM f = Unfold step Just where - inject a = Just a - {-# INLINE_LATE step #-} step (Just a) = (\case From b6935c2f9bd1aab34b2ce2da72fd3b439c45b550 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 18:46:37 +0530 Subject: [PATCH 05/20] Add a mkUnfold smart constructor --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 05bbb69327..aea23bf336 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -48,6 +48,7 @@ module Streamly.Internal.Data.Unfold.Type , Unfold (..) -- * Basic Constructors + , mkUnfold , mkUnfoldM , mkUnfoldrM , unfoldrM @@ -233,6 +234,13 @@ data Unfold m a b = -- XXX unfoldWith? +-- | Make an unfold from @step@ and a pure @inject@ function. +-- +-- /Pre-release/ +{-# INLINE mkUnfold #-} +mkUnfold :: (s -> m (Step s b)) -> (a -> s) -> Unfold m a b +mkUnfold = Unfold + -- | Make an unfold from a step function. -- -- See also: 'unfoldrM' From c0a4ceb6c509a32b0c17d1e331560d79dd2829e7 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 18:48:27 +0530 Subject: [PATCH 06/20] Deprecate mkUnfoldrM --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index aea23bf336..2e43918694 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -50,7 +50,6 @@ module Streamly.Internal.Data.Unfold.Type -- * Basic Constructors , mkUnfold , mkUnfoldM - , mkUnfoldrM , unfoldrM , unfoldr , functionM @@ -110,6 +109,7 @@ module Streamly.Internal.Data.Unfold.Type , zipWith -- * Deprecated + , mkUnfoldrM , many , many2 , manyInterleave @@ -245,7 +245,7 @@ mkUnfold = Unfold -- -- See also: 'unfoldrM' -- --- /Pre-release/ +{-# DEPRECATED mkUnfoldrM "Use mkUnfold with id as the inject function instead." #-} {-# INLINE mkUnfoldrM #-} mkUnfoldrM :: (a -> m (Step a b)) -> Unfold m a b mkUnfoldrM step = Unfold step id From 4a0dd05c328597bf1159d007cf9e8dca69c9939e Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 18:54:12 +0530 Subject: [PATCH 07/20] Add haddock definition in mkUnfoldM --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 2e43918694..3a56599b9e 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -326,10 +326,14 @@ lmapM f (Unfold ustep uinject) = Unfold step LMapMPre -- | Make an unfold from @step@ and a /monadic/ @inject@ function. The inject -- effect is deferred to the first step. -- +-- Definition: +-- +-- >>> mkUnfoldM step inject = Unfold.lmapM inject (Unfold.mkUnfold step id) +-- -- /Pre-release/ {-# INLINE mkUnfoldM #-} mkUnfoldM :: Functor m => (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b -mkUnfoldM step inject = lmapM inject (Unfold step id) +mkUnfoldM step inject = lmapM inject (mkUnfold step id) -- | Supply the seed to an unfold closing the input end of the unfold. -- From 709f6127dd864e3582aaa194bbf32e37ea39ca9b Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Mon, 25 May 2026 19:22:05 +0530 Subject: [PATCH 08/20] Add constructor naming rationale --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 3a56599b9e..c19d2b07ac 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -47,6 +47,12 @@ module Streamly.Internal.Data.Unfold.Type Step(..) , Unfold (..) + -- Constructor Naming: + -- - "mk" prefix marks the primitive constructors. Use mkUnfold vs unfold + -- because it would collide with 'Data.Stream.unfold'. + -- - Bare names follow Data.List APIs. + -- - Suffixes track the variants + -- * Basic Constructors , mkUnfold , mkUnfoldM From 66290fe36499354968bacd7ec869eccb9d37711b Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Tue, 26 May 2026 00:19:07 +0530 Subject: [PATCH 09/20] Add mapMaybe, catMaybes in Unfold --- core/src/Streamly/Internal/Data/Unfold.hs | 56 +++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index b795154662..b88ac2babc 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -77,6 +77,9 @@ module Streamly.Internal.Data.Unfold , drop , dropWhile , dropWhileM + , mapMaybe + , mapMaybeM + , catMaybes -- ** Cross product , innerJoin @@ -638,6 +641,7 @@ take n (Unfold step1 inject1) = Unfold step inject -- {-# INLINE_NORMAL filterM #-} filterM :: Monad m => (b -> m Bool) -> Unfold m a b -> Unfold m a b +-- filterM p = mapMaybeM (\x -> fmap (\b -> if b then Just x else Nothing) (p x)) filterM f (Unfold step1 inject1) = Unfold step inject1 where {-# INLINE_LATE step #-} @@ -716,6 +720,58 @@ dropWhileM f (Unfold step inject) = Unfold step' inject' dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b dropWhile f = dropWhileM (return . f) +------------------------------------------------------------------------------ +-- Maybe Unfolds +------------------------------------------------------------------------------ + +-- Note: do not define in terms of "filter", avoid partial fromJust. +-- instead define filter in terms of mapMaybe. + +-- | Like 'mapMaybe' but maps a monadic function. +-- +-- Definition: +-- +-- >>> mapMaybeM f = Unfold.catMaybes . Unfold.mapM f +-- +{-# INLINE_NORMAL mapMaybeM #-} +mapMaybeM :: Monad m => (b -> m (Maybe c)) -> Unfold m a b -> Unfold m a c +mapMaybeM f (Unfold step1 inject1) = Unfold step inject1 + where + {-# INLINE_LATE step #-} + step st = do + r <- step1 st + case r of + Yield x s -> do + b <- f x + return $ case b of + Just c -> Yield c s + Nothing -> Skip s + Skip s -> return $ Skip s + Stop -> return Stop + +-- | Map a 'Maybe' returning function on the output of the unfold, filter out +-- the 'Nothing' elements, and return an unfold yielding the values extracted +-- from 'Just'. +-- +-- Definition: +-- +-- >>> mapMaybe f = Unfold.catMaybes . fmap f +-- +{-# INLINE mapMaybe #-} +mapMaybe :: Monad m => (b -> Maybe c) -> Unfold m a b -> Unfold m a c +mapMaybe f = mapMaybeM (return . f) + +-- | In an unfold whose output is a 'Maybe', discard 'Nothing's and unwrap +-- 'Just's. +-- +-- Definition: +-- +-- >>> catMaybes = Unfold.mapMaybe id +-- +{-# INLINE catMaybes #-} +catMaybes :: Monad m => Unfold m a (Maybe b) -> Unfold m a b +catMaybes = mapMaybe id + -- | Cross intersection of two unfolds. See -- 'Streamly.Internal.Data.Stream.innerJoin' for more details. {-# INLINE_NORMAL innerJoin #-} From 8fc960dd4997777e7a59fa605bdfaae6d3fd225c Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 14:09:10 +0530 Subject: [PATCH 10/20] Simplify functionMaybeM in Unfold --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index c19d2b07ac..b51a075268 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -60,7 +60,7 @@ module Streamly.Internal.Data.Unfold.Type , unfoldr , functionM , function - , functionMaybeM + , functionMaybeM -- XXX remove in favor of catMaybes functionM? , identity -- * From Values @@ -946,6 +946,8 @@ function f = functionM $ pure Prelude.. f -- | Lift a monadic Maybe returning function into an unfold. The unfold -- generates a singleton stream. -- +-- >>> functionMaybeM = Unfold.catMaybes . Unfold.functionM +-- {-# INLINE functionMaybeM #-} functionMaybeM :: Applicative m => (a -> m (Maybe b)) -> Unfold m a b functionMaybeM f = Unfold step Just @@ -953,10 +955,7 @@ functionMaybeM f = Unfold step Just where {-# INLINE_LATE step #-} - step (Just a) = - (\case - Just b -> Yield b Nothing - Nothing -> Stop) <$> f a + step (Just a) = maybe Stop (`Yield` Nothing) <$> f a step Nothing = pure Stop -- | Identity unfold. The unfold generates a singleton stream having the input From b2f273f9885c10fec5c245dd3831520fb3c71ea0 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 17:40:34 +0530 Subject: [PATCH 11/20] Add postscanl in the Unfold module --- core/src/Streamly/Internal/Data/Unfold.hs | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index b88ac2babc..2237ab204f 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -61,7 +61,7 @@ module Streamly.Internal.Data.Unfold -- ** Mapping on Output , postscanlM' - , postscan + , postscanl , scanl , scanlMany , foldMany @@ -112,6 +112,7 @@ module Streamly.Internal.Data.Unfold , handle -- ** Deprecated + , postscan , scan , scanMany ) @@ -134,6 +135,7 @@ import Streamly.Internal.Data.SVar.Type (defState) import qualified Control.Monad.Catch as MC import qualified Data.Tuple as Tuple import qualified Streamly.Internal.Data.Fold.Type as FL +import qualified Streamly.Internal.Data.Scanl.Type as Scanl import qualified Streamly.Internal.Data.Stream.Type as D import qualified Streamly.Internal.Data.StreamK.Type as K import qualified Prelude @@ -318,12 +320,14 @@ either (Unfold stepL injectL) (Unfold stepR injectR) = Unfold step inject -- postscan2 :: Monad m => Refold m a b c -> Unfold m a b -> Unfold m a c --- | Scan the output of an 'Unfold' to change it in a stateful manner. +-- | Scan the output of an 'Unfold' to change it in a stateful manner, using a +-- 'Scanl' instead of a 'Fold'. The initial value of the scan is not emitted in +-- the output. -- -- /Pre-release/ -{-# INLINE_NORMAL postscan #-} -postscan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c -postscan (Fold stepF initial extract final) (Unfold stepU injectU) = +{-# INLINE_NORMAL postscanl #-} +postscanl :: Monad m => Scanl m b c -> Unfold m a b -> Unfold m a c +postscanl (Scanl stepF initial extract final) (Unfold stepU injectU) = mkUnfoldM step inject where @@ -350,6 +354,12 @@ postscan (Fold stepF initial extract final) (Unfold stepU injectU) = step Nothing = return Stop +-- When we remove extract from Fold this function should be removed. +{-# DEPRECATED postscan "Please use postscanl instead" #-} +{-# INLINE_NORMAL postscan #-} +postscan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c +postscan (Fold s i e f) = postscanl (Scanl s i e f) + data ScanState s f = ScanInit s | ScanDo s !f | ScanDone {-# INLINE_NORMAL scanWith #-} @@ -425,7 +435,7 @@ scan (Fold s i e f) = scanWith False (Scanl s i e f) -- /Pre-release/ {-# INLINE_NORMAL postscanlM' #-} postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b -postscanlM' f z = postscan (FL.foldlM' f z) +postscanlM' f z = postscanl (Scanl.scanlM' f z) ------------------------------------------------------------------------------- -- Convert streams into unfolds From 73a5b45618e1276ad1fb1413e6cafa8b0ad42992 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 17:54:07 +0530 Subject: [PATCH 12/20] Deprecate postscanlM' --- core/src/Streamly/Internal/Data/Unfold.hs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index 2237ab204f..a3fe0d9a8b 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -60,7 +60,6 @@ module Streamly.Internal.Data.Unfold , fold -- ** Mapping on Output - , postscanlM' , postscanl , scanl , scanlMany @@ -113,6 +112,7 @@ module Streamly.Internal.Data.Unfold -- ** Deprecated , postscan + , postscanlM' , scan , scanMany ) @@ -430,12 +430,10 @@ scanl = scanWith False scan :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c scan (Fold s i e f) = scanWith False (Scanl s i e f) --- | Scan the output of an 'Unfold' to change it in a stateful manner. --- --- /Pre-release/ +{-# DEPRECATED postscanlM' "Please use \"postscanl (Scanl.scanlM' f z)\" instead" #-} {-# INLINE_NORMAL postscanlM' #-} postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Unfold m c a -> Unfold m c b -postscanlM' f z = postscanl (Scanl.scanlM' f z) +postscanlM' f z = postscanl (Scanl.mkScanlM f z) ------------------------------------------------------------------------------- -- Convert streams into unfolds From 9c13aea3863ef80866d3f734ff9b214951a30eab Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 23:21:41 +0530 Subject: [PATCH 13/20] Add comment about naming of lmap specializations --- core/src/Streamly/Internal/Data/Unfold.hs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index a3fe0d9a8b..30f6af01c6 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -50,6 +50,10 @@ module Streamly.Internal.Data.Unfold -- * Combinators -- ** Mapping on Input + + -- A named lmap specialization earns its name only if it's more + -- forward-thinkable than lmap f itself. + , discardFirst , discardSecond , swap From 48d6392dc65ceb316b314223b0dc75598902e362 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 18:29:47 +0530 Subject: [PATCH 14/20] Fix Category and Arrow instances for Unfold --- .../src/Streamly/Internal/Data/Unfold/Type.hs | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index b51a075268..658fb38fa6 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -129,13 +129,13 @@ where #include "deprecation.h" #include "inline.hs" --- import Control.Arrow (Arrow(..)) --- import Control.Category (Category(..)) +-- import Control.Arrow (Arrow(arr, (***))) +import Control.Category (Category(id, (.))) import Data.Void (Void) import Fusion.Plugin.Types (Fuse(..)) import Streamly.Internal.Data.Stream.Step (Step(..)) -import Prelude hiding (map, mapM, concatMap, zipWith, takeWhile) +import Prelude hiding (id, (.), map, mapM, concatMap, zipWith, takeWhile) #include "DocTestDataUnfold.hs" @@ -304,7 +304,7 @@ unfoldr step = unfoldrM (pure . step) -- {-# INLINE_NORMAL lmap #-} lmap :: (a -> c) -> Unfold m c b -> Unfold m a b -lmap f (Unfold ustep uinject) = Unfold ustep (uinject Prelude.. f) +lmap f (Unfold ustep uinject) = Unfold ustep (uinject . f) -- | State used by 'lmapM' to defer the monadic input transformation to the -- first step. @@ -593,7 +593,7 @@ fromEffect m = Unfold step inject -- /Pre-release/ {-# INLINE fromPure #-} fromPure :: Applicative m => b -> Unfold m a b -fromPure = fromEffect Prelude.. pure +fromPure = fromEffect . pure data TupleState a = TupleBoth a a | TupleOne a | TupleNone @@ -879,7 +879,7 @@ concatMapM f (Unfold step1 inject1) = Unfold step inject {-# INLINE concatMap #-} concatMap :: Monad m => (b -> Unfold m a c) -> Unfold m a b -> Unfold m a c -concatMap f = concatMapM (return Prelude.. f) +concatMap f = concatMapM (return . f) infixl 1 `bind` @@ -941,7 +941,7 @@ functionM f = Unfold step Just -- {-# INLINE function #-} function :: Applicative m => (a -> b) -> Unfold m a b -function f = functionM $ pure Prelude.. f +function f = functionM $ pure . f -- | Lift a monadic Maybe returning function into an unfold. The unfold -- generates a singleton stream. @@ -961,12 +961,12 @@ functionMaybeM f = Unfold step Just -- | Identity unfold. The unfold generates a singleton stream having the input -- as the only element. -- --- > identity = function Prelude.id +-- > identity = function id -- -- /Pre-release/ {-# INLINE identity #-} identity :: Applicative m => Unfold m a a -identity = function Prelude.id +identity = function id {-# ANN type ConcatState Fuse #-} data ConcatState s1 s2 = ConcatOuter s1 | ConcatInner s1 s2 @@ -1001,8 +1001,8 @@ unfoldEach (Unfold step2 inject2) (Unfold step1 inject1) = Unfold step inject RENAME(many,unfoldEach) {- --- XXX There are multiple possible ways to combine the unfolds, "many" appends --- them, we could also have other variants of "many" e.g. manyInterleave. +-- XXX There are multiple possible ways to combine the unfolds, "unfoldEach" +-- appends them, we could also have other variants e.g. unfoldEachInterleave. -- Should we even have a category instance or just use these functions -- directly? -- @@ -1011,7 +1011,7 @@ instance Monad m => Category (Unfold m) where id = identity {-# INLINE (.) #-} - (.) = many + (.) = unfoldEach -} ------------------------------------------------------------------------------- @@ -1118,24 +1118,27 @@ zipArrowWith f = zipArrowWithM (\a b -> return (f a b)) -- could zip, merge, append and more. What is the preferred way for Arrow -- instance? Should we even have an arrow instance or just use these functions -- directly? + +-- | '***' splits the input tuple between the two unfolds and zips their +-- outputs (same as @Unfold.zipArrowWith (,)@). The default '&&&' distributes +-- the same input to both unfolds and zips their outputs (same as +-- @Unfold.zipWith (,)@). -- --- | '***' is a zip like operation, in fact it is the same as @Unfold.zipWith --- (,)@, '&&&' is a tee like operation i.e. distributes the input to both the --- unfolds and then zips the output. --- -{-# ANN module "HLint: ignore Use zip" #-} instance Monad m => Arrow (Unfold m) where {-# INLINE arr #-} arr = function {-# INLINE (***) #-} - u1 *** u2 = zipWith (,) (lmap fst u1) (lmap snd u2) + (***) = zipArrowWith (,) -} ------------------------------------------------------------------------------ -- Interleaving ------------------------------------------------------------------------------ +-- XXX If we have interleave, we can have append as well and all binary +-- operations that streams have. + -- We can possibly have an "interleave" operation to interleave the streams -- from two seeds: -- From 8d37a2a31cd50f00d60506cb932fe7ecd9a436a9 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Wed, 27 May 2026 16:07:43 +0530 Subject: [PATCH 15/20] Increase heap to build Data.Array.Generic benchmark --- benchmark/streamly-benchmarks.cabal | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/benchmark/streamly-benchmarks.cabal b/benchmark/streamly-benchmarks.cabal index f096085952..04bfb191b3 100644 --- a/benchmark/streamly-benchmarks.cabal +++ b/benchmark/streamly-benchmarks.cabal @@ -260,8 +260,8 @@ benchmark Data.Array.Generic buildable: False else buildable: True - if flag(limit-build-mem) && !flag(fusion-plugin) - ghc-options: +RTS -M500M -RTS + if flag(limit-build-mem) + ghc-options: +RTS -M600M -RTS benchmark Data.Array.Stream import: bench-options From e746bf33775642bb0e2a02a49b4eee85bba2d754 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Thu, 28 May 2026 19:09:41 +0530 Subject: [PATCH 16/20] Fix Unfold.postscan deprecation warning in tests --- test/Streamly/Test/Data/Unfold.hs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/Streamly/Test/Data/Unfold.hs b/test/Streamly/Test/Data/Unfold.hs index fd7bdf7ae9..476bb95b51 100644 --- a/test/Streamly/Test/Data/Unfold.hs +++ b/test/Streamly/Test/Data/Unfold.hs @@ -14,6 +14,7 @@ import Streamly.Internal.Data.Unfold (Unfold) import qualified Data.List as List import qualified Prelude import qualified Streamly.Internal.Data.Fold as Fold +import qualified Streamly.Internal.Data.Scanl as Scanl import qualified Streamly.Internal.Data.Unfold as UF import qualified Streamly.Internal.Data.Stream as S import qualified Streamly.Internal.Data.Stream as D @@ -459,7 +460,7 @@ postscan :: Property postscan = property $ \(ls :: [Int]) -> - let unf = UF.postscan Fold.sum UF.fromList + let unf = UF.postscanl Scanl.sum UF.fromList mList = scanl1 (+) ls in testUnfold unf ls mList From b6c7ff7b4dba27344de0b8eaba12af1d5f9465d7 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Thu, 28 May 2026 19:25:22 +0530 Subject: [PATCH 17/20] Deprecate "swap", rename "first", "second", "carry" --- core/src/Streamly/Data/Unfold.hs | 9 ++- core/src/Streamly/Internal/Data/MutArray.hs | 4 +- .../src/Streamly/Internal/Data/Stream/Type.hs | 2 +- core/src/Streamly/Internal/Data/Unfold.hs | 7 ++- .../src/Streamly/Internal/Data/Unfold/Type.hs | 61 +++++++++++-------- .../src/Streamly/Internal/FileSystem/DirIO.hs | 2 +- .../Streamly/Internal/FileSystem/Handle.hs | 4 +- src/Streamly/Internal/Network/Inet/TCP.hs | 10 +-- src/Streamly/Internal/Network/Socket.hs | 4 +- 9 files changed, 58 insertions(+), 45 deletions(-) diff --git a/core/src/Streamly/Data/Unfold.hs b/core/src/Streamly/Data/Unfold.hs index 6d0c0aa974..6b417183bc 100644 --- a/core/src/Streamly/Data/Unfold.hs +++ b/core/src/Streamly/Data/Unfold.hs @@ -65,9 +65,9 @@ module Streamly.Data.Unfold -- ** Mapping on Input , lmap , lmapM - , first - , second - , carry + , supplyFirst + , supplySecond + , carryInput -- ** Mapping on Output , mapM @@ -93,6 +93,9 @@ module Streamly.Data.Unfold -- * Deprecated , many + , first + , second + , carry ) where diff --git a/core/src/Streamly/Internal/Data/MutArray.hs b/core/src/Streamly/Internal/Data/MutArray.hs index 95961ec0dd..110286bb15 100644 --- a/core/src/Streamly/Internal/Data/MutArray.hs +++ b/core/src/Streamly/Internal/Data/MutArray.hs @@ -97,7 +97,7 @@ indexerFromLen from len = let fromThenTo n = (from, from + len, n - 1) mkSlice n i = return (i, min len (n - i)) in Unfold.lmap length - $ Unfold.mapM (uncurry mkSlice) . Unfold.carry + $ Unfold.mapM (uncurry mkSlice) . Unfold.carryInput $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo RENAME(sliceIndexerFromLen,indexerFromLen) @@ -121,7 +121,7 @@ splitterFromLen, slicerFromLen :: forall m a. (Monad m, Unbox a) splitterFromLen from len = let mkSlice arr (i, n) = return $ unsafeSliceOffLen i n arr in Unfold.mapM (uncurry mkSlice) - $ Unfold.carry (indexerFromLen from len) + $ Unfold.carryInput (indexerFromLen from len) RENAME(slicerFromLen,splitterFromLen) {-# DEPRECATED getSlicesFromLen "Please use splitterFromLen instead." #-} diff --git a/core/src/Streamly/Internal/Data/Stream/Type.hs b/core/src/Streamly/Internal/Data/Stream/Type.hs index d0515f677c..6003202215 100644 --- a/core/src/Streamly/Internal/Data/Stream/Type.hs +++ b/core/src/Streamly/Internal/Data/Stream/Type.hs @@ -1517,7 +1517,7 @@ loop = crossWith (\b a -> (a,b)) loopBy :: Monad m => Unfold m x b -> x -> Stream m a -> Stream m (a, b) loopBy u x s = let u1 = Unfold.lmap snd u - u2 = Unfold.map (first fst) (Unfold.carry u1) + u2 = Unfold.map (first fst) (Unfold.carryInput u1) in unfoldEach u2 $ fmap (, x) s ------------------------------------------------------------------------------ diff --git a/core/src/Streamly/Internal/Data/Unfold.hs b/core/src/Streamly/Internal/Data/Unfold.hs index 30f6af01c6..32b3ae4be4 100644 --- a/core/src/Streamly/Internal/Data/Unfold.hs +++ b/core/src/Streamly/Internal/Data/Unfold.hs @@ -54,9 +54,8 @@ module Streamly.Internal.Data.Unfold -- A named lmap specialization earns its name only if it's more -- forward-thinkable than lmap f itself. - , discardFirst - , discardSecond - , swap + , discardFirst -- asSecond + , discardSecond -- asFirst -- coapply -- comonad @@ -119,6 +118,7 @@ module Streamly.Internal.Data.Unfold , postscanlM' , scan , scanMany + , swap ) where @@ -196,6 +196,7 @@ discardSecond = lmap fst -- -- /Pre-release/ -- +{-# DEPRECATED swap "Please use \"lmap Tuple.swap\" instead" #-} {-# INLINE_NORMAL swap #-} swap :: Unfold m (a, c) b -> Unfold m (c, a) b swap = lmap Tuple.swap diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index 658fb38fa6..b10cebb47e 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -72,14 +72,14 @@ module Streamly.Internal.Data.Unfold.Type , fromTuple -- * Transformations - , lmap - , lmapM + , lmap -- XXX plug + , lmapM -- XXX plugM , map , mapM - , supply -- input or useInput - , first -- asFirst - , second --asSecond - , carry -- XXX carryInput? + , supply + , supplyFirst + , supplySecond + , carryInput , consInput , consInputWith @@ -123,6 +123,9 @@ module Streamly.Internal.Data.Unfold.Type , mapM2 , takeWhileMWithInput , both + , first + , second + , carry ) where @@ -361,39 +364,43 @@ both a = lmap (Prelude.const a) -- as a seed. -- -- @ --- first a = Unfold.lmap (a, ) +-- supplyFirst a = Unfold.lmap (a, ) -- @ -- -- /Pre-release/ -- -{-# INLINE_NORMAL first #-} -first :: a -> Unfold m (a, b) c -> Unfold m b c -first a = lmap (a, ) +{-# INLINE_NORMAL supplyFirst #-} +supplyFirst, first :: a -> Unfold m (a, b) c -> Unfold m b c +supplyFirst a = lmap (a, ) + +RENAME(first,supplyFirst) -- | Supply the second component of the tuple to an unfold that accepts a tuple -- as a seed resulting in a fold that accepts the first component of the tuple -- as a seed. -- -- @ --- second b = Unfold.lmap (, b) +-- supplySecond b = Unfold.lmap (, b) -- @ -- -- /Pre-release/ -- -{-# INLINE_NORMAL second #-} -second :: b -> Unfold m (a, b) c -> Unfold m a c -second b = lmap (, b) +{-# INLINE_NORMAL supplySecond #-} +supplySecond, second :: b -> Unfold m (a, b) c -> Unfold m a c +supplySecond b = lmap (, b) + +RENAME(second,supplySecond) ------------------------------------------------------------------------------ -- Filter input ------------------------------------------------------------------------------ -- | --- >>> takeWhileMWithInput f u = Unfold.map snd $ Unfold.takeWhileM (\(a,b) -> f a b) (Unfold.carry u) +-- >>> takeWhileMWithInput f u = Unfold.map snd $ Unfold.takeWhileM (\(a,b) -> f a b) (Unfold.carryInput u) {-# INLINE_NORMAL takeWhileMWithInput #-} takeWhileMWithInput :: Monad m => (a -> b -> m Bool) -> Unfold m a b -> Unfold m a b -takeWhileMWithInput f u = map snd $ takeWhileM (\(a,b) -> f a b) (carry u) +takeWhileMWithInput f u = map snd $ takeWhileM (\(a,b) -> f a b) (carryInput u) {- takeWhileMWithInput f (Unfold step1 inject1) = Unfold step inject @@ -445,10 +452,10 @@ takeWhile f = takeWhileM (return . f) -- Functor ------------------------------------------------------------------------------ -{-# DEPRECATED mapM2 "Use carry with mapM instead." #-} +{-# DEPRECATED mapM2 "Use carryInput with mapM instead." #-} {-# INLINE_NORMAL mapM2 #-} mapM2 :: Monad m => (a -> b -> m c) -> Unfold m a b -> Unfold m a c -mapM2 f = mapM (uncurry f) . carry +mapM2 f = mapM (uncurry f) . carryInput {- mapM2 f (Unfold ustep uinject) = Unfold step inject where @@ -484,15 +491,15 @@ mapM f (Unfold ustep uinject) = Unfold step uinject -- | Carry the input along with the output as the first element of the output -- tuple. -- --- carry = Unfold.lmap (\x -> (x,x)) . Unfold.zipRepeat +-- carryInput = Unfold.lmap (\x -> (x,x)) . Unfold.zipRepeat -- -- Note that the input seed may mutate (e.g. if the seed is a Handle or IORef) -- as stream is generated from it, so we need to be careful when reusing the -- seed while the stream is being generated from it. -- -{-# INLINE_NORMAL carry #-} -carry :: Functor m => Unfold m a b -> Unfold m a (a,b) -carry (Unfold ustep uinject) = Unfold step (\a -> (a, uinject a)) +{-# INLINE_NORMAL carryInput #-} +carryInput, carry :: Functor m => Unfold m a b -> Unfold m a (a,b) +carryInput (Unfold ustep uinject) = Unfold step (\a -> (a, uinject a)) where @@ -505,6 +512,8 @@ carry (Unfold ustep uinject) = Unfold step (\a -> (a, uinject a)) {-# INLINE_LATE step #-} step (a, st) = fmap (func a) (ustep st) +RENAME(carry,carryInput) + {-# ANN type ConsInputState Fuse #-} data ConsInputState a s = ConsInputFirst a s | ConsInputRest s @@ -539,10 +548,10 @@ consInputWith f (Unfold ustep uinject) = Unfold step inject consInput :: Applicative m => Unfold m a a -> Unfold m a a consInput = consInputWith id -{-# DEPRECATED map2 "Use carry with map instead." #-} +{-# DEPRECATED map2 "Use carryInput with map instead." #-} {-# INLINE_NORMAL map2 #-} map2 :: Functor m => (a -> b -> c) -> Unfold m a b -> Unfold m a c -map2 f = map (uncurry f) . carry +map2 f = map (uncurry f) . carryInput -- | Map a function on the output of the unfold (the type @b@). -- @@ -648,11 +657,11 @@ crossApplyFst (Unfold _step1 _inject1) (Unfold _step2 _inject2) = undefined data Many2State x s1 s2 = Many2Outer x s1 | Many2Inner x s1 s2 -} -{-# DEPRECATED many2 "Use carry with unfoldEach instead." #-} +{-# DEPRECATED many2 "Use carryInput with unfoldEach instead." #-} {-# INLINE_NORMAL many2 #-} many2 :: Monad m => Unfold m (a, b) c -> Unfold m a b -> Unfold m a c -many2 u1 u2 = unfoldEach u1 (carry u2) +many2 u1 u2 = unfoldEach u1 (carryInput u2) {- unfoldEach2 (Unfold step2 inject2) (Unfold step1 inject1) = Unfold step inject diff --git a/core/src/Streamly/Internal/FileSystem/DirIO.hs b/core/src/Streamly/Internal/FileSystem/DirIO.hs index d3a738e1bc..53bb2a146f 100644 --- a/core/src/Streamly/Internal/FileSystem/DirIO.hs +++ b/core/src/Streamly/Internal/FileSystem/DirIO.hs @@ -243,7 +243,7 @@ eitherReaderPaths ::(MonadIO m, MonadCatch m) => (ReadOptions -> ReadOptions) -> eitherReaderPaths f = let () = Path.join in fmap (\(dir, x) -> bimap (dir ) (dir ) x) - $ UF.carry (OS.eitherReader f) + $ UF.carryInput (OS.eitherReader f) -- -- | Read files only. diff --git a/core/src/Streamly/Internal/FileSystem/Handle.hs b/core/src/Streamly/Internal/FileSystem/Handle.hs index 2c2a752a10..a03fcb80be 100644 --- a/core/src/Streamly/Internal/FileSystem/Handle.hs +++ b/core/src/Streamly/Internal/FileSystem/Handle.hs @@ -304,11 +304,11 @@ readChunks = readChunksWith defaultChunkSize -- size of arrays in the resulting stream are therefore less than or equal to -- 'Streamly.Internal.Data.Array.Type.defaultChunkSize'. -- --- >>> chunkReader = Unfold.first IO.defaultChunkSize Handle.chunkReaderWith +-- >>> chunkReader = Unfold.supplyFirst IO.defaultChunkSize Handle.chunkReaderWith -- {-# INLINE chunkReader #-} chunkReader :: MonadIO m => Unfold m Handle (Array Word8) -chunkReader = UF.first defaultChunkSize chunkReaderWith +chunkReader = UF.supplyFirst defaultChunkSize chunkReaderWith ------------------------------------------------------------------------------- -- Read File to Stream diff --git a/src/Streamly/Internal/Network/Inet/TCP.hs b/src/Streamly/Internal/Network/Inet/TCP.hs index e55f014f37..61fd14043c 100644 --- a/src/Streamly/Internal/Network/Inet/TCP.hs +++ b/src/Streamly/Internal/Network/Inet/TCP.hs @@ -182,17 +182,17 @@ acceptorOnAddr = acceptorOnAddrWith [] acceptorWith :: MonadIO m => [(SocketOption, Int)] -> Unfold m PortNumber Socket -acceptorWith opts = UF.first (0,0,0,0) (acceptorOnAddrWith opts) +acceptorWith opts = UF.supplyFirst (0,0,0,0) (acceptorOnAddrWith opts) -- | Like 'acceptorOnAddr' but binds on the IPv4 address @0.0.0.0@ i.e. on all -- IPv4 addresses/interfaces of the machine and listens for TCP connections on -- the specified port. -- --- >>> acceptor = Unfold.first (0,0,0,0) TCP.acceptorOnAddr +-- >>> acceptor = Unfold.supplyFirst (0,0,0,0) TCP.acceptorOnAddr -- {-# INLINE acceptor #-} acceptor :: MonadIO m => Unfold m PortNumber Socket -acceptor = UF.first (0,0,0,0) acceptorOnAddr +acceptor = UF.supplyFirst (0,0,0,0) acceptorOnAddr {-# DEPRECATED acceptorOnPort "Use \"acceptor\" instead." #-} {-# INLINE acceptorOnPort #-} @@ -203,11 +203,11 @@ acceptorOnPort = acceptor -- server can only be accessed from the local host, it cannot be accessed from -- other hosts on the network. -- --- >>> acceptorLocal = Unfold.first (127,0,0,1) TCP.acceptorOnAddr +-- >>> acceptorLocal = Unfold.supplyFirst (127,0,0,1) TCP.acceptorOnAddr -- {-# INLINE acceptorLocal #-} acceptorLocal :: MonadIO m => Unfold m PortNumber Socket -acceptorLocal = UF.first (127,0,0,1) acceptorOnAddr +acceptorLocal = UF.supplyFirst (127,0,0,1) acceptorOnAddr {-# DEPRECATED acceptorOnPortLocal "Use \"acceptorLocal\" instead." #-} {-# INLINE acceptorOnPortLocal #-} diff --git a/src/Streamly/Internal/Network/Socket.hs b/src/Streamly/Internal/Network/Socket.hs index 9676fc0540..6aaffb0d87 100644 --- a/src/Streamly/Internal/Network/Socket.hs +++ b/src/Streamly/Internal/Network/Socket.hs @@ -390,7 +390,7 @@ readChunksWithBufferOf = chunkReaderWith -- {-# INLINE chunkReader #-} chunkReader :: MonadIO m => Unfold m Socket (Array Word8) -chunkReader = UF.first defaultChunkSize chunkReaderWith +chunkReader = UF.supplyFirst defaultChunkSize chunkReaderWith ------------------------------------------------------------------------------- -- Read File to Stream @@ -436,7 +436,7 @@ readWithBufferOf = readerWith -- {-# INLINE reader #-} reader :: MonadIO m => Unfold m Socket Word8 -reader = UF.first defaultChunkSize readerWith +reader = UF.supplyFirst defaultChunkSize readerWith ------------------------------------------------------------------------------- -- Writing From a268d7aad58d6c92805d40097c0531a4d48562f1 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Thu, 28 May 2026 20:09:06 +0530 Subject: [PATCH 18/20] Deprecate takeWhileMWithInput --- core/src/Streamly/Internal/Data/Unfold/Type.hs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/Streamly/Internal/Data/Unfold/Type.hs b/core/src/Streamly/Internal/Data/Unfold/Type.hs index b10cebb47e..b9eef9b219 100644 --- a/core/src/Streamly/Internal/Data/Unfold/Type.hs +++ b/core/src/Streamly/Internal/Data/Unfold/Type.hs @@ -397,6 +397,7 @@ RENAME(second,supplySecond) -- | -- >>> takeWhileMWithInput f u = Unfold.map snd $ Unfold.takeWhileM (\(a,b) -> f a b) (Unfold.carryInput u) +{-# DEPRECATED takeWhileMWithInput "Use \"map snd . takeWhileM (uncurry f) . carryInput\" instead." #-} {-# INLINE_NORMAL takeWhileMWithInput #-} takeWhileMWithInput :: Monad m => (a -> b -> m Bool) -> Unfold m a b -> Unfold m a b From c0ea22f6531181317840dbb0762c1af2119947da Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Thu, 28 May 2026 18:38:02 +0530 Subject: [PATCH 19/20] Add a FreeBSD CI based on github vmactions --- .github/workflows/freebsd.yml | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 .github/workflows/freebsd.yml diff --git a/.github/workflows/freebsd.yml b/.github/workflows/freebsd.yml new file mode 100644 index 0000000000..b901119a6c --- /dev/null +++ b/.github/workflows/freebsd.yml @@ -0,0 +1,89 @@ +# FreeBSD CI +# +# FreeBSD is not a native GitHub Actions runner, so this workflow runs +# the build inside a VM via vmactions/freebsd-vm. It is kept separate +# from haskell.yml because the cache/restore step model in that +# workflow (hackage index, ghcup, deps caches) does not apply inside +# the VM. + +name: FREEBSD + +on: + workflow_dispatch: + pull_request: + push: + branches: + - master + +jobs: + build: + name: >- + freebsd-${{ matrix.release }} + ${{ matrix.command }} + ${{ matrix.ghc_version }} + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.ignore_error }} + strategy: + fail-fast: false + matrix: + include: + - release: "14.3" + command: cabal + ghc_version: 9.14.1 + ignore_error: true + + steps: + - uses: actions/checkout@v4 + + - name: Build on FreeBSD + uses: vmactions/freebsd-vm@v1 + env: + PACKCHECK_COMMAND: ${{ matrix.command }} + GHCVER: ${{ matrix.ghc_version }} + # For updating see: https://downloads.haskell.org/~ghcup/ + GHCUP_VERSION: 0.1.50.2 + LC_ALL: C.UTF-8 + CABAL_REINIT_CONFIG: y + CABAL_CHECK_RELAX: y + CABAL_PROJECT: cabal.project + DISABLE_BENCH: "y" + DISABLE_DOCS: "y" + DISABLE_SDIST_BUILD: "y" + DISABLE_DIST_CHECKS: "y" + PACKCHECK: "./packcheck.sh" + PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" + PACKCHECK_GITHUB_COMMIT: "b3743510c7c26f83254ffd9ef91bcd71560cff05" + with: + release: ${{ matrix.release }} + usesh: true + copyback: false + envs: >- + PACKCHECK_COMMAND GHCVER GHCUP_VERSION LC_ALL + CABAL_REINIT_CONFIG CABAL_CHECK_RELAX CABAL_PROJECT + DISABLE_BENCH DISABLE_DOCS DISABLE_SDIST_BUILD DISABLE_DIST_CHECKS + PACKCHECK PACKCHECK_GITHUB_URL PACKCHECK_GITHUB_COMMIT + prepare: | + pkg update + pkg install -y gmake + pkg install -y bash + pkg install -y git + pkg install -y gmp + run: | + if test ! -e "$PACKCHECK" + then + if test -z "$PACKCHECK_GITHUB_COMMIT" + then + echo "PACKCHECK_GITHUB_COMMIT is not specified." >&2 + exit 1 + fi + PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh + curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1 + chmod +x $PACKCHECK + elif test ! -x "$PACKCHECK" + then + chmod +x $PACKCHECK + fi + # Use "bash -c" instead of invoking directly to preserve quoted + # arguments in PACKCHECK_COMMAND e.g. DOCSPEC_OPTIONS="--timeout 60". + # Direct invocation would word-split on spaces inside quoted values. + bash -c "$PACKCHECK $PACKCHECK_COMMAND" From a3a4b89eb907e249eaa03cb917949d8689194fa4 Mon Sep 17 00:00:00 2001 From: Harendra Kumar Date: Thu, 28 May 2026 18:53:06 +0530 Subject: [PATCH 20/20] Remove cirrus CI --- .cirrus.yml | 106 ---------------------------------------------------- 1 file changed, 106 deletions(-) delete mode 100644 .cirrus.yml diff --git a/.cirrus.yml b/.cirrus.yml deleted file mode 100644 index 8790f21485..0000000000 --- a/.cirrus.yml +++ /dev/null @@ -1,106 +0,0 @@ -freebsd_instance: - image_family: freebsd-14-3 - -task: - name: FreeBSD+ghc-9.14.1+cabal - env: - PACKCHECK_COMMAND: cabal - - # ------------------------------------------------------------------------ - # Common options - # ------------------------------------------------------------------------ - CABAL_REINIT_CONFIG: y - LC_ALL: C.UTF-8 - - # ------------------------------------------------------------------------ - # What to build - # ------------------------------------------------------------------------ - # DISABLE_TEST: "y" - DISABLE_BENCH: "y" - DISABLE_DOCS: "y" - DISABLE_SDIST_BUILD: "y" - # DISABLE_SDIST_GIT_CHECK: "y" - DISABLE_DIST_CHECKS: "y" - - # ------------------------------------------------------------------------ - # Selecting tool versions - # ------------------------------------------------------------------------ - # For updating see: https://downloads.haskell.org/~ghcup/ - GHCUP_VERSION: 0.1.50.2 - GHCVER: 9.14.1 - - # ------------------------------------------------------------------------ - # stack options (if using stack builds) - # ------------------------------------------------------------------------ - # Note requiring a specific version of stack using STACKVER may fail due to - # github API limit while checking and upgrading/downgrading to the specific - # version. - #STACKVER: "1.6.5" - #STACK_UPGRADE: "y" - #STACK_YAML: "stack.yaml" - - # ------------------------------------------------------------------------ - # cabal options - # ------------------------------------------------------------------------ - CABAL_CHECK_RELAX: y - CABAL_PROJECT: cabal.project - - # ------------------------------------------------------------------------ - # Location of packcheck.sh (the shell script invoked to perform CI tests ). - # ------------------------------------------------------------------------ - # You can either commit the packcheck.sh script at this path in your repo or - # you can use it by specifying the PACKCHECK_REPO_URL option below in which - # case it will be automatically copied from the packcheck repo to this path - # during CI tests. In any case it is finally invoked from this path. - PACKCHECK: "./packcheck.sh" - # If you have not committed packcheck.sh in your repo at PACKCHECK - # then it is automatically pulled from this URL. - PACKCHECK_GITHUB_URL: "https://raw.githubusercontent.com/composewell/packcheck" - PACKCHECK_GITHUB_COMMIT: "b3743510c7c26f83254ffd9ef91bcd71560cff05" - - cabal_cache: - folder: ~/.cabal - fingerprint_script: echo $GHCVER - - # Cabal store is in .local/state/cabal - local_cache: - folder: ~/.local - fingerprint_script: echo $GHCVER - - ghcup_cache: - folder: ~/.ghcup - fingerprint_script: echo $GHCUP_VERSION $GHCVER - - #local_bin_cache: - # folder: ~/.local/bin - # fingerprint_script: echo $HLINT_VERSION - - # git is required for cabal files with git URLs - deps_install_script: | - pkg update - pkg install -y gmake - pkg install -y bash - pkg install -y git - - packcheck_install_script: | - if test ! -e "$PACKCHECK" - then - if test -z "$PACKCHECK_GITHUB_COMMIT" - then - die "PACKCHECK_GITHUB_COMMIT is not specified." - fi - PACKCHECK_URL=${PACKCHECK_GITHUB_URL}/${PACKCHECK_GITHUB_COMMIT}/packcheck.sh - curl --fail -sL -o "$PACKCHECK" $PACKCHECK_URL || exit 1 - chmod +x $PACKCHECK - elif test ! -x "$PACKCHECK" - then - chmod +x $PACKCHECK - fi - - packcheck_run_script: | - # Commands like mount, sysctl for info require sbin - # PTH=/usr/local/bin:/usr/bin:/bin:/sbin:/usr/sbin - # Use "bash -c" instead of invoking directly to preserve quoted - # arguments in PACKCHECK_COMMAND e.g. DOCSPEC_OPTIONS="--timeout 60". - # Direct invocation would word-split on spaces inside quoted values. - bash -c "$PACKCHECK $PACKCHECK_COMMAND"