packages feed

streamly-core 0.1.0 → 0.2.0

raw patch · 145 files changed

+37902/−37143 lines, 145 filesdep +Win32dep +ghc-bignumdep +integer-gmpdep ~basedep ~containersdep ~filepathnew-uploader

Dependencies added: Win32, ghc-bignum, integer-gmp, unix

Dependency ranges changed: base, containers, filepath, ghc-prim, template-haskell

Files

Changelog.md view
@@ -1,8 +1,59 @@ # Changelog +## 0.2.0 (Nov 2023)++See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)+for a full list of API changes in this release. Only a few significant+changes are mentioned here.++### Breaking Changes++* `ParserK` in `Streamly.Data.ParserK` is not implicitly specialized+  to arrays anymore. To adapt to the new code, change `ParserK a m+  b` to `ParserK (Array a) m b` where the `Array` type comes from+  `Streamly.Data.Array`. This change also affected the signatures of+  `parseChunks` and `parseBreakChunks`.+* Changed the signature of 'Streamly.Data.Stream.handle' to make the+  exception handler monadic.+* Behavior change: Exceptions are now rethrown promptly in `bracketIO`.++### Enhancements++* __Serialization__: Added a `Streamly.Data.MutByteArray` module with a+  `Serialize` type class for fast binary serialization. The Data.Array+  module supplies the `serialize` and `deserialize` operations for arrays.+* __Unpinned Arrays__: Unboxed arrays are now created unpinned by default,+  they were created pinned earlier. During IO operations, unpinned arrays+  are automatically copied to pinned memory. When arrays are directly+  passed to IO operations programmers can choose to create them pinned to+  avoid a copy.  To create pinned arrays, use the internal APIs with the+  `pinned*` prefix.+* StreamK now supports native exception handling routines (handle, bracketIO).+  Earlier we had to convert it to the `Stream` type for exception handling.++### Deprecations++See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)+for a full list of deprecations.++### Internal API Changes++* Fold constructor has changed, added a `final` field to support+  finalization and cleanup of a chain of folds. The `extract` field is+  now used only for mapping the fold internal state to fold result for+  scanning purposes.+* Many low level internal modules have been removed, they are entirely+  exported from higher level internal modules. If you were importing any+  of the missing low level modules then import the higher level modules instead.+* Internal module changes:+  * Streamly.Internal.Serialize.FromBytes -> Streamly.Internal.Data.Binary.Parser+  * Streamly.Internal.Serialize.ToBytes ->   Streamly.Internal.Data.Binary.Stream+  * Streamly.Internal.Data.Unbox is now exported via Streamly.Internal.Data.Serialize+  * Streamly.Internal.Data.IORef.Unboxed is now exported via Streamly.Internal.Data.Serialize+ ## 0.1.0 (March 2023) -Also see [streamly-core-0.1.0 API Changelog](/core/docs/ApiChangelogs/0.1.0.txt) or+Also see [streamly-core-0.1.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0.txt) or https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt  `streamly` package is split into two packages, (1) `streamly-core` that
LICENSE view
@@ -279,3 +279,44 @@ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.++-------------------------------------------------------------------------------+th-utilities-0.2.5.0 (https://hackage.haskell.org/package/th-utilities)+-------------------------------------------------------------------------------+Copyright (c) 2016 FP Complete Corporation.++Permission is hereby granted, free of charge, to any person obtaining+a copy of this software and associated documentation files (the+"Software"), to deal in the Software without restriction, including+without limitation the rights to use, copy, modify, merge, publish,+distribute, sublicense, and/or sell copies of the Software, and to+permit persons to whom the Software is furnished to do so, subject to+the following conditions:++The above copyright notice and this permission notice shall be+included in all copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND+NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE+LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION+OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION+WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.++-------------------------------------------------------------------------------+th-abstraction-0.5.0.0 (https://hackage.haskell.org/package/th-abstraction)+-------------------------------------------------------------------------------+Copyright (c) 2017-2020 Eric Mertens++Permission to use, copy, modify, and/or distribute this software for any purpose+with or without fee is hereby granted, provided that the above copyright notice+and this permission notice appear in all copies.++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH+REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND+FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,+INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS+OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER+TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF+THIS SOFTWARE.
configure view
@@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles.-# Generated by GNU Autoconf 2.71 for streamly-core 0.1.0.+# Generated by GNU Autoconf 2.71 for streamly-core 0.2.0. # # Report bugs to <streamly@composewell.com>. #@@ -610,8 +610,8 @@ # Identity of this package. PACKAGE_NAME='streamly-core' PACKAGE_TARNAME='streamly-core'-PACKAGE_VERSION='0.1.0'-PACKAGE_STRING='streamly-core 0.1.0'+PACKAGE_VERSION='0.2.0'+PACKAGE_STRING='streamly-core 0.2.0' PACKAGE_BUGREPORT='streamly@composewell.com' PACKAGE_URL='https://streamly.composewell.com' @@ -1256,7 +1256,7 @@   # Omit some internal or obsolete options to make the list less imposing.   # This message is too long to be a string in the A/UX 3.1 sh.   cat <<_ACEOF-\`configure' configures streamly-core 0.1.0 to adapt to many kinds of systems.+\`configure' configures streamly-core 0.2.0 to adapt to many kinds of systems.  Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1318,7 +1318,7 @@  if test -n "$ac_init_help"; then   case $ac_init_help in-     short | recursive ) echo "Configuration of streamly-core 0.1.0:";;+     short | recursive ) echo "Configuration of streamly-core 0.2.0:";;    esac   cat <<\_ACEOF @@ -1404,7 +1404,7 @@ test -n "$ac_init_help" && exit $ac_status if $ac_init_version; then   cat <<\_ACEOF-streamly-core configure 0.1.0+streamly-core configure 0.2.0 generated by GNU Autoconf 2.71  Copyright (C) 2021 Free Software Foundation, Inc.@@ -1622,7 +1622,7 @@ This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by streamly-core $as_me 0.1.0, which was+It was created by streamly-core $as_me 0.2.0, which was generated by GNU Autoconf 2.71.  Invocation command line was    $ $0$ac_configure_args_raw@@ -3832,7 +3832,7 @@ # report actual input values of CONFIG_FILES etc. instead of their # values after options handling. ac_log="-This file was extended by streamly-core $as_me 0.1.0, which was+This file was extended by streamly-core $as_me 0.2.0, which was generated by GNU Autoconf 2.71.  Invocation command line was    CONFIG_FILES    = $CONFIG_FILES@@ -3888,7 +3888,7 @@ cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 ac_cs_config='$ac_cs_config_escaped' ac_cs_version="\\-streamly-core config.status 0.1.0+streamly-core config.status 0.2.0 configured by $0, generated by GNU Autoconf 2.71,   with options \\"\$ac_cs_config\\" 
configure.ac view
@@ -3,7 +3,7 @@ # See https://www.gnu.org/software/autoconf/manual/autoconf.html for help on # the macros used in this file. -AC_INIT([streamly-core], [0.1.0], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])+AC_INIT([streamly-core], [0.2.0], [streamly@composewell.com], [streamly-core], [https://streamly.composewell.com])  # To suppress "WARNING: unrecognized options: --with-compiler" AC_ARG_WITH([compiler], [GHC])
+ docs/ApiChangelogs/0.1.0-0.2.0.txt view
@@ -0,0 +1,1471 @@+---------------------------------+API Annotations+---------------------------------++[A] : Added+[R] : Removed+[C] : Changed+[O] : Old definition+[N] : New definition+[D] : Deprecated++---------------------------------+API diff+---------------------------------++[C] Streamly.Unicode.Parser+    [A] double :: Monad m => Parser Char m Double+[C] Streamly.FileSystem.Handle+    [A] readWith :: MonadIO m => Int -> Handle -> Stream m Word8+    [A] readChunksWith :: MonadIO m => Int -> Handle -> Stream m (Array Word8)+[C] Streamly.FileSystem.Dir+    [C] readEither+        [O] readEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+        [N] readEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)+    [C] read+        [O] read :: MonadIO m => FilePath -> Stream m FilePath+        [N] read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath+[C] Streamly.Data.Unfold+    [A] class Enum a => Enumerable a+    [A] second :: b -> Unfold m (a, b) c -> Unfold m a c+    [A] first :: a -> Unfold m (a, b) c -> Unfold m b c+    [A] enumerateFromTo :: (Enumerable a, Monad m) => Unfold m (a, a) a+    [A] enumerateFromThenTo :: (Enumerable a, Monad m) => Unfold m (a, a, a) a+    [A] enumerateFromThen :: (Enumerable a, Monad m) => Unfold m (a, a) a+    [A] enumerateFrom :: (Enumerable a, Monad m) => Unfold m a a+[C] Streamly.Data.StreamK+    [C] parseChunks+        [O] parseChunks :: (Monad m, Unbox a) => ParserK a m b -> StreamK m (Array a) -> m (Either ParseError b)+        [N] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)+    [C] parseBreakChunks+        [O] parseBreakChunks :: (Monad m, Unbox a) => ParserK a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+        [N] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] parseBreak :: forall m a b. Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)+    [A] parse :: Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b)+    [A] handle :: (MonadCatch m, Exception e) => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a+    [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a+[C] Streamly.Data.Stream+    [A] wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [C] handle+        [O] handle :: (MonadCatch m, Exception e) => (e -> Stream m a) -> Stream m a -> Stream m a+        [N] handle :: (MonadCatch m, Exception e) => (e -> m (Stream m a)) -> Stream m a -> Stream m a+    [A] groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b+[C] Streamly.Data.ParserK+    [D] fromParser :: (MonadIO m, Unbox a) => Parser a m b -> ParserK (Array a) m b+    [D] fromFold :: (MonadIO m, Unbox a) => Fold m a b -> ParserK (Array a) m b+    [A] adaptCG :: Monad m => Parser a m b -> ParserK (Array a) m b+    [A] adaptC :: (Monad m, Unbox a) => Parser a m b -> ParserK (Array a) m b+    [A] adapt :: Monad m => Parser a m b -> ParserK a m b+[C] Streamly.Data.Parser+    [A] groupByRollingEither :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)+    [A] groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b+[A] Streamly.Data.MutByteArray+    [A] class Unbox a+    [A] class Serialize a+    [A] SerializeConfig+    [A] MutByteArray+    [A] unpin :: MutByteArray -> IO MutByteArray+    [A] sizeOf :: (Unbox a, SizeOfRep (Rep a)) => Proxy a -> Int+    [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int+    [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()+    [A] pinnedNew :: Int -> IO MutByteArray+    [A] pin :: MutByteArray -> IO MutByteArray+    [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a+    [A] new :: Int -> IO MutByteArray+    [A] isPinned :: MutByteArray -> Bool+    [A] inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)+    [A] deriveUnbox :: Q [Dec] -> Q [Dec]+    [A] deriveSerializeWith :: (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]+    [A] deriveSerialize :: Q [Dec] -> Q [Dec]+    [A] addSizeTo :: Serialize a => Int -> a -> Int+[C] Streamly.Data.MutArray.Generic+    [A] write :: MonadIO m => Fold m a (MutArray a)+    [A] readRev :: MonadIO m => MutArray a -> Stream m a+    [A] read :: MonadIO m => MutArray a -> Stream m a+    [A] putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()+    [A] modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] length :: MutArray a -> Int+    [A] getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a+    [C] getIndex+        [O] getIndex :: MonadIO m => Int -> MutArray a -> m a+        [N] getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)+    [A] fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)+    [A] fromList :: MonadIO m => [a] -> m (MutArray a)+[C] Streamly.Data.MutArray+    [A] unpin :: MutArray a -> IO (MutArray a)+    [A] readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a+    [A] read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a+    [A] putIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()+    [D] pokeByteIndex :: Unbox a => Int -> MutByteArray -> a -> IO ()+    [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()+    [A] pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] pin :: MutArray a -> IO (MutArray a)+    [D] peekByteIndex :: Unbox a => Int -> MutByteArray -> IO a+    [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a+    [D] newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] modifyIndex :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] modify :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (a -> a) -> m ()+    [A] isPinned :: MutArray a -> Bool+    [A] getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+    [C] getIndex+        [O] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+        [N] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)+[C] Streamly.Data.Array.Generic+    [A] toList :: Array a -> [a]+    [A] getIndex :: Int -> Array a -> Maybe a+[C] Streamly.Data.Array+    [A] unpin :: Array a -> IO (Array a)+    [D] pokeByteIndex :: Unbox a => Int -> MutByteArray -> a -> IO ()+    [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()+    [A] pin :: Array a -> IO (Array a)+    [D] peekByteIndex :: Unbox a => Int -> MutByteArray -> IO a+    [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a+    [A] isPinned :: Array a -> Bool+[C] Streamly.Console.Stdio++---------------------------------+Internal API diff+---------------------------------++[C] Streamly.Internal.Unicode.Stream+    [C] writeCharUtf8'+        [O] writeCharUtf8' :: Monad m => Fold m Word8 Char+        [N] writeCharUtf8' :: Monad m => Parser Word8 m Char+[C] Streamly.Internal.Unicode.Parser+    [A] number :: Monad m => Parser Char m (Integer, Int)+    [A] mkDouble :: Integer -> Int -> Double+    [A] doubleParser :: Monad m => Parser Char m (Int, Int)+    [C] double+        [O] double :: Parser Char m Double+        [N] double :: Monad m => Parser Char m Double+[R] Streamly.Internal.Serialize.ToBytes+[R] Streamly.Internal.Serialize.FromBytes+[C] Streamly.Internal.FileSystem.File+    [A] writeAppendWith :: (MonadIO m, MonadCatch m) => Int -> FilePath -> Stream m Word8 -> m ()+    [A] writeAppendChunks :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Array a) -> m ()+    [A] writeAppendArray :: FilePath -> Array a -> IO ()+    [A] writeAppend :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()+    [R] appendWith :: (MonadIO m, MonadCatch m) => Int -> FilePath -> Stream m Word8 -> m ()+    [R] appendChunks :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Array a) -> m ()+    [R] appendArray :: FilePath -> Array a -> IO ()+    [R] append :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()+[C] Streamly.Internal.FileSystem.Dir+    [C] reader+        [O] reader :: MonadIO m => Unfold m FilePath FilePath+        [N] reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath+    [C] readFiles+        [O] readFiles :: MonadIO m => FilePath -> Stream m FilePath+        [N] readFiles :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath+    [C] readEitherPaths+        [O] readEitherPaths :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+        [N] readEitherPaths :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)+    [C] readEither+        [O] readEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+        [N] readEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath)+    [C] readDirs+        [O] readDirs :: MonadIO m => FilePath -> Stream m FilePath+        [N] readDirs :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath+    [C] read+        [O] read :: MonadIO m => FilePath -> Stream m FilePath+        [N] read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath+    [C] fileReader+        [O] fileReader :: MonadIO m => Unfold m FilePath FilePath+        [N] fileReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath+    [C] eitherReaderPaths+        [O] eitherReaderPaths :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)+        [N] eitherReaderPaths :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath)+    [C] eitherReader+        [O] eitherReader :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)+        [N] eitherReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath)+    [C] dirReader+        [O] dirReader :: MonadIO m => Unfold m FilePath FilePath+        [N] dirReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath+[R] Streamly.Internal.Data.Unfold.Type+[R] Streamly.Internal.Data.Unfold.Enumeration+[C] Streamly.Internal.Data.Unfold+    [C] Unfold+        [A] Unfold :: (s -> m (Step s b)) -> (a -> m s) -> Unfold m a b+    [A] takeWhileMWithInput :: Monad m => (a -> b -> m Bool) -> Unfold m a b -> Unfold m a b+    [A] manyInterleave :: Monad m => Unfold m a b -> Unfold m c a -> Unfold m c b+    [A] enumerateFromStepIntegral :: (Monad m, Integral a) => Unfold m (a, a) a+    [A] crossApplySnd :: Unfold m a b -> Unfold m a c -> Unfold m a c+    [A] crossApplyFst :: Unfold m a b -> Unfold m a c -> Unfold m a b+    [A] concatMap :: Monad m => (b -> Unfold m a c) -> Unfold m a b -> Unfold m a c+[R] Streamly.Internal.Data.Unboxed+[C] Streamly.Internal.Data.Time.Units+    [R] Streamly.Internal.Data.Unboxed.Unbox+    [A] Streamly.Internal.Data.Unbox.Unbox+        [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.NanoSecond64+        [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.MilliSecond64+        [A] instance Streamly.Internal.Data.Unbox.Unbox Streamly.Internal.Data.Time.Units.MicroSecond64+[R] Streamly.Internal.Data.Time.Clock.Type+[A] Streamly.Internal.Data.StreamK+    [A] CrossStreamK+    [A] (FixityR,6)+    [A] (FixityR,6)+    [A] (FixityR,6)+    [A] (FixityR,5)+    [A] (FixityR,5)+    [A] (FixityR,6)+    [A] (FixityR,5)+    [A] StreamK+        [A] MkStream :: (forall r. State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a+    [A] zipWithM :: Monad m => (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c+    [A] zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c+    [A] unfoldrMWith :: Monad m => (m a -> StreamK m a -> StreamK m a) -> (b -> m (Maybe (a, b))) -> b -> StreamK m a+    [A] unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> StreamK m a+    [A] unfoldr :: (b -> Maybe (a, b)) -> b -> StreamK m a+    [A] uncons :: Applicative m => StreamK m a -> m (Maybe (a, StreamK m a))+    [A] unShare :: StreamK m a -> StreamK m a+    [A] unCross :: CrossStreamK m a -> StreamK m a+    [A] toStream :: Applicative m => StreamK m a -> Stream m a+    [A] toList :: Monad m => StreamK m a -> m [a]+    [A] the :: (Eq a, Monad m) => StreamK m a -> m (Maybe a)+    [A] takeWhile :: (a -> Bool) -> StreamK m a -> StreamK m a+    [A] take :: Int -> StreamK m a -> StreamK m a+    [A] tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))+    [A] sortBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> StreamK m a+    [A] sequence :: Monad m => StreamK m (m a) -> StreamK m a+    [A] scanlx' :: (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> StreamK m b+    [A] scanl' :: (b -> a -> b) -> b -> StreamK m a -> StreamK m b+    [A] reverse :: StreamK m a -> StreamK m a+    [A] replicateMWith :: (m a -> StreamK m a -> StreamK m a) -> Int -> m a -> StreamK m a+    [A] replicateM :: Monad m => Int -> m a -> StreamK m a+    [A] replicate :: Int -> a -> StreamK m a+    [A] repeatMWith :: (m a -> t m a -> t m a) -> m a -> t m a+    [A] repeatM :: Monad m => m a -> StreamK m a+    [A] repeat :: a -> StreamK m a+    [A] parseDBreak :: Monad m => Parser a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)+    [A] parseD :: Monad m => Parser a m b -> StreamK m a -> m (Either ParseError b)+    [A] parseChunksGeneric :: Monad m => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)+    [A] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)+    [A] parseBreakChunksGeneric :: forall m a b. Monad m => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] parseBreak :: forall m a b. Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b, StreamK m a)+    [A] parse :: Monad m => ParserK a m b -> StreamK m a -> m (Either ParseError b)+    [A] null :: Monad m => StreamK m a -> m Bool+    [A] notElem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool+    [A] nilM :: Applicative m => m b -> StreamK m a+    [A] nil :: StreamK m a+    [A] mkStream :: (forall r. State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a+    [A] mkCross :: StreamK m a -> CrossStreamK m a+    [A] minimumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)+    [A] minimum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)+    [A] mfix :: Monad m => (m a -> StreamK m a) -> StreamK m a+    [A] mergeMapWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m a -> StreamK m b+    [A] mergeIterateWith :: (StreamK m a -> StreamK m a -> StreamK m a) -> (a -> StreamK m a) -> StreamK m a -> StreamK m a+    [A] mergeByM :: Monad m => (a -> a -> m Ordering) -> StreamK m a -> StreamK m a -> StreamK m a+    [A] mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a+    [A] maximumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)+    [A] maximum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)+    [A] mapMaybe :: (a -> Maybe b) -> StreamK m a -> StreamK m b+    [A] mapM_ :: Monad m => (a -> m b) -> StreamK m a -> m ()+    [A] mapMWith :: (m b -> StreamK m b -> StreamK m b) -> (a -> m b) -> StreamK m a -> StreamK m b+    [A] mapMSerial :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b+    [A] mapM :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b+    [A] map :: (a -> b) -> StreamK m a -> StreamK m b+    [A] lookup :: (Monad m, Eq a) => a -> StreamK m (a, b) -> m (Maybe b)+    [A] liftInner :: (Monad m, MonadTrans t, Monad (t m)) => StreamK m a -> StreamK (t m) a+    [A] last :: Monad m => StreamK m a -> m (Maybe a)+    [A] iterateMWith :: Monad m => (m a -> StreamK m a -> StreamK m a) -> (a -> m a) -> m a -> StreamK m a+    [A] iterateM :: Monad m => (a -> m a) -> m a -> StreamK m a+    [A] iterate :: (a -> a) -> a -> StreamK m a+    [A] intersperseM :: Monad m => m a -> StreamK m a -> StreamK m a+    [A] intersperse :: Monad m => a -> StreamK m a -> StreamK m a+    [A] interleaveMin :: StreamK m a -> StreamK m a -> StreamK m a+    [A] interleaveFst :: StreamK m a -> StreamK m a -> StreamK m a+    [A] interleave :: StreamK m a -> StreamK m a -> StreamK m a+    [A] insertBy :: (a -> a -> Ordering) -> a -> StreamK m a -> StreamK m a+    [A] init :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))+    [A] hoist :: (Monad m, Monad n) => (forall x. m x -> n x) -> StreamK m a -> StreamK n a+    [A] head :: Monad m => StreamK m a -> m (Maybe a)+    [A] handle :: (MonadCatch m, Exception e) => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a+    [A] fromYieldK :: YieldK m a -> StreamK m a+    [A] fromStream :: Monad m => Stream m a -> StreamK m a+    [A] fromStopK :: StopK m -> StreamK m a+    [A] fromPure :: a -> StreamK m a+    [A] fromList :: [a] -> StreamK m a+    [A] fromIndicesMWith :: (m a -> StreamK m a -> StreamK m a) -> (Int -> m a) -> StreamK m a+    [A] fromIndicesM :: Monad m => (Int -> m a) -> StreamK m a+    [A] fromIndices :: (Int -> a) -> StreamK m a+    [A] fromFoldableM :: (Foldable f, Monad m) => f (m a) -> StreamK m a+    [A] fromFoldable :: Foldable f => f a -> StreamK m a+    [A] fromEffect :: Monad m => m a -> StreamK m a+    [A] foldrT :: (Monad m, Monad (s m), MonadTrans s) => (a -> s m b -> s m b) -> s m b -> StreamK m a -> s m b+    [A] foldrSShared :: (a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b+    [A] foldrSM :: Monad m => (m a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b+    [A] foldrS :: (a -> StreamK m b -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b+    [A] foldrM :: (a -> m b -> m b) -> m b -> StreamK m a -> m b+    [A] foldr1 :: Monad m => (a -> a -> a) -> StreamK m a -> m (Maybe a)+    [A] foldr :: Monad m => (a -> b -> b) -> b -> StreamK m a -> m b+    [A] foldlx' :: forall m a b x. Monad m => (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> m b+    [A] foldlT :: (Monad m, Monad (s m), MonadTrans s) => (s m b -> a -> s m b) -> s m b -> StreamK m a -> s m b+    [A] foldlS :: (StreamK m b -> a -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b+    [A] foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b+    [A] foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b+    [A] foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b+    [A] foldStreamShared :: State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> StreamK m a -> m r+    [A] foldStream :: State StreamK m a -> (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> StreamK m a -> m r+    [A] foldEither :: Monad m => Fold m a b -> StreamK m a -> m (Either (Fold m a b) (b, StreamK m a))+    [A] foldConcat :: Monad m => Producer m a b -> Fold m b c -> StreamK m a -> m (c, StreamK m a)+    [A] foldBreak :: Monad m => Fold m a b -> StreamK m a -> m (b, StreamK m a)+    [A] fold :: Monad m => Fold m a b -> StreamK m a -> m b+    [A] findM :: Monad m => (a -> m Bool) -> StreamK m a -> m (Maybe a)+    [A] findIndices :: (a -> Bool) -> StreamK m a -> StreamK m Int+    [A] find :: Monad m => (a -> Bool) -> StreamK m a -> m (Maybe a)+    [A] filter :: (a -> Bool) -> StreamK m a -> StreamK m a+    [A] evalStateT :: Monad m => m s -> StreamK (StateT s m) a -> StreamK m a+    [A] elem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool+    [A] dropWhile :: (a -> Bool) -> StreamK m a -> StreamK m a+    [A] drop :: Int -> StreamK m a -> StreamK m a+    [A] drain :: Monad m => StreamK m a -> m ()+    [A] deleteBy :: (a -> a -> Bool) -> a -> StreamK m a -> StreamK m a+    [A] crossWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c+    [A] crossApplyWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> StreamK m (a -> b) -> StreamK m a -> StreamK m b+    [A] crossApplySnd :: StreamK m a -> StreamK m b -> StreamK m b+    [A] crossApplyFst :: StreamK m a -> StreamK m b -> StreamK m a+    [A] crossApply :: StreamK m (a -> b) -> StreamK m a -> StreamK m b+    [A] cross :: Monad m => StreamK m a -> StreamK m b -> StreamK m (a, b)+    [A] consMBy :: Monad m => (StreamK m a -> StreamK m a -> StreamK m a) -> m a -> StreamK m a -> StreamK m a+    [A] consM :: Monad m => m a -> StreamK m a -> StreamK m a+    [A] consK :: YieldK m a -> StreamK m a -> StreamK m a+    [A] cons :: a -> StreamK m a -> StreamK m a+    [A] conjoin :: Monad m => StreamK m a -> StreamK m a -> StreamK m a+    [A] concatMapWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m a -> StreamK m b+    [A] concatMapEffect :: Monad m => (b -> StreamK m a) -> m b -> StreamK m a+    [A] concatMap :: (a -> StreamK m b) -> StreamK m a -> StreamK m b+    [A] concatIterateWith :: (StreamK m a -> StreamK m a -> StreamK m a) -> (a -> StreamK m a) -> StreamK m a -> StreamK m a+    [A] concatIterateScanWith :: Monad m => (StreamK m a -> StreamK m a -> StreamK m a) -> (b -> a -> m (b, StreamK m a)) -> m b -> StreamK m a -> StreamK m a+    [A] concatIterateLeftsWith :: b ~ Either a c => (StreamK m b -> StreamK m b -> StreamK m b) -> (a -> StreamK m b) -> StreamK m b -> StreamK m b+    [A] concatEffect :: Monad m => m (StreamK m a) -> StreamK m a+    [A] buildSM :: Monad m => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a+    [A] buildS :: ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a+    [A] buildM :: Monad m => (forall r. (a -> StreamK m a -> m r) -> (a -> m r) -> m r -> m r) -> StreamK m a+    [A] build :: forall m a. (forall b. (a -> b -> b) -> b -> b) -> StreamK m a+    [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a+    [A] bindWith :: (StreamK m b -> StreamK m b -> StreamK m b) -> StreamK m a -> (a -> StreamK m b) -> StreamK m b+    [A] before :: Monad m => m b -> StreamK m a -> StreamK m a+    [A] augmentSM :: Monad m => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a+    [A] augmentS :: ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a+    [A] append :: StreamK m a -> StreamK m a -> StreamK m a+    [A] any :: Monad m => (a -> Bool) -> StreamK m a -> m Bool+    [A] all :: Monad m => (a -> Bool) -> StreamK m a -> m Bool+    [A] (.:) :: a -> StreamK m a -> StreamK m a+    [A] (!!) :: Monad m => StreamK m a -> Int -> m (Maybe a)+[R] Streamly.Internal.Data.Stream.StreamK.Type+[R] Streamly.Internal.Data.Stream.StreamK.Transformer+[R] Streamly.Internal.Data.Stream.StreamK+[R] Streamly.Internal.Data.Stream.StreamD.Type+[R] Streamly.Internal.Data.Stream.StreamD.Transformer+[R] Streamly.Internal.Data.Stream.StreamD.Transform+[R] Streamly.Internal.Data.Stream.StreamD.Top+[R] Streamly.Internal.Data.Stream.StreamD.Step+[R] Streamly.Internal.Data.Stream.StreamD.Nesting+[R] Streamly.Internal.Data.Stream.StreamD.Lift+[R] Streamly.Internal.Data.Stream.StreamD.Generate+[R] Streamly.Internal.Data.Stream.StreamD.Exception+[R] Streamly.Internal.Data.Stream.StreamD.Eliminate+[R] Streamly.Internal.Data.Stream.StreamD.Container+[D] Streamly.Internal.Data.Stream.StreamD+[R] Streamly.Internal.Data.Stream.Common+[R] Streamly.Internal.Data.Stream.Chunked+[C] Streamly.Internal.Data.Stream+    [A] class Enum a => Enumerable a+    [A] Stream+        [A] UnStream :: (State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a+    [A] Step+        [A] Yield :: a -> s -> Step s a+        [A] Stop :: Step s a+        [A] Skip :: s -> Step s a+    [A] InterleaveState+        [A] InterleaveSecondOnly :: s2 -> InterleaveState s1 s2+        [A] InterleaveSecond :: s1 -> s2 -> InterleaveState s1 s2+        [A] InterleaveFirstOnly :: s1 -> InterleaveState s1 s2+        [A] InterleaveFirst :: s1 -> s2 -> InterleaveState s1 s2+    [A] FoldManyPost+        [A] FoldManyPostYield :: b -> FoldManyPost s fs b a -> FoldManyPost s fs b a+        [A] FoldManyPostStart :: s -> FoldManyPost s fs b a+        [A] FoldManyPostLoop :: s -> fs -> FoldManyPost s fs b a+        [A] FoldManyPostDone :: FoldManyPost s fs b a+    [A] FoldMany+        [A] FoldManyYield :: b -> FoldMany s fs b a -> FoldMany s fs b a+        [A] FoldManyStart :: s -> FoldMany s fs b a+        [A] FoldManyLoop :: s -> fs -> FoldMany s fs b a+        [A] FoldManyFirst :: fs -> s -> FoldMany s fs b a+        [A] FoldManyDone :: FoldMany s fs b a+    [A] CrossStream+    [A] ConcatUnfoldInterleaveState+        [A] ConcatUnfoldInterleaveOuter :: o -> [i] -> ConcatUnfoldInterleaveState o i+        [A] ConcatUnfoldInterleaveInnerR :: [i] -> [i] -> ConcatUnfoldInterleaveState o i+        [A] ConcatUnfoldInterleaveInnerL :: [i] -> [i] -> ConcatUnfoldInterleaveState o i+        [A] ConcatUnfoldInterleaveInner :: o -> [i] -> ConcatUnfoldInterleaveState o i+    [A] ConcatMapUState+        [A] ConcatMapUOuter :: o -> ConcatMapUState o i+        [A] ConcatMapUInner :: o -> i -> ConcatMapUState o i+    [A] AppendState+        [A] AppendSecond :: s2 -> AppendState s1 s2+        [A] AppendFirst :: s1 -> AppendState s1 s2+    [A] pattern Stream :: (State StreamK m a -> s -> m (Step s a)) -> s -> Stream m a+    [A] zipWithM :: Monad m => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c+    [A] zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c+    [A] wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] with :: Monad m => (Stream m a -> Stream m (s, a)) -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a)) -> ((s, a) -> b) -> Stream m a -> Stream m a+    [A] usingStateT :: Monad m => m s -> (Stream (StateT s m) a -> Stream (StateT s m) a) -> Stream m a -> Stream m a+    [A] usingReaderT :: Monad m => m r -> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a) -> Stream m a -> Stream m a+    [A] uniqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a+    [A] uniq :: (Eq a, Monad m) => Stream m a -> Stream m a+    [A] unionWithStreamGenericBy :: MonadIO m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+    [A] unionWithStreamAscBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a+    [A] unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a+    [A] unfoldRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b+    [A] unfoldMany :: Monad m => Unfold m a b -> Stream m a -> Stream m b+    [A] unfoldIterateDfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a+    [A] unfoldIterateBfsRev :: Monad m => Unfold m a a -> Stream m a -> Stream m a+    [A] unfoldIterateBfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a+    [A] unfoldInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b+    [A] unfold :: Applicative m => Unfold m a b -> a -> Stream m b+    [A] uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))+    [A] unCross :: CrossStream m a -> Stream m a+    [A] transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b+    [A] trace_ :: Monad m => m b -> Stream m a -> Stream m a+    [A] trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a+    [A] toStreamK :: Monad m => Stream m a -> StreamK m a+    [A] toListRev :: Monad m => Stream m a -> m [a]+    [A] toList :: Monad m => Stream m a -> m [a]+    [A] timestamped :: MonadIO m => Stream m a -> Stream m (AbsTime, a)+    [A] timestampWith :: MonadIO m => Double -> Stream m a -> Stream m (AbsTime, a)+    [A] timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)+    [A] times :: MonadIO m => Stream m (AbsTime, RelTime64)+    [A] timeout :: AbsTime -> t m ()+    [A] timeIndexed :: MonadIO m => Stream m a -> Stream m (RelTime64, a)+    [A] timeIndexWith :: MonadIO m => Double -> Stream m a -> Stream m (RelTime64, a)+    [A] the :: (Eq a, Monad m) => Stream m a -> m (Maybe a)+    [A] tapOffsetEvery :: Monad m => Int -> Int -> Fold m a b -> Stream m a -> Stream m a+    [A] tap :: Monad m => Fold m a b -> Stream m a -> Stream m a+    [A] takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+    [A] takeWhileLast :: (a -> Bool) -> Stream m a -> Stream m a+    [A] takeWhileAround :: (a -> Bool) -> Stream m a -> Stream m a+    [A] takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+    [A] takeEndByM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+    [A] takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+    [A] take :: Applicative m => Int -> Stream m a -> Stream m a+    [A] tail :: Monad m => Stream m a -> m (Maybe (Stream m a))+    [A] stripSuffixUnbox :: (MonadIO m, Eq a, Unbox a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))+    [A] stripSuffix :: (Monad m, Eq a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))+    [A] stripPrefix :: (Monad m, Eq a) => Stream m a -> Stream m a -> m (Maybe (Stream m a))+    [A] strideFromThen :: Monad m => Int -> Int -> Stream m a -> Stream m a+    [A] splitOnSuffixSeqAny :: [Array a] -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOnSuffixSeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) => Bool -> Array a -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOnSeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) => Array a -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOnPrefix :: (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOnAny :: [Array a] -> Fold m a b -> Stream m a -> Stream m b+    [A] splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] splitInnerBySuffix :: (Monad m, Eq (f a), Monoid (f a)) => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)+    [A] splitInnerBy :: Monad m => (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)+    [A] slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+    [A] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+    [A] sequence :: Monad m => Stream m (m a) -> Stream m a+    [A] scanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b+    [A] scanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b+    [A] scanlMAfter' :: Monad m => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b+    [A] scanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+    [A] scanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+    [A] scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a+    [A] scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a+    [A] scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a+    [A] scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a+    [A] scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+    [A] scanl :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+    [A] scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b+    [A] scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b+    [A] scan :: Monad m => Fold m a b -> Stream m a -> Stream m b+    [A] runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)+    [A] runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a+    [A] runInnerWithState :: Monad m => (forall b. s -> t m b -> m (b, s)) -> m s -> Stream (t m) a -> Stream m (s, a)+    [A] runInnerWith :: Monad m => (forall b. t m b -> m b) -> Stream (t m) a -> Stream m a+    [A] roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b+    [A] rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b+    [A] rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b+    [A] reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a+    [A] reverse :: Monad m => Stream m a -> Stream m a+    [A] replicateM :: Monad m => Int -> m a -> Stream m a+    [A] replicate :: Monad m => Int -> a -> Stream m a+    [A] repeated :: Stream m a -> Stream m a+    [A] repeatM :: Monad m => m a -> Stream m a+    [A] repeat :: Monad m => a -> Stream m a+    [A] relTimesWith :: MonadIO m => Double -> Stream m RelTime64+    [A] relTimes :: MonadIO m => Stream m RelTime64+    [A] refoldMany :: Monad m => Refold m x a b -> m x -> Stream m a -> Stream m b+    [A] refoldIterateM :: Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b+    [A] reduceIterateBfs :: Monad m => (a -> a -> m a) -> Stream m a -> m (Maybe a)+    [A] reassembleBy :: Fold m a b -> (a -> a -> Int) -> Stream m a -> Stream m b+    [A] prune :: (a -> Bool) -> Stream m a -> Stream m a+    [A] prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+    [A] prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+    [A] postscanlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b+    [A] postscanlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b+    [A] postscanlMAfter' :: Monad m => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b+    [A] postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+    [A] postscanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+    [A] postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a+    [A] postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a+    [A] postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b+    [A] parseSequence :: Stream m (Parser a m b) -> Stream m a -> Stream m b+    [A] parseManyTill :: Parser a m b -> Parser a m x -> Stream m a -> Stream m b+    [A] parseManyD :: Monad m => Parser a m b -> Stream m a -> Stream m (Either ParseError b)+    [A] parseMany :: Monad m => Parser a m b -> Stream m a -> Stream m (Either ParseError b)+    [A] parseIterateD :: Monad m => (b -> Parser a m b) -> b -> Stream m a -> Stream m (Either ParseError b)+    [A] parseIterate :: Monad m => (b -> Parser a m b) -> b -> Stream m a -> Stream m (Either ParseError b)+    [A] parseD :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)+    [A] parseBreakD :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)+    [A] parseBreak :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)+    [A] parse :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)+    [A] onException :: MonadCatch m => m b -> Stream m a -> Stream m a+    [A] null :: Monad m => Stream m a -> m Bool+    [A] nub :: (Monad m, Ord a) => Stream m a -> Stream m a+    [A] notElem :: (Monad m, Eq a) => a -> Stream m a -> m Bool+    [A] nilM :: Applicative m => m b -> Stream m a+    [A] nil :: Applicative m => Stream m a+    [A] morphInner :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a+    [A] mkCross :: Stream m a -> CrossStream m a+    [A] minimumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)+    [A] minimum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)+    [A] mergeMinBy :: (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] mergeFstBy :: (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] mergeByM :: Monad m => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] mergeBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] maximumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)+    [A] maximum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)+    [A] mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b+    [A] mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b+    [A] mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()+    [A] mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b+    [A] map :: Monad m => (a -> b) -> Stream m a -> Stream m b+    [A] lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)+    [A] liftInnerWith :: Monad (t m) => (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a+    [A] liftInner :: (Monad m, MonadTrans t, Monad (t m)) => Stream m a -> Stream (t m) a+    [A] last :: Monad m => Stream m a -> m (Maybe a)+    [A] joinOuterGeneric :: MonadIO m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (Maybe a, Maybe b)+    [A] joinOuterAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (Maybe a, Maybe b)+    [A] joinOuter :: (Ord k, MonadIO m) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)+    [A] joinLeftGeneric :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)+    [A] joinLeftAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)+    [A] joinLeft :: (Ord k, Monad m) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)+    [A] joinInnerGeneric :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)+    [A] joinInnerAscBy :: (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)+    [A] joinInner :: (Monad m, Ord k) => Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)+    [A] iterateM :: Monad m => (a -> m a) -> m a -> Stream m a+    [A] iterate :: Monad m => (a -> a) -> a -> Stream m a+    [A] isSuffixOfUnbox :: (MonadIO m, Eq a, Unbox a) => Stream m a -> Stream m a -> m Bool+    [A] isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool+    [A] isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool+    [A] isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool+    [A] isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a, Unbox a) => Stream m a -> Stream m a -> m Bool+    [A] intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a+    [A] intersperseMWith :: Int -> m a -> Stream m a -> Stream m a+    [A] intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a+    [A] intersperseMSuffixWith :: forall m a. Monad m => Int -> m a -> Stream m a -> Stream m a+    [A] intersperseMSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a+    [A] intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a+    [A] intersperseM :: Monad m => m a -> Stream m a -> Stream m a+    [A] intersperse :: Monad m => a -> Stream m a -> Stream m a+    [A] intersectBySorted :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] interposeSuffixM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c+    [A] interposeSuffix :: Monad m => c -> Unfold m b c -> Stream m b -> Stream m c+    [A] interposeM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c+    [A] interpose :: Monad m => c -> Unfold m b c -> Stream m b -> Stream m c+    [A] interleaveMin :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] interleaveFstSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] interleave :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] intercalateSuffix :: Monad m => Unfold m b c -> b -> Stream m b -> Stream m c+    [A] intercalate :: Monad m => Unfold m b c -> b -> Stream m b -> Stream m c+    [A] insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a+    [A] indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)+    [A] indexed :: Monad m => Stream m a -> Stream m (Int, a)+    [A] headElse :: Monad m => a -> Stream m a -> m a+    [A] head :: Monad m => Stream m a -> m (Maybe a)+    [A] handle :: (MonadCatch m, Exception e) => (e -> m (Stream m a)) -> Stream m a -> Stream m a+    [A] groupsWhile :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] groupsRollingBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+    [A] groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b+    [A] gintercalateSuffix :: Monad m => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c+    [A] gintercalate :: Monad m => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c+    [A] ghandle :: (MonadCatch m, Exception e) => (e -> Stream m a -> m (Stream m a)) -> Stream m a -> Stream m a+    [A] generateM :: Monad m => Int -> (Int -> m a) -> Stream m a+    [A] generate :: Monad m => Int -> (Int -> a) -> Stream m a+    [A] generalizeInner :: Monad m => Stream Identity a -> Stream m a+    [A] gbracket_ :: Monad m => m c -> (c -> m d) -> (c -> e -> Stream m b -> m (Stream m b)) -> (forall s. m s -> m (Either e s)) -> (c -> Stream m b) -> Stream m b+    [A] gbracket :: MonadIO m => IO c -> (c -> IO d1) -> (c -> e -> Stream m b -> IO (Stream m b)) -> (c -> IO d2) -> (forall s. m s -> m (Either e s)) -> (c -> Stream m b) -> Stream m b+    [A] fromStreamK :: Applicative m => StreamK m a -> Stream m a+    [A] fromPure :: Applicative m => a -> Stream m a+    [A] fromPtrN :: (Monad m, Storable a) => Int -> Ptr a -> Stream m a+    [A] fromPtr :: forall m a. (Monad m, Storable a) => Ptr a -> Stream m a+    [A] fromListM :: Monad m => [m a] -> Stream m a+    [A] fromList :: Applicative m => [a] -> Stream m a+    [A] fromIndicesM :: Monad m => (Int -> m a) -> Stream m a+    [A] fromIndices :: Monad m => (Int -> a) -> Stream m a+    [A] fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a+    [A] fromFoldable :: (Monad m, Foldable f) => f a -> Stream m a+    [A] fromEffect :: Applicative m => m a -> Stream m a+    [A] fromByteStr# :: Monad m => Addr# -> Stream m Word8+    [A] foldrT :: (Monad m, Monad (t m), MonadTrans t) => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b+    [A] foldrS :: Monad m => (a -> Stream m b -> Stream m b) -> Stream m b -> Stream m a -> Stream m b+    [A] foldrMx :: Monad m => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b+    [A] foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b+    [A] foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)+    [A] foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b+    [A] foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b+    [A] foldlT :: (Monad m, Monad (s m), MonadTrans s) => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b+    [A] foldlS :: Monad m => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b+    [A] foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b+    [A] foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b+    [A] foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b+    [A] foldSequence :: Stream m (Fold m a b) -> Stream m a -> Stream m b+    [A] foldManyPost :: Monad m => Fold m a b -> Stream m a -> Stream m b+    [A] foldMany :: Monad m => Fold m a b -> Stream m a -> Stream m b+    [A] foldIterateM :: Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b+    [A] foldIterateBfs :: Fold m a (Either a a) -> Stream m a -> m (Maybe a)+    [A] foldEither :: Monad m => Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))+    [A] foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)+    [A] foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b+    [A] foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)+    [A] fold :: Monad m => Fold m a b -> Stream m a -> m b+    [A] findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)+    [A] findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int+    [A] find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)+    [A] finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a+    [A] finallyIO :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a+    [A] filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+    [A] filterInStreamGenericBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+    [A] filterInStreamAscBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+    [A] evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a+    [A] eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool+    [A] enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a+    [A] enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a+    [A] enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a+    [A] enumerateFromToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> Stream m a+    [A] enumerateFromTo :: (Enumerable a, Monad m) => a -> a -> Stream m a+    [A] enumerateFromThenToSmall :: (Monad m, Enum a) => a -> a -> a -> Stream m a+    [A] enumerateFromThenToIntegral :: (Monad m, Integral a) => a -> a -> a -> Stream m a+    [A] enumerateFromThenToFractional :: (Monad m, Fractional a, Ord a) => a -> a -> a -> Stream m a+    [A] enumerateFromThenTo :: (Enumerable a, Monad m) => a -> a -> a -> Stream m a+    [A] enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a) => a -> a -> Stream m a+    [A] enumerateFromThenNum :: (Monad m, Num a) => a -> a -> Stream m a+    [A] enumerateFromThenIntegral :: (Monad m, Integral a, Bounded a) => a -> a -> Stream m a+    [A] enumerateFromThenFractional :: (Monad m, Fractional a) => a -> a -> Stream m a+    [A] enumerateFromThen :: (Enumerable a, Monad m) => a -> a -> Stream m a+    [A] enumerateFromStepNum :: (Monad m, Num a) => a -> a -> Stream m a+    [A] enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a+    [A] enumerateFromNum :: (Monad m, Num a) => a -> Stream m a+    [A] enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a+    [A] enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a+    [A] enumerateFromBounded :: (Monad m, Enumerable a, Bounded a) => a -> Stream m a+    [A] enumerateFrom :: (Enumerable a, Monad m) => a -> Stream m a+    [A] enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a+    [A] elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int+    [A] elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool+    [A] durations :: Double -> t m RelTime64+    [A] dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+    [A] dropWhileLast :: (a -> Bool) -> Stream m a -> Stream m a+    [A] dropWhileAround :: (a -> Bool) -> Stream m a -> Stream m a+    [A] dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+    [A] dropSuffix :: Stream m a -> Stream m a -> Stream m a+    [A] dropPrefix :: Stream m a -> Stream m a -> Stream m a+    [A] dropLast :: Int -> Stream m a -> Stream m a+    [A] dropInfix :: Stream m a -> Stream m a -> Stream m a+    [A] drop :: Monad m => Int -> Stream m a -> Stream m a+    [A] drain :: Monad m => Stream m a -> m ()+    [A] deleteInStreamGenericBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+    [A] deleteInStreamAscBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+    [A] deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a+    [A] delayPre :: MonadIO m => Double -> Stream m a -> Stream m a+    [A] delayPost :: MonadIO m => Double -> Stream m a -> Stream m a+    [A] delay :: MonadIO m => Double -> Stream m a -> Stream m a+    [A] crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c+    [A] crossApplySnd :: Functor f => Stream f a -> Stream f b -> Stream f b+    [A] crossApplyFst :: Functor f => Stream f a -> Stream f b -> Stream f a+    [A] crossApply :: Functor f => Stream f (a -> b) -> Stream f a -> Stream f b+    [A] cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)+    [A] consM :: Applicative m => m a -> Stream m a -> Stream m a+    [A] cons :: Applicative m => a -> Stream m a -> Stream m a+    [A] concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b+    [A] concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b+    [A] concatIterateScan :: Monad m => (b -> a -> m b) -> (b -> m (Maybe (b, Stream m a))) -> b -> Stream m a+    [A] concatIterateDfs :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a+    [A] concatIterateBfsRev :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a+    [A] concatIterateBfs :: Monad m => (a -> Maybe (Stream m a)) -> Stream m a -> Stream m a+    [A] concatEffect :: Monad m => m (Stream m a) -> Stream m a+    [A] concat :: Monad m => Stream m (Stream m a) -> Stream m a+    [A] cmpBy :: Monad m => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering+    [A] catRights :: Monad m => Stream m (Either a b) -> Stream m b+    [A] catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a+    [A] catLefts :: Monad m => Stream m (Either a b) -> Stream m a+    [A] catEithers :: Monad m => Stream m (Either a a) -> Stream m a+    [A] bracketUnsafe :: MonadCatch m => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a+    [A] bracketIO3 :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> IO d) -> (b -> IO e) -> (b -> Stream m a) -> Stream m a+    [A] bracketIO :: (MonadIO m, MonadCatch m) => IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a+    [A] before :: Monad m => m b -> Stream m a -> Stream m a+    [A] append :: Monad m => Stream m a -> Stream m a -> Stream m a+    [A] any :: Monad m => (a -> Bool) -> Stream m a -> m Bool+    [A] all :: Monad m => (a -> Bool) -> Stream m a -> m Bool+    [A] afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a+    [A] afterIO :: MonadIO m => IO b -> Stream m a -> Stream m a+    [A] absTimesWith :: MonadIO m => Double -> Stream m AbsTime+    [A] absTimes :: MonadIO m => Stream m AbsTime+    [A] (!!) :: Monad m => Stream m a -> Int -> m (Maybe a)+[R] Streamly.Internal.Data.Ring.Unboxed+[A] Streamly.Internal.Data.Ring.Generic+    [A] Ring+        [A] [ringMax] :: Ring a -> !Int+        [A] [ringHead] :: Ring a -> !Int+        [A] [ringArr] :: Ring a -> MutArray a+        [A] Ring :: MutArray a -> !Int -> !Int -> Ring a+    [A] writeLastN :: MonadIO m => Int -> Fold m a (Ring a)+    [A] unsafeInsertRingWith :: Ring a -> a -> IO Int+    [A] toStreamWith :: Int -> Ring a -> Stream m a+    [A] toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)+    [A] seek :: MonadIO m => Int -> Ring a -> m (Ring a)+    [A] createRing :: MonadIO m => Int -> m (Ring a)+    [A] copyToMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)+[C] Streamly.Internal.Data.Ring+    [C] Ring+        [A] [ringStart] :: Ring a -> {-# UNPACK #-} !ForeignPtr a+        [R] [ringMax] :: Ring a -> !Int+        [R] [ringHead] :: Ring a -> !Int+        [A] [ringBound] :: Ring a -> {-# UNPACK #-} !Ptr a+        [R] [ringArr] :: Ring a -> MutArray a+        [C] Ring+            [O] Ring :: MutArray a -> !Int -> !Int -> Ring a+            [N] Ring :: {-# UNPACK #-} !ForeignPtr a -> {-# UNPACK #-} !Ptr a -> Ring a+    [A] GHC.Show.Show+        [A] instance (GHC.Show.Show a, GHC.Show.Show b, GHC.Show.Show c, GHC.Show.Show d) => GHC.Show.Show (Streamly.Internal.Data.Ring.Tuple4' a b c d)+    [A] writeN :: Int -> Fold m a (Ring a)+    [R] writeLastN :: MonadIO m => Int -> Fold m a (Ring a)+    [R] unsafeInsertRingWith :: Ring a -> a -> IO Int+    [A] unsafeInsert :: Storable a => Ring a -> Ptr a -> a -> IO (Ptr a)+    [A] unsafeFoldRingNM :: forall m a b. (MonadIO m, Storable a) => Int -> Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+    [A] unsafeFoldRingM :: forall m a b. (MonadIO m, Storable a) => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+    [A] unsafeFoldRingFullM :: forall m a b. (MonadIO m, Storable a) => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+    [A] unsafeFoldRing :: forall a b. Storable a => Ptr a -> (b -> a -> b) -> b -> Ring a -> b+    [A] unsafeEqArrayN :: Ring a -> Ptr a -> Array a -> Int -> Bool+    [A] unsafeEqArray :: Ring a -> Ptr a -> Array a -> Bool+    [R] toStreamWith :: Int -> Ring a -> Stream m a+    [R] toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)+    [A] startOf :: Ring a -> Ptr a+    [A] slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unbox a) => Int -> Fold m ((a, Maybe a), m (MutArray a)) b -> Fold m a b+    [A] slidingWindow :: forall m a b. (MonadIO m, Storable a, Unbox a) => Int -> Fold m (a, Maybe a) b -> Fold m a b+    [A] slide :: Ring a -> a -> m (Ring a)+    [R] seek :: MonadIO m => Int -> Ring a -> m (Ring a)+    [A] ringsOf :: Int -> Stream m a -> Stream m (MutArray a)+    [A] readRev :: Unfold m (MutArray a) a+    [A] read :: forall m a. (MonadIO m, Storable a) => Unfold m (Ring a, Ptr a, Int) a+    [A] putIndex :: Ring a -> Int -> a -> m ()+    [A] newRing :: Int -> m (Ring a)+    [A] new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)+    [A] moveBy :: forall a. Storable a => Int -> Ring a -> Ptr a -> Ptr a+    [A] modifyIndex :: Ring a -> Int -> (a -> (a, b)) -> m b+    [A] length :: Ring a -> Int+    [A] getIndexUnsafe :: Ring a -> Int -> m a+    [A] getIndexRev :: Ring a -> Int -> m a+    [A] getIndex :: Ring a -> Int -> m a+    [A] fromArray :: MutArray a -> Ring a+    [R] createRing :: MonadIO m => Int -> m (Ring a)+    [A] castUnsafe :: Ring a -> Ring b+    [A] cast :: forall a b. Storable b => Ring a -> Maybe (Ring b)+    [A] bytesFree :: Ring a -> Int+    [A] byteLength :: Ring a -> Int+    [A] byteCapacity :: Ring a -> Int+    [A] asBytes :: Ring a -> Ring Word8+    [A] advance :: forall a. Storable a => Ring a -> Ptr a -> Ptr a+[R] Streamly.Internal.Data.Producer.Type+[R] Streamly.Internal.Data.Producer.Source+[C] Streamly.Internal.Data.Producer+    [A] Source+    [A] unread :: [b] -> Source a b -> Source a b+    [A] translate :: Functor m => (a -> c) -> (c -> a) -> Producer m c b -> Producer m a b+    [A] source :: Maybe a -> Source a b+    [A] producer :: Monad m => Producer m a b -> Producer m (Source a b) b+    [A] parseManyD :: Monad m => Parser a m b -> Producer m (Source x a) a -> Producer m (Source x a) (Either ParseError b)+    [A] parseMany :: Monad m => Parser a m b -> Producer m (Source x a) a -> Producer m (Source x a) (Either ParseError b)+    [A] parse :: Monad m => Parser a m b -> Producer m (Source s a) a -> Source s a -> m (Either ParseError b, Source s a)+    [A] lmap :: (a -> a) -> Producer m a b -> Producer m a b+    [A] isEmpty :: Source a b -> Bool+[R] Streamly.Internal.Data.Pipe.Type+[C] Streamly.Internal.Data.Pipe+    [A] Step+        [A] Yield :: a -> s -> Step s a+        [A] Continue :: s -> Step s a+    [A] PipeState+        [A] Produce :: s2 -> PipeState s1 s2+        [A] Consume :: s1 -> PipeState s1 s2+    [C] Pipe+        [A] Pipe :: (s1 -> a -> m (Step (PipeState s1 s2) b)) -> (s2 -> m (Step (PipeState s1 s2) b)) -> s1 -> Pipe m a b+[A] Streamly.Internal.Data.ParserK+    [A] Step+        [A] Partial :: !Int -> (Input a -> m (Step a m r)) -> Step a m r+        [A] Error :: !Int -> String -> Step a m r+        [A] Done :: !Int -> r -> Step a m r+        [A] Continue :: !Int -> (Input a -> m (Step a m r)) -> Step a m r+    [A] ParseResult+        [A] Success :: !Int -> !b -> ParseResult b+        [A] Failure :: !Int -> !String -> ParseResult b+    [A] Input+        [A] None :: Input a+        [A] Chunk :: a -> Input a+    [A] ParserK+        [A] [runParser] :: ParserK a m b -> forall r. (ParseResult b -> Int -> Input a -> m (Step a m r)) -> Int -> Int -> Input a -> m (Step a m r)+        [A] MkParser :: (forall r. (ParseResult b -> Int -> Input a -> m (Step a m r)) -> Int -> Int -> Input a -> m (Step a m r)) -> ParserK a m b+    [A] fromPure :: b -> ParserK a m b+    [A] fromEffect :: Monad m => m b -> ParserK a m b+    [A] die :: String -> ParserK a m b+    [A] adaptCG :: Monad m => Parser a m b -> ParserK (Array a) m b+    [A] adaptC :: (Monad m, Unbox a) => Parser a m b -> ParserK (Array a) m b+    [A] adapt :: Monad m => Parser a m b -> ParserK a m b+[R] Streamly.Internal.Data.Parser.ParserK.Type+[R] Streamly.Internal.Data.Parser.ParserD.Type+[R] Streamly.Internal.Data.Parser.ParserD.Tee+[R] Streamly.Internal.Data.Parser.ParserD+[C] Streamly.Internal.Data.Parser+    [A] Step+        [A] Partial :: !Int -> !s -> Step s b+        [A] Error :: !String -> Step s b+        [A] Done :: !Int -> !b -> Step s b+        [A] Continue :: !Int -> !s -> Step s b+    [A] Parser+        [A] Parser :: (s -> a -> m (Step s b)) -> m (Initial s b) -> (s -> m (Step s b)) -> Parser a m b+    [A] Initial+        [A] IPartial :: !s -> Initial s b+        [A] IError :: !String -> Initial s b+        [A] IDone :: !b -> Initial s b+    [A] GHC.Show.Show+        [A] instance (GHC.Show.Show a, GHC.Show.Show b) => GHC.Show.Show (Streamly.Internal.Data.Parser.Tuple'Fused a b)+    [A] ParseError+        [A] ParseError :: String -> ParseError+    [A] zipWithM :: Monad m => (a -> b -> m c) -> Stream m a -> Fold m c x -> Parser b m x+    [A] zip :: Monad m => Stream m a -> Fold m (a, b) x -> Parser b m x+    [A] wordWithQuotes :: (Monad m, Eq a) => Bool -> (a -> a -> Maybe a) -> a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] wordProcessQuotes :: (Monad m, Eq a) => a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] wordKeepQuotes :: (Monad m, Eq a) => a -> (a -> Maybe a) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] wordFramedBy :: Monad m => (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+    [A] toFold :: Monad m => Parser a m b -> Fold m a b+    [A] takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+    [A] takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeP :: Monad m => Int -> Parser a m b -> Parser a m b+    [A] takeGE :: Monad m => Int -> Fold m a b -> Parser a m b+    [A] takeFramedBy_ :: Monad m => (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeFramedByGeneric :: Monad m => Maybe (a -> Bool) -> Maybe (a -> Bool) -> Maybe (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeFramedByEsc_ :: Monad m => (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b+    [A] takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b+    [A] takeEndByEsc :: Monad m => (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b+    [A] takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+    [A] takeEitherSepBy :: (a -> Bool) -> Fold m (Either a b) c -> Parser a m c+    [A] takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b+    [A] takeBetween :: Monad m => Int -> Int -> Fold m a b -> Parser a m b+    [A] subsequenceBy :: (a -> a -> Bool) -> Stream m a -> Parser a m ()+    [A] streamEqBy :: Monad m => (a -> a -> Bool) -> Stream m a -> Parser a m ()+    [A] split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b+    [A] splitWith :: Monad m => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+    [A] splitSome :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+    [A] splitManyPost :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+    [A] splitMany :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+    [A] spanByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+    [A] spanBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+    [A] span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+    [A] some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+    [A] sequence :: Monad m => Stream m (Parser a m b) -> Fold m b c -> Parser a m c+    [A] sepByAll :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+    [A] sepBy1 :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+    [A] sepBy :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+    [A] satisfy :: Monad m => (a -> Bool) -> Parser a m a+    [A] sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Parser a m b+    [A] roundRobin :: t (Parser a m b) -> Fold m b c -> Parser a m c+    [A] rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c+    [A] retryMaxTotal :: Int -> Parser a m b -> Fold m b c -> Parser a m c+    [A] retryMaxSuccessive :: Int -> Parser a m b -> Fold m b c -> Parser a m c+    [A] retry :: Parser a m b -> Parser a m b+    [A] postscan :: Fold m a b -> Parser b m c -> Parser a m c+    [A] peek :: Monad m => Parser a m a+    [A] oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a+    [A] oneNotEq :: (Monad m, Eq a) => a -> Parser a m a+    [A] oneEq :: (Monad m, Eq a) => a -> Parser a m a+    [A] one :: Monad m => Parser a m a+    [A] noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a+    [A] noErrorUnsafeSplit_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b+    [A] noErrorUnsafeSplitWith :: Monad m => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+    [A] noErrorUnsafeConcatMap :: Monad m => (b -> Parser a m c) -> Parser a m b -> Parser a m c+    [A] maybe :: Monad m => (a -> Maybe b) -> Parser a m b+    [A] manyTillP :: Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c+    [A] manyTill :: Monad m => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+    [A] manyThen :: Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+    [A] manyP :: Parser a m b -> Parser b m c -> Parser a m c+    [A] many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+    [A] makeIndexFilter :: (Fold m (s, a) b -> Parser a m b) -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b) -> ((s, a) -> Bool) -> Fold m a b -> Parser a m b+    [A] lookAhead :: Monad m => Parser a m b -> Parser a m b+    [A] lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r+    [A] lmap :: (a -> b) -> Parser b m r -> Parser a m r+    [A] listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]+    [A] listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]+    [A] indexed :: forall m a b. Monad m => Fold m (Int, a) b -> Parser a m b+    [A] groupByRollingEither :: Monad m => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)+    [A] groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b+    [A] groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b+    [A] fromPure :: Monad m => b -> Parser a m b+    [A] fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b+    [A] fromFold :: Monad m => Fold m a b -> Parser a m b+    [A] fromEffect :: Monad m => m b -> Parser a m b+    [A] filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+    [A] extractStep :: Monad m => (s -> m (Step s1 b)) -> Step s b -> m (Step s1 b)+    [A] eof :: Monad m => Parser a m ()+    [A] either :: Monad m => (a -> Either String b) -> Parser a m b+    [A] dropWhile :: Monad m => (a -> Bool) -> Parser a m ()+    [A] dieM :: Monad m => m String -> Parser a m b+    [A] die :: Monad m => String -> Parser a m b+    [A] deintercalateAll :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z+    [A] deintercalate1 :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z+    [A] deintercalate :: Monad m => Parser a m x -> Parser a m y -> Fold m (Either x y) z -> Parser a m z+    [A] countBetween :: Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c+    [A] count :: Int -> Parser a m b -> Fold m b c -> Parser a m c+    [A] concatMap :: Monad m => (b -> Parser a m c) -> Parser a m b -> Parser a m c+    [A] blockWithQuotes :: (Monad m, Eq a) => (a -> Bool) -> (a -> Bool) -> a -> a -> Fold m a b -> Parser a m b+    [A] bimapOverrideCount :: Int -> (s -> s1) -> (b -> b1) -> Step s b -> Step s1 b1+    [A] alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a+[A] Streamly.Internal.Data.MutByteArray+    [A] class Unbox a+    [A] class SizeOfRep (f :: Type -> Type)+    [A] class Serialize a+    [A] TypeOfType+        [A] UnitType :: Name -> TypeOfType+        [A] TheType :: SimpleDataCon -> TypeOfType+        [A] MultiType :: [SimpleDataCon] -> TypeOfType+    [A] SimpleDataCon+        [A] SimpleDataCon :: Name -> [Field] -> SimpleDataCon+    [A] SerializeConfig+        [A] [cfgRecordSyntaxWithHeader] :: SerializeConfig -> Bool+        [A] [cfgInlineSize] :: SerializeConfig -> Maybe Inline+        [A] [cfgInlineSerialize] :: SerializeConfig -> Maybe Inline+        [A] [cfgInlineDeserialize] :: SerializeConfig -> Maybe Inline+        [A] [cfgConstructorTagAsString] :: SerializeConfig -> Bool+        [A] SerializeConfig :: Maybe Inline -> Maybe Inline -> Maybe Inline -> Bool -> Bool -> SerializeConfig+    [A] PinnedState+        [A] Unpinned :: PinnedState+        [A] Pinned :: PinnedState+    [A] MutByteArray+        [A] MutByteArray :: MutableByteArray# RealWorld -> MutByteArray+    [A] DataType+        [A] [dtTvs] :: DataType -> [Name]+        [A] [dtName] :: DataType -> Name+        [A] [dtCxt] :: DataType -> Cxt+        [A] [dtCons] :: DataType -> [DataCon]+        [A] DataType :: Name -> [Name] -> Cxt -> [DataCon] -> DataType+    [A] DataCon+        [A] [dcTvs] :: DataCon -> [Name]+        [A] [dcName] :: DataCon -> Name+        [A] [dcFields] :: DataCon -> [(Maybe Name, Type)]+        [A] [dcCxt] :: DataCon -> Cxt+        [A] DataCon :: Name -> [Name] -> Cxt -> [(Maybe Name, Type)] -> DataCon+    [A] BoundedPtr+        [A] BoundedPtr :: MutByteArray -> Int -> Int -> BoundedPtr+    [A] Streamly.Internal.Data.Serialize.Type.Serialize+        [A] instance Streamly.Internal.Data.Serialize.Type.Serialize Streamly.Internal.Data.MutByteArray.LiftedInteger+        [A] instance Streamly.Internal.Data.Serialize.Type.Serialize GHC.Num.Integer.Integer+        [A] instance Streamly.Internal.Data.Serialize.Type.Serialize a => Streamly.Internal.Data.Serialize.Type.Serialize (GHC.Maybe.Maybe a)+        [A] instance Streamly.Internal.Data.Serialize.Type.Serialize (Data.Proxy.Proxy a)+        [A] instance (Streamly.Internal.Data.Serialize.Type.Serialize a, Streamly.Internal.Data.Serialize.Type.Serialize b) => Streamly.Internal.Data.Serialize.Type.Serialize (Data.Either.Either a b)+    [A] Peeker+        [A] Peeker :: Builder BoundedPtr IO a -> Peeker a+    [A] type Field = (Maybe Name, Type)+    [A] xorCmp :: [Word8] -> Name -> Name -> Q Exp+    [A] wListToString :: [Word8] -> String+    [A] w8_int :: Word8 -> Int+    [A] w32_int :: Word32 -> Int+    [A] unpin :: MutByteArray -> IO MutByteArray+    [A] typeOfType :: Type -> [DataCon] -> TypeOfType+    [A] skipByte :: Peeker ()+    [A] sizeOfRep :: SizeOfRep f => f x -> Int+    [A] sizeOfMutableByteArray :: MutByteArray -> IO Int+    [A] sizeOf :: (Unbox a, SizeOfRep (Rep a)) => Proxy a -> Int+    [A] simplifyDataCon :: DataCon -> SimpleDataCon+    [A] serializeW8List :: Name -> Name -> [Word8] -> Q Exp+    [A] serializeConfig :: SerializeConfig+    [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int+    [A] runPeeker :: Peeker a -> BoundedPtr -> IO a+    [A] reifyDataType :: Name -> Q DataType+    [A] readUnsafe :: Unbox a => Peeker a+    [A] read :: Unbox a => Peeker a+    [A] putSliceUnsafe :: MonadIO m => MutByteArray -> Int -> MutByteArray -> Int -> Int -> m ()+    [A] pokeRep :: PokeRep f => f a -> BoundedPtr -> IO BoundedPtr+    [A] pokeBoundedPtrUnsafe :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr+    [A] pokeBoundedPtr :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr+    [A] pokeAt :: (Unbox a, Generic a, PokeRep (Rep a)) => Int -> MutByteArray -> a -> IO ()+    [A] pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray+    [A] pinnedNew :: Int -> IO MutByteArray+    [A] pinnedCloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray+    [A] pin :: MutByteArray -> IO MutByteArray+    [A] peekRep :: PeekRep f => Peeker (f x)+    [A] peekAt :: (Unbox a, Generic a, PeekRep (Rep a)) => Int -> MutByteArray -> IO a+    [A] openConstructor :: Name -> Int -> Q Pat+    [A] nil :: MutByteArray+    [A] newBytesAs :: PinnedState -> Int -> IO MutByteArray+    [A] new :: Int -> IO MutByteArray+    [A] mkSerializeExprFields :: Name -> [Field] -> Q Exp+    [A] mkRecSizeOfExpr :: SimpleDataCon -> Q Exp+    [A] mkRecSerializeExpr :: Name -> SimpleDataCon -> Q Exp+    [A] mkRecDeserializeExpr :: Name -> Name -> Name -> SimpleDataCon -> Q Exp+    [A] mkFieldName :: Int -> Name+    [A] mkDeserializeKeysDec :: Name -> Name -> SimpleDataCon -> Q [Dec]+    [A] mkDeserializeExprOne :: Name -> SimpleDataCon -> Q Exp+    [A] matchConstructor :: Name -> Int -> Q Exp -> Q Match+    [A] makeN :: Int -> Name+    [A] makeI :: Int -> Name+    [A] makeA :: Int -> Name+    [A] litProxy :: Unbox a => Proxy a -> Q Exp+    [A] litIntegral :: Integral a => a -> Q Exp+    [A] isUnitType :: [DataCon] -> Bool+    [A] isRecordSyntax :: SimpleDataCon -> Bool+    [A] isPinned :: MutByteArray -> Bool+    [A] int_w8 :: Int -> Word8+    [A] int_w32 :: Int -> Word32+    [A] inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig+    [A] getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld+    [A] genericSizeOf :: forall a. SizeOfRep (Rep a) => Proxy a -> Int+    [A] genericPokeByteIndex :: (Generic a, PokeRep (Rep a)) => MutByteArray -> Int -> a -> IO ()+    [A] genericPeekByteIndex :: (Generic a, PeekRep (Rep a)) => MutByteArray -> Int -> IO a+    [A] errorUnsupported :: String -> a+    [A] errorUnimplemented :: a+    [A] encodeRecordFields :: Bool -> SerializeConfig -> SerializeConfig+    [A] encodeConstrNames :: Bool -> SerializeConfig -> SerializeConfig+    [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)+    [A] deriveUnbox :: Q [Dec] -> Q [Dec]+    [A] deriveSerializeWith :: (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]+    [A] deriveSerialize :: Q [Dec] -> Q [Dec]+    [A] conUpdateFuncDec :: Name -> [Field] -> Q [Dec]+    [A] cloneSliceUnsafeAs :: MonadIO m => PinnedState -> Int -> Int -> MutByteArray -> m MutByteArray+    [A] cloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray+    [A] c2w :: Char -> Word8+    [A] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+    [A] addSizeTo :: Serialize a => Int -> a -> Int+    [A] _x :: Name+    [A] _val :: Name+    [A] _tag :: Name+    [A] _initialOffset :: Name+    [A] _endOffset :: Name+    [A] _arr :: Name+    [A] _acc :: Name+[A] Streamly.Internal.Data.MutArray.Stream+    [A] SpliceState+        [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr+        [A] SpliceInitial :: s -> SpliceState s arr+        [A] SpliceFinish :: SpliceState s arr+        [A] SpliceBuffering :: s -> arr -> SpliceState s arr+    [A] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))+    [A] splitOn :: (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> Stream m (MutArray a)+    [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)+    [A] packArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] lpackArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+    [A] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)+    [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (Either ParseError (MutArray a))+    [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] compact :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+    [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)+[A] Streamly.Internal.Data.MutArray.Generic+    [A] MutArray+        [A] [arrTrueLen] :: MutArray a -> {-# UNPACK #-} !Int+        [A] [arrStart] :: MutArray a -> {-# UNPACK #-} !Int+        [A] [arrLen] :: MutArray a -> {-# UNPACK #-} !Int+        [A] [arrContents#] :: MutArray a -> MutableArray# RealWorld a+        [A] MutArray :: MutableArray# RealWorld a -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> MutArray a+    [A] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] writeN :: MonadIO m => Int -> Fold m a (MutArray a)+    [A] write :: MonadIO m => Fold m a (MutArray a)+    [A] uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)+    [A] toStreamK :: MonadIO m => MutArray a -> StreamK m a+    [A] toList :: MonadIO m => MutArray a -> m [a]+    [A] strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)+    [A] snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)+    [A] snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)+    [A] snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)+    [A] realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)+    [A] reader :: MonadIO m => Unfold m (MutArray a) a+    [A] readRev :: MonadIO m => MutArray a -> Stream m a+    [A] read :: MonadIO m => MutArray a -> Stream m a+    [A] putSliceUnsafe :: MonadIO m => MutArray a -> Int -> MutArray a -> Int -> Int -> m ()+    [A] putIndices :: MonadIO m => MutArray a -> Fold m (Int, a) ()+    [A] putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()+    [A] putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()+    [A] producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a+    [A] producer :: MonadIO m => Producer m (MutArray a) a+    [A] nil :: MonadIO m => m (MutArray a)+    [A] new :: MonadIO m => Int -> m (MutArray a)+    [A] modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] length :: MutArray a -> Int+    [A] getSliceUnsafe :: Int -> Int -> MutArray a -> MutArray a+    [A] getSlice :: Int -> Int -> MutArray a -> MutArray a+    [A] getIndexUnsafeWith :: MonadIO m => MutableArray# RealWorld a -> Int -> m a+    [A] getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a+    [A] getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)+    [A] fromStreamN :: MonadIO m => Int -> Stream m a -> m (MutArray a)+    [A] fromStream :: MonadIO m => Stream m a -> m (MutArray a)+    [A] fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)+    [A] fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)+    [A] fromList :: MonadIO m => [a] -> m (MutArray a)+    [A] eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool+    [A] cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering+    [A] clone :: MonadIO m => MutArray a -> m (MutArray a)+    [A] chunksOf :: forall m a. MonadIO m => Int -> Stream m a -> Stream m (MutArray a)+[A] Streamly.Internal.Data.MutArray+    [A] MutByteArray+    [A] MutArray+        [A] [arrStart] :: MutArray a -> {-# UNPACK #-} !Int+        [A] [arrEnd] :: MutArray a -> {-# UNPACK #-} !Int+        [A] [arrContents] :: MutArray a -> {-# UNPACK #-} !MutByteArray+        [A] [arrBound] :: MutArray a -> {-# UNPACK #-} !Int+        [A] MutArray :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> MutArray a+    [A] IORef+    [A] ArrayUnsafe+        [A] ArrayUnsafe :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> ArrayUnsafe a+    [A] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+    [A] writeNWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+    [A] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] writeIORef :: Unbox a => IORef a -> a -> IO ()+    [A] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))+    [A] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+    [A] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] writeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)+    [A] writeAppend :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)+    [A] write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [A] unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> MutArray a -> m ()+    [A] unpin :: MutArray a -> IO (MutArray a)+    [A] toStreamKWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+    [A] toStreamKRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+    [A] toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+    [A] toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+    [A] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [A] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a+    [A] toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]+    [A] swapIndices :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> MutArray a -> m ()+    [A] strip :: forall a m. (Unbox a, MonadIO m) => (a -> Bool) -> MutArray a -> m (MutArray a)+    [A] splitOn :: (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> Stream m (MutArray a)+    [A] splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)+    [A] spliceWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)+    [A] spliceUnsafe :: MonadIO m => MutArray a -> MutArray a -> m (MutArray a)+    [A] spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+    [A] spliceCopy :: forall m a. MonadIO m => MutArray a -> MutArray a -> m (MutArray a)+    [A] splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+    [A] snocWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> MutArray a -> a -> m (MutArray a)+    [A] snocUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+    [A] snocMay :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (Maybe (MutArray a))+    [A] snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+    [A] snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+    [A] shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()+    [A] roundUpToPower2 :: Int -> Int+    [A] rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)+    [A] reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()+    [A] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [A] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [A] realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+    [A] readerRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Unfold m (MutArray a) a+    [A] readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+    [A] reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+    [A] readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a+    [A] readIORef :: Unbox a => IORef a -> IO a+    [A] read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Stream m a+    [A] putIndices :: forall m a. (MonadIO m, Unbox a) => MutArray a -> Fold m (Int, a) ()+    [A] putIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()+    [A] putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()+    [A] producerWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Producer m (MutArray a) a+    [A] producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a+    [A] pollIntIORef :: (MonadIO m, Unbox a) => IORef a -> Stream m a+    [A] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (MutArray a)+    [A] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+    [A] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+    [A] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)+    [A] pinnedNewAligned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)+    [A] pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] pinnedFromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+    [A] pinnedFromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+    [A] pinnedClone :: MonadIO m => MutArray a -> m (MutArray a)+    [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)+    [A] pin :: MutArray a -> IO (MutArray a)+    [A] permute :: MutArray a -> m Bool+    [A] partitionBy :: forall m a. (MonadIO m, Unbox a) => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)+    [A] nil :: MutArray a+    [A] newIORef :: forall a. Unbox a => a -> IO (IORef a)+    [A] newArrayWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int -> m MutByteArray) -> Int -> Int -> m (MutArray a)+    [A] new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+    [A] modifyIndices :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (Int -> a -> a) -> Fold m Int ()+    [A] modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] modifyIndex :: forall m a b. (MonadIO m, Unbox a) => Int -> MutArray a -> (a -> (a, b)) -> m b+    [A] modifyIORef' :: Unbox a => IORef a -> (a -> a) -> IO ()+    [A] modify :: forall m a. (MonadIO m, Unbox a) => MutArray a -> (a -> a) -> m ()+    [A] mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()+    [A] memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()+    [A] memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool+    [A] length :: forall a. Unbox a => MutArray a -> Int+    [A] isPinned :: MutArray a -> Bool+    [A] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)+    [A] getSliceUnsafe :: forall a. Unbox a => Int -> Int -> MutArray a -> MutArray a+    [A] getSlice :: forall a. Unbox a => Int -> Int -> MutArray a -> MutArray a+    [A] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a+    [A] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+    [A] getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+    [A] getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+    [A] getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)+    [A] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)+    [A] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)+    [A] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)+    [A] fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)+    [A] fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)+    [A] fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+    [A] fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+    [A] fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+    [A] fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+    [A] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)+    [A] foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b+    [A] foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b+    [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a+    [A] divideBy :: Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()+    [A] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+    [A] clone :: MonadIO m => MutArray a -> m (MutArray a)+    [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (MutArray a)+    [A] castUnsafe :: MutArray a -> MutArray b+    [A] cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)+    [A] c_memchr :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)+    [A] bytesFree :: MutArray a -> Int+    [A] byteLength :: MutArray a -> Int+    [A] byteCapacity :: MutArray a -> Int+    [A] bubble :: (MonadIO m, Unbox a) => (a -> a -> Ordering) -> MutArray a -> m ()+    [A] breakOn :: MonadIO m => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))+    [A] blockSize :: Int+    [A] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+    [A] asBytes :: MutArray a -> MutArray Word8+    [A] arrayChunkBytes :: Int+    [A] allocBytesToElemCount :: Unbox a => a -> Int -> Int+[C] Streamly.Internal.Data.IsMap+    [A] mapTraverseWithKey :: (IsMap f, Applicative t) => (Key f -> a -> t b) -> f a -> t (f b)+[R] Streamly.Internal.Data.IORef.Unboxed+[R] Streamly.Internal.Data.Fold.Window+[R] Streamly.Internal.Data.Fold.Type+[R] Streamly.Internal.Data.Fold.Tee+[R] Streamly.Internal.Data.Fold.Step+[R] Streamly.Internal.Data.Fold.Container+[D] Streamly.Internal.Data.Fold.Chunked+[C] Streamly.Internal.Data.Fold+    [A] ManyState+    [C] Fold+        [C] Fold+            [O] Fold :: (s -> a -> m (Step s b)) -> m (Step s b) -> (s -> m b) -> Fold m a b+            [N] Fold :: (s -> a -> m (Step s b)) -> m (Step s b) -> (s -> m b) -> (s -> m b) -> Fold m a b+    [A] windowSumInt :: forall m a. (Monad m, Integral a) => Fold m (a, Maybe a) a+    [A] windowSum :: forall m a. (Monad m, Num a) => Fold m (a, Maybe a) a+    [A] windowRollingMapM :: Monad m => (Maybe a -> a -> m (Maybe b)) -> Fold m (a, Maybe a) (Maybe b)+    [A] windowRollingMap :: Monad m => (Maybe a -> a -> Maybe b) -> Fold m (a, Maybe a) (Maybe b)+    [A] windowRange :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe (a, a))+    [A] windowPowerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a+    [A] windowPowerSum :: (Monad m, Num a) => Int -> Fold m (a, Maybe a) a+    [A] windowMinimum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)+    [A] windowMean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a+    [A] windowMaximum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)+    [A] windowLmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b+    [A] windowLength :: (Monad m, Num b) => Fold m (a, Maybe a) b+    [A] toSet :: (Monad m, Ord a) => Fold m a (Set a)+    [A] toMapIO :: (MonadIO m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (Map k b)+    [A] toMap :: (Monad m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (Map k b)+    [A] toIntSet :: Monad m => Fold m Int IntSet+    [A] toContainerIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (f b)+    [A] toContainer :: (Monad m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (f b)+    [A] nubInt :: Monad m => Fold m Int (Maybe Int)+    [A] nub :: (Monad m, Ord a) => Fold m a (Maybe a)+    [A] mapMStep :: Applicative m => (a -> m b) -> Step s a -> m (Step s b)+    [A] kvToMap :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)+    [A] frequency :: (Monad m, Ord a) => Fold m a (Map a Int)+    [A] demuxToMapIO :: (MonadIO m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)+    [A] demuxToMap :: (Monad m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (Map k b)+    [A] demuxToContainerIO :: (MonadIO m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)+    [A] demuxToContainer :: (Monad m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)+    [A] demuxKvToMap :: (Monad m, Ord k) => (k -> m (Fold m a b)) -> Fold m (k, a) (Map k b)+    [A] demuxKvToContainer :: (Monad m, IsMap f, Traversable f) => (Key f -> m (Fold m a b)) -> Fold m (Key f, a) (f b)+    [A] demuxIO :: (MonadIO m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (m (Map k b), Maybe (k, b))+    [A] demuxGenericIO :: (MonadIO m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (m (f b), Maybe (Key f, b))+    [A] demuxGeneric :: (Monad m, IsMap f, Traversable f) => (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (m (f b), Maybe (Key f, b))+    [A] demux :: (Monad m, Ord k) => (a -> k) -> (a -> m (Fold m a b)) -> Fold m a (m (Map k b), Maybe (k, b))+    [A] cumulative :: Fold m (a, Maybe a) b -> Fold m a b+    [A] countDistinctInt :: Monad m => Fold m Int Int+    [A] countDistinct :: (Monad m, Ord a) => Fold m a Int+    [A] classifyIO :: (MonadIO m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))+    [A] classifyGenericIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))+    [A] classifyGeneric :: (Monad m, IsMap f, Traversable f, Ord (Key f)) => (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))+    [A] classify :: (Monad m, Ord k) => (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b))+    [A] chainStepM :: Applicative m => (s1 -> m s2) -> (a -> m (Step s2 b)) -> Step s1 a -> m (Step s2 b)+[C] Streamly.Internal.Data.Builder+    [C] Builder+        [C] Builder+            [O] Builder :: (s -> m (s, a)) -> Builder s m a+            [N] Builder :: (s -> m (a, s)) -> Builder s m a+[A] Streamly.Internal.Data.Binary.Stream+    [A] class ToBytes a+    [A] word8 :: Applicative m => Word8 -> Stream m Word8+    [A] word64le :: Monad m => Word64 -> Stream m Word8+    [A] word64host :: Monad m => Word64 -> Stream m Word8+    [A] word64be :: Monad m => Word64 -> Stream m Word8+    [A] word32le :: Monad m => Word32 -> Stream m Word8+    [A] word32be :: Monad m => Word32 -> Stream m Word8+    [A] word16le :: Monad m => Word16 -> Stream m Word8+    [A] word16be :: Monad m => Word16 -> Stream m Word8+    [A] unit :: Applicative m => Stream m Word8+    [A] toBytes :: ToBytes a => a -> Stream m Word8+    [A] ordering :: Applicative m => Ordering -> Stream m Word8+    [A] int8 :: Applicative m => Int8 -> Stream m Word8+    [A] int64le :: Monad m => Int64 -> Stream m Word8+    [A] int64be :: Monad m => Int64 -> Stream m Word8+    [A] int32le :: Monad m => Int32 -> Stream m Word8+    [A] int32be :: Monad m => Int32 -> Stream m Word8+    [A] int16le :: Monad m => Int16 -> Stream m Word8+    [A] int16be :: Monad m => Int16 -> Stream m Word8+    [A] float32le :: Monad m => Float -> Stream m Word8+    [A] float32be :: Monad m => Float -> Stream m Word8+    [A] double64le :: Monad m => Double -> Stream m Word8+    [A] double64be :: Monad m => Double -> Stream m Word8+    [A] charUtf8 :: Monad m => Char -> Stream m Word8+    [A] charLatin1 :: Applicative m => Char -> Stream m Word8+    [A] bool :: Applicative m => Bool -> Stream m Word8+[A] Streamly.Internal.Data.Binary.Parser+    [A] class FromBytes a+    [A] word8 :: Monad m => Parser Word8 m Word8+    [A] word64le :: Monad m => Parser Word8 m Word64+    [A] word64host :: MonadIO m => Parser Word8 m Word64+    [A] word64be :: Monad m => Parser Word8 m Word64+    [A] word32le :: Monad m => Parser Word8 m Word32+    [A] word32be :: Monad m => Parser Word8 m Word32+    [A] word16le :: Monad m => Parser Word8 m Word16+    [A] word16be :: Monad m => Parser Word8 m Word16+    [A] unit :: Monad m => Parser Word8 m ()+    [A] ordering :: Monad m => Parser Word8 m Ordering+    [A] int8 :: Monad m => Parser Word8 m Int8+    [A] int64le :: Monad m => Parser Word8 m Int64+    [A] int64be :: Monad m => Parser Word8 m Int64+    [A] int32le :: Monad m => Parser Word8 m Int32+    [A] int32be :: Monad m => Parser Word8 m Int32+    [A] int16le :: Monad m => Parser Word8 m Int16+    [A] int16be :: Monad m => Parser Word8 m Int16+    [A] fromBytes :: FromBytes a => Parser Word8 m a+    [A] float32le :: MonadIO m => Parser Word8 m Float+    [A] float32be :: MonadIO m => Parser Word8 m Float+    [A] eqWord8 :: Monad m => Word8 -> Parser Word8 m Word8+    [A] double64le :: MonadIO m => Parser Word8 m Double+    [A] double64be :: MonadIO m => Parser Word8 m Double+    [A] charLatin1 :: Monad m => Parser Word8 m Char+    [A] bool :: Monad m => Parser Word8 m Bool+[R] Streamly.Internal.Data.Array.Type+[A] Streamly.Internal.Data.Array.Stream+    [A] unlines :: forall m a. (MonadIO m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [A] toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)+    [A] splitOnSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)+    [A] splitOn :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)+    [A] runArrayParserDBreak :: forall m a b. (MonadIO m, Unbox a) => Parser (Array a) m b -> Stream m (Array a) -> m (Either ParseError b, Stream m (Array a))+    [A] runArrayFoldMany :: (Monad m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> StreamK m (Either ParseError b)+    [A] runArrayFoldBreak :: (MonadIO m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] runArrayFold :: (MonadIO m, Unbox a) => ChunkFold m a b -> StreamK m (Array a) -> m (Either ParseError b)+    [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)+    [A] parseChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)+    [A] parseBreakChunks :: (Monad m, Unbox a) => ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] parseBreak :: (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))+    [A] lpackArraysChunksOf :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()+    [A] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+    [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a+    [A] foldBreakD :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))+    [A] foldBreak :: (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))+    [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] compact :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)+    [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)+    [A] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))+[R] Streamly.Internal.Data.Array.Mut.Type+[R] Streamly.Internal.Data.Array.Mut.Stream+[R] Streamly.Internal.Data.Array.Mut+[R] Streamly.Internal.Data.Array.Generic.Mut.Type+[C] Streamly.Internal.Data.Array.Generic+    [A] getIndex :: Int -> Array a -> Maybe a+    [A] fromPureStream :: Stream Identity a -> Array a+    [A] fromByteStr# :: Addr# -> Array Word8+    [A] chunksOf :: forall m a. MonadIO m => Int -> Stream m a -> Stream m (Array a)+[C] Streamly.Internal.Data.Array+    [A] ArrayUnsafe+        [A] ArrayUnsafe :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> ArrayUnsafe a+    [C] Array+        [A] [arrStart] :: Array a -> {-# UNPACK #-} !Int+        [A] [arrEnd] :: Array a -> {-# UNPACK #-} !Int+        [A] [arrContents] :: Array a -> {-# UNPACK #-} !MutByteArray+        [A] Array :: {-# UNPACK #-} !MutByteArray -> {-# UNPACK #-} !Int -> {-# UNPACK #-} !Int -> Array a+    [A] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [R] writeNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)+    [A] unsafeMakePure :: Monad m => Fold IO a b -> Fold m a b+    [A] unsafeIndexIO :: forall a. Unbox a => Int -> Array a -> IO a+    [D] unsafeIndex :: forall a. Unbox a => Int -> Array a -> a+    [A] unsafeFreezeWithShrink :: Unbox a => MutArray a -> Array a+    [A] unpin :: Array a -> IO (Array a)+    [A] toStreamKRev :: forall m a. (Monad m, Unbox a) => Array a -> StreamK m a+    [A] toStreamK :: forall m a. (Monad m, Unbox a) => Array a -> StreamK m a+    [A] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a+    [A] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a+    [A] splitAt :: Unbox a => Int -> Array a -> (Array a, Array a)+    [A] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)+    [A] serialize :: Serialize a => a -> Array Word8+    [A] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)+    [A] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+    [A] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+    [A] pinnedSerialize :: Serialize a => a -> Array Word8+    [A] pinnedFromListN :: Unbox a => Int -> [a] -> Array a+    [A] pinnedFromList :: Unbox a => [a] -> Array a+    [A] pinnedClone :: MonadIO m => Array a -> m (Array a)+    [A] pinnedChunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)+    [A] pin :: Array a -> IO (Array a)+    [A] nil :: Array a+    [A] isPinned :: Array a -> Bool+    [A] getIndexUnsafe :: forall a. Unbox a => Int -> Array a -> a+    [A] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)+    [A] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)+    [A] fromPureStream :: Unbox a => Stream Identity a -> Array a+    [A] fromListRevN :: Unbox a => Int -> [a] -> Array a+    [A] fromListRev :: Unbox a => [a] -> Array a+    [A] fromByteStr# :: Addr# -> Array Word8+    [A] foldr :: Unbox a => (a -> b -> b) -> b -> Array a -> b+    [A] foldl' :: forall a b. Unbox a => (b -> a -> b) -> b -> Array a -> b+    [A] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a+    [A] encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8+    [A] deserialize :: Serialize a => Array Word8 -> a+    [A] clone :: MonadIO m => Array a -> m (Array a)+    [A] chunksOf :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> Stream m (Array a)+    [A] byteLength :: Array a -> Int+    [A] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))+    [A] breakOn :: MonadIO m => Word8 -> Array Word8 -> m (Array Word8, Maybe (Array Word8))
docs/Changelog.md view
@@ -1,8 +1,59 @@ # Changelog +## 0.2.0 (Nov 2023)++See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)+for a full list of API changes in this release. Only a few significant+changes are mentioned here.++### Breaking Changes++* `ParserK` in `Streamly.Data.ParserK` is not implicitly specialized+  to arrays anymore. To adapt to the new code, change `ParserK a m+  b` to `ParserK (Array a) m b` where the `Array` type comes from+  `Streamly.Data.Array`. This change also affected the signatures of+  `parseChunks` and `parseBreakChunks`.+* Changed the signature of 'Streamly.Data.Stream.handle' to make the+  exception handler monadic.+* Behavior change: Exceptions are now rethrown promptly in `bracketIO`.++### Enhancements++* __Serialization__: Added a `Streamly.Data.MutByteArray` module with a+  `Serialize` type class for fast binary serialization. The Data.Array+  module supplies the `serialize` and `deserialize` operations for arrays.+* __Unpinned Arrays__: Unboxed arrays are now created unpinned by default,+  they were created pinned earlier. During IO operations, unpinned arrays+  are automatically copied to pinned memory. When arrays are directly+  passed to IO operations programmers can choose to create them pinned to+  avoid a copy.  To create pinned arrays, use the internal APIs with the+  `pinned*` prefix.+* StreamK now supports native exception handling routines (handle, bracketIO).+  Earlier we had to convert it to the `Stream` type for exception handling.++### Deprecations++See [0.1.0-0.2.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0-0.2.0.txt)+for a full list of deprecations.++### Internal API Changes++* Fold constructor has changed, added a `final` field to support+  finalization and cleanup of a chain of folds. The `extract` field is+  now used only for mapping the fold internal state to fold result for+  scanning purposes.+* Many low level internal modules have been removed, they are entirely+  exported from higher level internal modules. If you were importing any+  of the missing low level modules then import the higher level modules instead.+* Internal module changes:+  * Streamly.Internal.Serialize.FromBytes -> Streamly.Internal.Data.Binary.Parser+  * Streamly.Internal.Serialize.ToBytes ->   Streamly.Internal.Data.Binary.Stream+  * Streamly.Internal.Data.Unbox is now exported via Streamly.Internal.Data.Serialize+  * Streamly.Internal.Data.IORef.Unboxed is now exported via Streamly.Internal.Data.Serialize+ ## 0.1.0 (March 2023) -Also see [streamly-core-0.1.0 API Changelog](/core/docs/ApiChangelogs/0.1.0.txt) or+Also see [streamly-core-0.1.0 API Changelog](https://github.com/composewell/streamly/blob/streamly-0.10.0/core/docs/ApiChangelogs/0.1.0.txt) or https://hackage.haskell.org/package/streamly-core-0.1.0/docs/docs/ApiChangelogs/0.1.0.txt  `streamly` package is split into two packages, (1) `streamly-core` that
src/DocTestDataArray.hs view
@@ -1,6 +1,7 @@ {- $setup >>> :m >>> :set -XFlexibleContexts+>>> :set -XMagicHash >>> import Data.Function ((&)) >>> import Data.Functor.Identity (Identity(..)) >>> import System.IO.Unsafe (unsafePerformIO)@@ -11,4 +12,8 @@ >>> import qualified Streamly.Data.Array as Array >>> import qualified Streamly.Data.Fold as Fold >>> import qualified Streamly.Data.Stream as Stream++For APIs that have not been released yet.++>>> import qualified Streamly.Internal.Data.Array as Array -}
src/DocTestDataFold.hs view
@@ -24,5 +24,4 @@ For APIs that have not been released yet.  >>> import qualified Streamly.Internal.Data.Fold as Fold->>> import qualified Streamly.Internal.Data.Fold.Window as FoldW -}
src/DocTestDataMutArray.hs view
@@ -6,5 +6,5 @@  For APIs that have not been released yet. ->>> import Streamly.Internal.Data.Array.Mut as MutArray+>>> import Streamly.Internal.Data.MutArray as MutArray -}
src/DocTestDataMutArrayGeneric.hs view
@@ -6,5 +6,5 @@  For APIs that have not been released yet. ->>> import Streamly.Internal.Data.Array.Generic.Mut.Type as MutArray+>>> import Streamly.Internal.Data.MutArray.Generic as MutArray -}
+ src/DocTestDataParserK.hs view
@@ -0,0 +1,15 @@+{- $setup+>>> :m+>>> import Control.Applicative ((<|>))+>>> import Data.Char (isDigit, isAlpha)++>>> import Streamly.Data.Parser (Parser)+>>> import Streamly.Data.ParserK (ParserK)++>>> import qualified Streamly.Data.Parser as Parser+>>> import qualified Streamly.Data.ParserK as ParserK++For APIs that have not been released yet.++>>> import qualified Streamly.Internal.Data.ParserK as ParserK+-}
src/DocTestDataStream.hs view
@@ -22,6 +22,7 @@ >>> import qualified Streamly.Data.Array as Array >>> import qualified Streamly.Data.Fold as Fold >>> import qualified Streamly.Data.Stream as Stream+>>> import qualified Streamly.Data.StreamK as StreamK >>> import qualified Streamly.Data.Unfold as Unfold >>> import qualified Streamly.Data.Parser as Parser >>> import qualified Streamly.FileSystem.Dir as Dir@@ -29,7 +30,6 @@ For APIs that have not been released yet.  >>> import qualified Streamly.Internal.Data.Fold as Fold->>> import qualified Streamly.Internal.Data.Fold.Window as Window >>> import qualified Streamly.Internal.Data.Parser as Parser >>> import qualified Streamly.Internal.Data.Stream as Stream >>> import qualified Streamly.Internal.Data.Unfold as Unfold
src/DocTestDataStreamK.hs view
@@ -1,6 +1,7 @@ {- $setup  >>> :m+>>> import Control.Concurrent (threadDelay) >>> import Data.Function (fix, (&)) >>> import Data.Semigroup (cycle1) @@ -15,6 +16,6 @@  For APIs that have not been released yet. ->>> import qualified Streamly.Internal.Data.Stream.StreamK as StreamK+>>> import qualified Streamly.Internal.Data.StreamK as StreamK >>> import qualified Streamly.Internal.FileSystem.Dir as Dir -}
+ src/DocTestFileSystemHandle.hs view
@@ -0,0 +1,15 @@+{- $setup+>>> :m+>>> import qualified Streamly.Data.Array as Array+>>> import qualified Streamly.FileSystem.Handle as Handle hiding (readChunks)+>>> import qualified Streamly.Data.Fold as Fold+>>> import qualified Streamly.Data.Stream as Stream+>>> import qualified Streamly.Data.Unfold as Unfold++For APIs that have not been released yet.++>>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)+>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)+>>> import qualified Streamly.Internal.FileSystem.Handle as Handle+>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)+-}
+ src/DocTestUnicodeParser.hs view
@@ -0,0 +1,9 @@+{- $setup+>>> :m+>>> import qualified Streamly.Data.Stream as Stream+>>> import qualified Streamly.Unicode.Parser as Unicode++For APIs that have not been released yet.++>>> import qualified Streamly.Internal.Unicode.Parser as Unicode (number, mkDouble)+-}
+ src/DocTestUnicodeStream.hs view
@@ -0,0 +1,12 @@+{- $setup+>>> :m++>>> import qualified Streamly.Data.Fold as Fold+>>> import qualified Streamly.Data.Stream as Stream+>>> import qualified Streamly.Unicode.Stream as Unicode++For APIs that have not been released yet.++>>> :set -XMagicHash+>>> import qualified Streamly.Internal.Unicode.Stream as Unicode+-}
+ src/DocTestUnicodeString.hs view
@@ -0,0 +1,5 @@+{- $setup+>>> :m+>>> :set -XQuasiQuotes+>>> import Streamly.Internal.Unicode.String+-}
src/Streamly/Console/Stdio.hs view
@@ -24,33 +24,7 @@     -- * Write (stderr)     , writeErr     , writeErrChunks--    -- * Deprecated-    , read-    , readChunks     ) where -import Control.Monad.IO.Class (MonadIO(..))-import Data.Word (Word8)-import Streamly.Internal.Data.Array.Type (Array)-import Streamly.Internal.Data.Unfold (Unfold)--import Streamly.Internal.Console.Stdio hiding (read, readChunks)-import Prelude hiding (read)---- Same as 'reader'------ @since 0.8.0-{-# DEPRECATED read "Please use 'reader' instead" #-}-{-# INLINE read #-}-read :: MonadIO m => Unfold m () Word8-read = reader---- Same as 'chunkReader'------ @since 0.8.0-{-# DEPRECATED readChunks "Please use 'chunkReader' instead" #-}-{-# INLINE readChunks #-}-readChunks :: MonadIO m => Unfold m () (Array Word8)-readChunks = chunkReader+import Streamly.Internal.Console.Stdio
src/Streamly/Data/Array.hs view
@@ -29,7 +29,7 @@     -- $overview      -- * The Array Type-      A.Array+      Array      -- * Construction     -- | When performance matters, the fastest way to generate an array is@@ -40,48 +40,61 @@     -- 'fromList' can be used to construct an array from a string literal.      -- Pure List APIs-    , A.fromListN-    , A.fromList+    , fromListN+    , fromList      -- Monadic APIs-    , A.writeN      -- drop new-    , A.write       -- full buffer+    , writeN      -- drop new+    , write       -- full buffer     , writeLastN    -- drop old (ring buffer)      -- * Conversion     -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a     -- list.-    , A.toList+    , toList +    -- * Pinning & Unpinning+    -- | Arrays are created unpinned by default unless pinned versions of+    -- creation APIs are used. Look for APIs with @pinned@ prefix in+    -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.+    -- If an array is to be sent to the OS without any further modification+    -- then it should be created pinned in the first place instead of pinning+    -- it later. Pinning an unpinned array has a copy overhead. OS interfacing+    -- APIs create a pinned array directly or convert an unpinned array to+    -- pinned array before sending it to the OS.+    , pin+    , unpin+    , isPinned++    -- * Streams+    , read+    , readRev+     -- * Unfolds-    , A.reader-    , A.readerRev+    , reader+    , readerRev      -- * Casting     , cast     , asBytes      -- * Random Access-    , A.length+    , length     -- , (!!)-    , A.getIndex+    , getIndex -    -- * Unbox Type Class+    -- * Re-exports     , Unbox (..) -    -- * Deprecated-    , read-    , readRev     ) where  #include "inline.hs" -import Streamly.Internal.Data.Unfold (Unfold)-import Streamly.Internal.Data.Array as A hiding (read, readRev)+import Streamly.Internal.Data.Array+import Streamly.Internal.Data.Unbox (Unbox (..)) -import Streamly.Internal.Data.Unboxed (Unbox (..))-import Prelude hiding (read)+import Prelude hiding (read, length)  #include "DocTestDataArray.hs" @@ -97,7 +110,7 @@ -- -- Convert array to stream, and fold the stream: ----- >>> fold f arr = Stream.unfold Array.reader arr & Stream.fold f+-- >>> fold f arr = Array.read arr & Stream.fold f -- >>> fold Fold.sum (Array.fromList [1,2,3::Int]) -- 6 --@@ -105,18 +118,17 @@ -- -- Convert array to stream, transform, and fold back to array: ----- >>> amap f arr = Stream.unfold Array.reader arr & fmap f & Stream.fold Array.write+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write -- >>> amap (+1) (Array.fromList [1,2,3::Int]) -- fromList [2,3,4] -- -- == Pinned and Unpinned Arrays ----- The array type can use both pinned and unpinned memory under the hood.--- Currently the array creation APIs create arrays in pinned memory but it will--- change to unpinned in future releases. The change should not affect users--- functionally unless they are directly accessing the internal memory of the--- array via internal APIs. As of now unpinned arrays can be created using--- unreleased APIs.+-- The array type can use both pinned and unpinned memory under the hood. The+-- default array creation operations create unpinned arrays. IO operations+-- automatically copy an array to pinned memory if the array passed to it is+-- unpinned. Programmers can use appropriate pinned array generation APIs to+-- reduce the copying if it happens. -- -- Unpinned arrays have the advantage of allowing automatic defragmentation of -- the memory by GC. Whereas pinned arrays have the advantage of not requiring@@ -127,20 +139,14 @@ -- -- == Creating Arrays from Non-IO Streams ----- Array creation folds require 'MonadIO' because they need to sequence effects--- in IO streams. To operate on streams in pure Monads like 'Identity' you can--- morph it to IO monad as follows:+-- Array creation folds require 'MonadIO' otherwise the compiler may+-- incorrectly share the array memory thinking it is pure. ----- The 'MonadIO' based folds can be morphed to 'Identity' stream folds:+-- See the 'fromPureStream' unreleased API to generate an array from an+-- Identity stream safely without using MonadIO constraint. ----- >>> purely = Fold.morphInner (Identity . unsafePerformIO)--- >>> Stream.fold (purely Array.write) $ Stream.fromList [1,2,3::Int]--- Identity fromList [1,2,3] ----- Since it is a pure stream we can use 'unsafePerformIO' to extract the result--- of fold from IO.------ Alternatively, 'Identity' streams can be generalized to IO streams:+-- Note that 'Identity' streams can be generalized to IO streams: -- -- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int -- >>> generally = Stream.morphInner (return . runIdentity)@@ -152,17 +158,3 @@ -- This module is designed to be imported qualified: -- -- >>> import qualified Streamly.Data.Array as Array---- | Same as 'reader'----{-# DEPRECATED read "Please use 'reader' instead" #-}-{-# INLINE_NORMAL read #-}-read :: (Monad m, Unbox a) => Unfold m (Array a) a-read = reader---- | Same as 'readerRev'----{-# DEPRECATED readRev "Please use 'readerRev' instead" #-}-{-# INLINE_NORMAL readRev #-}-readRev :: (Monad m, Unbox a) => Unfold m (Array a) a-readRev = readerRev
src/Streamly/Data/Array/Generic.hs view
@@ -22,15 +22,20 @@     , A.writeN     , A.write +    -- * Conversion+    , A.toList+     -- * Streams     , A.read     , A.readRev      -- * Unfolds     , A.reader+    -- , A.readerRev      -- * Random Access     , A.length+    , A.getIndex      -- -- * Folding Arrays     -- , A.streamFold
src/Streamly/Data/Fold.hs view
@@ -10,6 +10,156 @@ -- Fast, composable stream consumers with ability to terminate, supporting -- stream fusion. --+-- == Using Folds+--+-- This module provides elementary folds and fold combinators that can be used+-- to consume a stream of data and reduce it to a final value, or transform it+-- in a stateful manner using scans. A data stream can be reduced into a stream+-- of folded data elements by folding segments of the stream. Fold combinators+-- can be used to compose multiple folds in parallel or to create a pipeline of+-- folds such that the next fold consumes the result of the previous fold. To+-- run these folds on a stream see 'Streamly.Data.Stream.fold',+-- 'Streamly.Data.Stream.scan', 'Streamly.Data.Stream.postscan',+-- 'Streamly.Data.Stream.scanMaybe', 'Streamly.Data.Stream.foldMany' and other+-- operations accepting 'Fold' type as argument "Streamly.Data.Stream".+--+-- == Reducing a Stream+--+-- A 'Fold' is a consumer of a stream of values. A fold driver (such as+-- 'Streamly.Data.Stream.fold') initializes the fold @accumulator@, runs the+-- fold @step@ function in a loop, processing the input stream one element at a+-- time and accumulating the result. The loop continues until the fold+-- terminates, at which point the accumulated result is returned.+--+-- For example, a 'sum' Fold represents a stream consumer that adds the values+-- in the input stream:+--+-- >>> Stream.fold Fold.sum $ Stream.fromList [1..100]+-- 5050+--+-- Conceptually, a 'Fold' is a data type that mimics a strict left fold+-- ('Data.List.foldl').  The above example is similar to a left fold using+-- @(+)@ as the step and @0@ as the initial value of the accumulator:+--+-- >>> Data.List.foldl' (+) 0 [1..100]+-- 5050+--+-- 'Fold's have an early termination capability e.g. the 'one' fold terminates+-- after consuming one element:+--+-- >>> Stream.fold Fold.one $ Stream.fromList [1..]+-- Just 1+--+-- The above example is similar to the following right fold:+--+-- >>> Prelude.foldr (\x _ -> Just x) Nothing [1..]+-- Just 1+--+-- 'Fold's can be combined together using combinators. For example, to create a+-- fold that sums first two elements in a stream:+--+-- >>> sumTwo = Fold.take 2 Fold.sum+-- >>> Stream.fold sumTwo $ Stream.fromList [1..100]+-- 3+--+-- == Parallel Composition+--+-- Folds can be combined to run in parallel on the same input. For example, to+-- compute the average of numbers in a stream without going through the stream+-- twice:+--+-- >>> avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)+-- >>> Stream.fold avg $ Stream.fromList [1.0..100.0]+-- 50.5+--+-- Folds can be combined so as to partition the input stream over multiple+-- folds. For example, to count even and odd numbers in a stream:+--+-- >>> split n = if even n then Left n else Right n+-- >>> stream = fmap split $ Stream.fromList [1..100]+-- >>> countEven = fmap (("Even " ++) . show) Fold.length+-- >>> countOdd = fmap (("Odd "  ++) . show) Fold.length+-- >>> f = Fold.partition countEven countOdd+-- >>> Stream.fold f stream+-- ("Even 50","Odd 50")+--+-- == Sequential Composition+--+-- Terminating folds can be combined to parse the stream serially such that the+-- first fold consumes the input until it terminates and the second fold+-- consumes the rest of the input until it terminates:+--+-- >>> f = Fold.splitWith (,) (Fold.take 8 Fold.toList) (Fold.takeEndBy (== '\n') Fold.toList)+-- >>> Stream.fold f $ Stream.fromList "header: hello\n"+-- ("header: ","hello\n")+--+-- == Splitting a Stream+--+-- A 'Fold' can be applied repeatedly on a stream to transform it to a stream+-- of fold results. To split a stream on newlines:+--+-- >>> f = Fold.takeEndBy (== '\n') Fold.toList+-- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.fromList "Hello there!\nHow are you\n"+-- ["Hello there!\n","How are you\n"]+--+-- Similarly, we can split the input of a fold too:+--+-- >>> Stream.fold (Fold.many f Fold.toList) $ Stream.fromList "Hello there!\nHow are you\n"+-- ["Hello there!\n","How are you\n"]+--+-- == Folds vs. Streams+--+-- We can often use streams or folds to achieve the same goal. However, streams+-- are more efficient in composition of producers (e.g.+-- 'Data.Stream.append' or 'Data.Stream.mergeBy') whereas folds are+-- more efficient in composition of consumers (e.g.  'splitWith', 'partition'+-- or 'teeWith').+--+-- Streams are producers, transformations on streams happen on the output side:+--+-- >>> :{+--  f stream =+--        Stream.filter odd stream+--      & fmap (+1)+--      & Stream.fold Fold.sum+-- :}+--+-- >>> f $ Stream.fromList [1..100 :: Int]+-- 2550+--+-- Folds are stream consumers with an input stream and an output value, stream+-- transformations on folds happen on the input side:+--+-- >>> :{+-- f =+--        Fold.filter odd+--      $ Fold.lmap (+1)+--      $ Fold.sum+-- :}+--+-- >>> Stream.fold f $ Stream.fromList [1..100 :: Int]+-- 2550+--+-- Notice the similiarity in the definition of @f@ in both cases, the only+-- difference is the composition by @&@ vs @$@ and the use @lmap@ vs @map@, the+-- difference is due to output vs input side transformations.+--+-- == Fusion Limitations+--+-- Folds support stream fusion for generating loops comparable to the speed of+-- C. However, it has some limitations. For fusion to work, the folds must be+-- inlined, folds must be statically known and not generated dynamically, folds+-- should not be passed recursively.+--+-- Another limitation is due to the quadratic complexity causing slowdown when+-- too many nested compositions are used. Especially, the performance of the+-- Applicative instance and splitting operations (e.g. 'splitWith') degrades+-- quadratically (O(n^2)) when combined @n@ times, roughly 8 or less sequenced+-- operations are fine. For these cases folds can be converted to parsers and+-- then used as ParserK.+--+-- == Experimental APIs+-- -- Please refer to "Streamly.Internal.Data.Fold" for more functions that have -- not yet been released. @@ -21,9 +171,6 @@     --     -- $setup -    -- * Overview-    -- $overview-     -- * Running A Fold       drive     -- XXX Should we have a stream returning function in fold module?@@ -72,10 +219,6 @@     , toListRev     , toSet     , toIntSet-    , toMap-    , toMapIO-    , demuxToMap-    , demuxToMapIO     , topBy      -- ** Non-Empty Accumulators@@ -97,10 +240,6 @@     , uniqBy     , nub     , nubInt-    , classify-    , classifyIO-    , demux-    , demuxIO      -- ** Terminating Folds     , one@@ -193,8 +332,11 @@     , takeEndBy     , takeEndBy_ -    -- ** Serial Append+    -- ** Splitting     , splitWith+    , many+    , groupsOf+    -- , intervalsOf      -- ** Parallel Distribution     -- | For applicative composition using distribution see@@ -216,14 +358,21 @@     --, partitionByMinM     --, partitionBy +    -- ** Key-value Collectors+    , toMap+    , toMapIO+    , demuxToMap+    , demuxToMapIO++    -- ** Key-value Scanners+    , classify+    , classifyIO+    , demux+    , demuxIO+     -- ** Unzipping     , unzip -    -- ** Splitting-    , many-    , groupsOf-    -- , intervalsOf-     -- ** Nesting     , concatMap @@ -245,132 +394,16 @@ where  import Prelude-       hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,-               foldl, map, mapM_, sequence, all, any, sum, product, elem,-               notElem, maximum, minimum, head, last, tail, length, null,-               reverse, iterate, init, and, or, lookup, foldr1, (!!),-               scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip,+       hiding (Foldable(..), filter, drop, dropWhile, take, takeWhile, zipWith,+               map, mapM_, sequence, all, any,+               notElem, head, last, tail,+               reverse, iterate, init, and, or, lookup, (!!),+               scanl, scanl1, replicate, concatMap, mconcat, unzip,                span, splitAt, break, mapM, maybe)  import Streamly.Internal.Data.Fold-import Streamly.Internal.Data.Fold.Container  #include "DocTestDataFold.hs"---- $overview------ A 'Fold' is a consumer of a stream of values. A fold driver (such as--- 'Streamly.Data.Stream.fold') initializes the fold @accumulator@, runs the--- fold @step@ function in a loop, processing the input stream one element at a--- time and accumulating the result. The loop continues until the fold--- terminates, at which point the accumulated result is returned.------ For example, a 'sum' Fold represents a stream consumer that adds the values--- in the input stream:------ >>> Stream.fold Fold.sum $ Stream.fromList [1..100]--- 5050------ Conceptually, a 'Fold' is a data type that mimics a strict left fold--- ('Data.List.foldl').  The above example is similar to a left fold using--- @(+)@ as the step and @0@ as the initial value of the accumulator:------ >>> Data.List.foldl' (+) 0 [1..100]--- 5050------ 'Fold's have an early termination capability e.g. the 'one' fold terminates--- after consuming one element:------ >>> Stream.fold Fold.one $ Stream.fromList [1..]--- Just 1------ The above example is similar to the following right fold:------ >>> Prelude.foldr (\x _ -> Just x) Nothing [1..]--- Just 1------ 'Fold's can be combined together using combinators. For example, to create a--- fold that sums first two elements in a stream:------ >>> sumTwo = Fold.take 2 Fold.sum--- >>> Stream.fold sumTwo $ Stream.fromList [1..100]--- 3------ Folds can be combined to run in parallel on the same input. For example, to--- compute the average of numbers in a stream without going through the stream--- twice:------ >>> avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)--- >>> Stream.fold avg $ Stream.fromList [1.0..100.0]--- 50.5------ Folds can be combined so as to partition the input stream over multiple--- folds. For example, to count even and odd numbers in a stream:------ >>> split n = if even n then Left n else Right n--- >>> stream = fmap split $ Stream.fromList [1..100]--- >>> countEven = fmap (("Even " ++) . show) Fold.length--- >>> countOdd = fmap (("Odd "  ++) . show) Fold.length--- >>> f = Fold.partition countEven countOdd--- >>> Stream.fold f stream--- ("Even 50","Odd 50")------ Terminating folds can be combined to parse the stream serially such that the--- first fold consumes the input until it terminates and the second fold--- consumes the rest of the input until it terminates:------ >>> f = Fold.splitWith (,) (Fold.take 8 Fold.toList) (Fold.takeEndBy (== '\n') Fold.toList)--- >>> Stream.fold f $ Stream.fromList "header: hello\n"--- ("header: ","hello\n")------ A 'Fold' can be applied repeatedly on a stream to transform it to a stream--- of fold results. To split a stream on newlines:------ >>> f = Fold.takeEndBy (== '\n') Fold.toList--- >>> Stream.fold Fold.toList $ Stream.foldMany f $ Stream.fromList "Hello there!\nHow are you\n"--- ["Hello there!\n","How are you\n"]------ Similarly, we can split the input of a fold too:------ >>> Stream.fold (Fold.many f Fold.toList) $ Stream.fromList "Hello there!\nHow are you\n"--- ["Hello there!\n","How are you\n"]------ = Folds vs. Streams------ We can often use streams or folds to achieve the same goal. However, streams--- are more efficient in composition of producers (e.g.--- 'Data.Stream.append' or 'Data.Stream.mergeBy') whereas folds are--- more efficient in composition of consumers (e.g.  'splitWith', 'partition'--- or 'teeWith').------ Streams are producers, transformations on streams happen on the output side:------ >>> :{---  f stream =---        Stream.filter odd stream---      & fmap (+1)---      & Stream.fold Fold.sum--- :}------ >>> f $ Stream.fromList [1..100 :: Int]--- 2550------ Folds are stream consumers with an input stream and an output value, stream--- transformations on folds happen on the input side:------ >>> :{--- f =---        Fold.filter odd---      $ Fold.lmap (+1)---      $ Fold.sum--- :}------ >>> Stream.fold f $ Stream.fromList [1..100 :: Int]--- 2550------ Notice the similiarity in the definition of @f@ in both cases, the only--- difference is the composition by @&@ vs @$@ and the use @lmap@ vs @map@, the--- difference is due to output vs input side transformations.  -------------------------------------------------------------------------------- -- Deprecated
src/Streamly/Data/MutArray.hs view
@@ -12,7 +12,7 @@ -- contents of a mutable array can be modified in-place. For general -- documentation, please refer to the original module. ----- Please refer to "Streamly.Internal.Data.Array.Mut" for functions that have+-- Please refer to "Streamly.Internal.Data.MutArray" for functions that have -- not yet been released. -- -- For mutable arrays that work on boxed types, not requiring the 'Unbox'@@ -33,7 +33,7 @@      -- Uninitialized Arrays     , new-    , newPinned+    , pinnedNew      -- From containers     , fromListN@@ -42,22 +42,36 @@     , write       -- full buffer     -- writeLastN +    -- * Pinning & Unpinning+    , pin+    , unpin+    , isPinned+     -- * Appending elements     , snoc      -- * Appending streams-    , writeAppendN-    , writeAppend+    , writeAppendN -- XXX Rename to writeSnocN?+    , writeAppend -- XXX Rename to writeSnoc?      -- * Inplace mutation     , putIndex+    , putIndexUnsafe+    , modifyIndex+    , modifyIndexUnsafe+    , modify      -- * Random access     , getIndex+    , getIndexUnsafe      -- * Conversion     , toList +    -- * Streams+    , read+    , readRev+     -- * Unfolds     , reader     , readerRev@@ -69,13 +83,22 @@     -- * Size     , length -    -- * Unbox Type Class+    -- * Re-exports     , Unbox (..)++    -- * Deprecated+    , newPinned     ) where  import Prelude hiding (length, read)-import Streamly.Internal.Data.Array.Mut-import Streamly.Internal.Data.Unboxed (Unbox (..))+import Streamly.Internal.Data.MutArray+import Streamly.Internal.Data.Unbox (Unbox (..))+import Control.Monad.IO.Class (MonadIO)  #include "DocTestDataMutArray.hs"++{-# DEPRECATED newPinned "Please use pinnedNew instead." #-}+{-# INLINE newPinned #-}+newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+newPinned = pinnedNew
src/Streamly/Data/MutArray/Generic.hs view
@@ -23,27 +23,43 @@       MutArray      -- * Construction+    , new+    , fromListN+    , fromList     , writeN+    , write      -- * Appending elements-    , new     , snoc +    -- * Inplace mutation+    , putIndex+    , putIndexUnsafe+    , modifyIndex+    , modifyIndexUnsafe+    -- , modify++    -- * Random reads+    , getIndex+    , getIndexUnsafe+     -- * Conversion     , toList +    -- * Streams+    , read+    , readRev+     -- * Unfolds     , reader--    -- * Random reads-    , getIndex+    -- , readerRev -    -- * Inplace mutation-    , putIndex-    , modifyIndex+    -- * Size+    , length     ) where -import Streamly.Internal.Data.Array.Generic.Mut.Type+import Streamly.Internal.Data.MutArray.Generic+import  Prelude hiding (read, length)  #include "DocTestDataMutArrayGeneric.hs"
+ src/Streamly/Data/MutByteArray.hs view
@@ -0,0 +1,97 @@+-- |+-- Module      : Streamly.Data.MutByteArray+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--+-- A low level byte Array type 'MutByteArray', along with type classes 'Unbox'+-- and 'Serialize' for fast binary serialization and deserialization of Haskell+-- values. Serialization, deserialization performance is similar to, and in+-- some cases many times better than the store package. Conceptually, the+-- 'Serialize' type class works in the same way as store.+--+-- Serialize instances are configurable to use constructor names (see+-- 'Streamly.Internal.Data.MutByteArray.encodeConstrNames'), record field names (see+-- 'Streamly.Internal.Data.MutByteArray.encodeRecordFields') instead of binary+-- encoded values. This is an experimental feature which allows JSON like+-- properties with faster speed. For example, you can change the order of+-- constructors or record fields without affecting serialized value.+--+-- Higher level unboxed array modules "Streamly.Data.Array" and+-- "Streamly.Data.MutArray" are built on top of this module. Unboxed arrays are+-- essentially serialized Haskell values. Array modules provide higher level+-- serialization routines like 'Streamly.Data.Array.pinnedSerialize'+-- and 'Streamly.Data.Array.deserialize' from the+-- "Streamly.Data.Array" module.+--+-- == Mutable Byte Array+--+-- 'MutByteArray' is a primitive mutable array in the IO monad. 'Unbox' and+-- 'Serialize' type classes use this primitive array to serialize data to and+-- deserialize it from. This array is used to build higher level unboxed+-- array types 'Streamly.Data.MutArray.MutArray' and 'Streamly.Data.Array.Array'.+--+-- == Using Unbox+--+-- The 'Unbox' type class is simple and used to serialize non-recursive fixed+-- size data types. This type class is primarily used to implement unboxed+-- arrays. Unboxed arrays are just a sequence of serialized fixed length+-- Haskell data types. Instances of this type class can be derived using+-- 'Generic' or template haskell based deriving functions provided in this+-- module.+--+-- Writing a data type to an array using the array creation routines in+-- "Streamly.Data.Array" or "Streamly.Data.MutArray" (e.g. @writeN@ or+-- @fromListN@), serializes the type to the array. Similarly, reading the data+-- type from the array deserializes it. You can also serialize and deserialize+-- directly to and from a 'MutByteArray', using the type class methods.+--+-- == Using Serialize+--+-- The 'Serialize' type class is a superset of the 'Unbox' type class, it can+-- serialize variable length data types as well e.g. Haskell lists. Use+-- 'deriveSerialize' to derive the instances of the type class automatically+-- and then use the type class methods to serialize and deserialize to and from+-- a 'MutByteArray'.+--+-- See 'Streamly.Data.Array.pinnedSerialize' and+-- 'Streamly.Data.Array.deserialize' for 'Array' type based+-- serialization.+--+module Streamly.Data.MutByteArray+    (++    -- * Mutable Byte Array+    -- | The standard way to read from or write to a 'MutByteArray' is by using+    -- the 'Unbox' or 'Serialize' type class methods.+      MutByteArray+    , isPinned+    , pin+    , unpin+    , new+    , pinnedNew++    -- * Unbox+    , Unbox(..)+    , deriveUnbox++    -- * Serialize+    , Serialize(..)++    -- ** Instance Config+    , SerializeConfig+    , inlineAddSizeTo+    , inlineSerializeAt+    , inlineDeserializeAt++    -- ** Instance Deriving+    , deriveSerialize+    , deriveSerializeWith+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Streamly.Internal.Data.MutByteArray
src/Streamly/Data/Parser.hs view
@@ -7,13 +7,138 @@ -- Stability   : pre-release -- Portability : GHC ----- Fast, composable stream consumers with ability to terminate, backtrack and--- fail, supporting stream fusion. Parsers are a natural extension of--- "Streamly.Data.Fold". Parsers and folds can be interconverted.+-- Parsers are stream consumers like folds with the following differences: ----- Please refer to "Streamly.Internal.Data.Parser" for functions that have--- not yet been released.+-- * folds cannot fail but parsers can fail and backtrack.+-- * folds can be composed as a Tee but parsers cannot.+-- * folds can be used for scanning but parsers cannot.+-- * folds can be converted to parsers. --+-- This module implements parsers with stream fusion which compile to efficient+-- loops comparable to the speed of C.+--+-- == Using Parsers+--+-- This module provides elementary parsers and parser combinators that can be+-- used to parse a stream of data. Additionally, all the folds from the+-- "Streamly.Data.Fold" module can be converted to parsers using 'fromFold'.+-- All the parsing functionality provided by popular parsing libraries, and+-- more is available. Also see "Streamly.Unicode.Parser" module for Char stream+-- parsers.+--+-- A data stream can be transformed to a stream of parsed data elements. Parser+-- combinators can be used to create a pipeline of folds or parsers such that+-- the next fold or parser consumes the result of the previous parser. See+-- 'Streamly.Data.Stream.parse' and 'Streamly.Data.Stream.parseMany' to run+-- these parsers on a stream.+--+-- == Parser vs ParserK+--+-- There are two functionally equivalent parsing modules,+-- "Streamly.Data.Parser" (this module) and "Streamly.Data.ParserK". The latter+-- is a CPS based wrapper over the former, and can be used for parsing in+-- general. "Streamly.Data.Parser" enables stream fusion and should be+-- preferred over "Streamly.Data.ParserK" for high performance stream parsing+-- use cases. However, there are a few cases where this module is not+-- suitable and ParserK should be used instead.+--+-- For static fusion, parser combinators have to use strict pattern matching on+-- arguments of type Parser. This leads to infinte loop when a parser is+-- defined recursively, due to strict evaluation of the recursive call. For+-- example, the following implementation loops infinitely because of the+-- recursive use of parser @p@ in the @*>@ combinator:+--+-- >>> import Streamly.Data.Parser (Parser)+-- >>> import qualified Streamly.Data.Fold as Fold+-- >>> import qualified Streamly.Data.Parser as Parser+-- >>> import qualified Streamly.Data.Stream as Stream+-- >>> import Control.Applicative ((<|>))+--+-- >>> :{+-- >>> p :: Monad m => Parser Char m String+-- >>> p = Parser.satisfy (== '(') *> p <|> Parser.fromFold Fold.toList+-- >>> :}+--+-- Use ParserK when recursive use is required:+--+-- >>> import Streamly.Data.ParserK (ParserK)+-- >>> import qualified Streamly.Data.StreamK as StreamK+-- >>> import qualified Streamly.Internal.Data.StreamK as StreamK (parse)+-- >>> import qualified Streamly.Internal.Data.ParserK as ParserK (adapt)+--+-- >>> :{+-- >>> p :: Monad m => ParserK Char m String+-- >>> p = ParserK.adapt (Parser.satisfy (== '(')) *> p <|> ParserK.adapt (Parser.fromFold Fold.toList)+-- >>> :}+--+-- >>> StreamK.parse p $ StreamK.fromStream $ Stream.fromList "hello"+-- Right "hello"+--+-- For this reason Applicative, Alternative or Monad compositions with+-- recursion cannot be used with the 'Parser' type. Alternative type class based+-- operations like 'asum' and Alternative based generic parser combinators use+-- recursion. Similarly, Applicative type class based operations like+-- 'Prelude.sequence' use recursion. Custom implementations of many such+-- operations are provided in this module (e.g. 'some', 'many'), and those+-- should be used instead.+--+-- Another limitation of Parser type is due to the quadratic complexity causing+-- slowdown when too many nested compositions are used. Especially Applicative,+-- Monad, Alternative instances, and sequenced parsing operations (e.g. nested+-- 'one', and 'splitWith') degrade the performance quadratically (O(n^2)) when+-- combined @n@ times, roughly 8 or less sequenced parsers are fine. READ THE+-- DOCS OF APPLICATIVE, MONAD AND ALTERNATIVE INSTANCES.+--+-- == Streaming Parsers+--+-- With 'Streamly.Data.ParserK.ParserK' you can use the generic Alternative+-- type class based parsers from the+-- <https://hackage.haskell.org/package/parser-combinators parser-combinators>+-- library or similar. However, we recommend that you use the equivalent+-- functionality from this module for better performance and for streaming+-- behavior.+--+-- Firstly, the combinators in this module are faster due to stream fusion.+-- Secondly, these are streaming in nature as the results can be passed+-- directly to other stream consumers (folds or parsers). The Alternative type+-- class based parsers would end up buffering all the results in lists before+-- they can be consumed.+--+-- When recursion or heavy nesting is needed use ParserK.+--+-- == Error Reporting+--+-- These parsers do not report the error context (e.g. line number or column).+-- This may be supported in future.+--+-- == Monad Transformer Stack+--+-- 'MonadTrans' instance is not provided. If the 'Parser' type is the top most+-- layer (which should be the case almost always) you can just use 'fromEffect'+-- to execute the lower layer monad effects.+--+-- == Parser vs ParserK Implementation+--+-- The 'Parser' type represents a stream consumer by composing state as data+-- which enables stream fusion. Stream fusion generates a tight loop without+-- any constructor allocations between the stages, providing C like performance+-- for the loop. Stream fusion works when multiple functions are combined in a+-- pipeline statically. Therefore, the operations in this module must be+-- inlined and must not be used recursively to allow for stream fusion.+--+-- The 'ParserK' type represents a stream consumer by composing function calls,+-- therefore, a function call overhead is incurred at each composition. It is+-- quite fast in general but may be a few times slower than a fused parser.+-- However, it allows for scalable dynamic composition especially parsers can+-- be used in recursive calls. Using the 'ParserK' type operations like+-- 'splitWith' provide linear (O(n)) performance with respect to the number of+-- compositions.+--+-- == Experimental APIs+--+-- Please refer to "Streamly.Internal.Data.Parser" for functions that have not+-- yet been released.+-- module Streamly.Data.Parser     (     -- * Setup@@ -22,9 +147,6 @@     --     -- $setup -    -- * Overview-    -- $overview-     -- * Parser Type       Parser @@ -96,8 +218,8 @@      -- ** Grouping     , groupBy-    -- , groupByRolling-    -- , groupByRollingEither+    , groupByRolling+    , groupByRollingEither      -- ** Framing     -- , wordFramedBy@@ -123,55 +245,3 @@ import Prelude hiding (dropWhile, takeWhile, filter)  #include "DocTestDataParser.hs"---- $overview------ Several combinators in this module can be many times faster than CPS based--- parsers because of stream fusion. For example,--- 'Streamly.Internal.Data.Parser.many' combinator in this module is much--- faster than the 'Control.Applicative.many' combinator of--- 'Control.Applicative.Alternative' type class used by CPS based parsers.------ The use of 'Alternative' type class, in parsers has another drawback.--- Alternative based parsers use plain Haskell lists to collect the results. In--- a strict Monad like IO, the results are necessarily buffered before they can--- be consumed.  This may not perform optimally in streaming applications--- processing large amounts of data.  Equivalent combinators in this module can--- consume the results of parsing using a 'Fold' or another parser, thus--- providing a scalable and composable consumer.------ Note that these parsers do not report the error context (e.g. line number or--- column). This may be supported in future.------ mtl instances are not provided. If the 'Parser' type is the top most layer--- (which should be the case almost always) you can just use 'fromEffect' to--- execute the lower layer monad effects.------ == Performance Notes------ The 'Parser' type represents a stream consumer by composing state as data--- which enables stream fusion. Stream fusion generates a tight loop without--- any constructor allocations between the stages, providing C like performance--- for the loop. Stream fusion works when multiple functions are combined in a--- pipeline statically. Therefore, the operations in this module must be--- inlined and must not be used recursively to allow for stream fusion. Note--- that operations like 'sequence', and 'asum' that compose pasrers using--- recursion should be avoided with these parsers. You can use these with the--- 'ParserK' module instead.------ Using the 'Parser' type, parsing operations like 'one', 'splitWith' etc.--- degrade quadratically (O(n^2)) when combined many times. If you need to--- combine these operations, say more than 8 times in a single loop, then you--- should consider using the continuation style parser type 'ParserK' instead.--- Also, if you need to use these operations in a recursive loop you should use--- 'ParserK' instead.------ The 'ParserK' type represents a stream consumer by composing function calls,--- therefore, a function call overhead is incurred at each composition. It is--- quite fast in general but may be a few times slower than a fused parser.--- However, it allows for scalable dynamic composition especially parsers can--- be used in recursive calls. Using the 'ParserK' type operations like--- 'splitWith' provide linear (O(n)) performance with respect to the number of--- compositions..------ 'Parser' and 'ParserK' types can be interconverted.
src/Streamly/Data/ParserK.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Data.ParserK -- Copyright   : (c) 2023 Composewell Technologies@@ -6,39 +7,133 @@ -- Stability   : pre-release -- Portability : GHC ----- Parsers using Continuation Passing Style (CPS). See notes in--- "Streamly.Data.Parser" module to know when to use this module.+-- See the general notes about parsing in the "Streamly.Data.Parser" module.+-- This module implements a using Continuation Passing Style (CPS) wrapper over+-- the "Streamly.Data.Parser" module. It is as fast or faster than attoparsec. ----- To run a 'ParserK' use 'Streamly.Data.StreamK.parseChunks'.+-- == Parser vs ParserK --+-- 'ParserK' is preferred over 'Streamly.Data.Parser.Parser' when extensive+-- applicative, alternative and monadic composition is required, or when+-- recursive or dynamic composition of parsers is required. The+-- 'Streamly.Data.Parser.Parser' type fuses statically and creates efficient+-- loops whereas 'ParserK' uses function call based composition and has+-- comparatively larger runtime overhead but it is better suited to the+-- specific use cases mentioned above. 'ParserK' also allows to efficient parse+-- a stream of arrays, it can also break the input stream into a parse result+-- and remaining stream so that the stream can be parsed independently in+-- segments.+--+-- == Using ParserK+--+-- All the parsers from the "Streamly.Data.Parser" module can be adapted to+-- ParserK using the 'Streamly.Data.ParserK.adaptC',+-- 'Streamly.Internal.Data.ParserK.adapt', and+-- 'Streamly.Internal.Data.ParserK.adaptCG' combinators.+--+-- 'Streamly.Data.StreamK.parseChunks' runs a parser on a stream of unboxed+-- arrays, this is the preferred and most efficient way to parse chunked input.+-- The more general 'Streamly.Data.StreamK.parseBreakChunks' function returns+-- the remaining stream as well along with the parse result. There are+-- 'Streamly.Internal.Data.StreamK.parseChunksGeneric',+-- 'Streamly.Internal.Data.StreamK.parseBreakChunksGeneric' as well to run+-- parsers on boxed arrays. 'Streamly.Internal.Data.StreamK.parse',+-- 'Streamly.Internal.Data.StreamK.parseBreak' run parsers on a stream of+-- individual elements instead of stream of arrays.+--+-- == Monadic Composition+--+-- Monad composition can be used for lookbehind parsers, we can dynamically+-- compose new parsers based on the results of the previously parsed values.+--+-- If we have to parse "a9" or "9a" but not "99" or "aa" we can use the+-- following non-monadic, backtracking parser:+--+-- >>> digits p1 p2 = ((:) <$> p1 <*> ((:) <$> p2 <*> pure []))+-- >>> :{+-- backtracking :: Monad m => ParserK Char m String+-- backtracking = ParserK.adapt $+--     digits (Parser.satisfy isDigit) (Parser.satisfy isAlpha)+--     <|>+--     digits (Parser.satisfy isAlpha) (Parser.satisfy isDigit)+-- :}+--+-- We know that if the first parse resulted in a digit at the first place then+-- the second parse is going to fail.  However, we waste that information and+-- parse the first character again in the second parse only to know that it is+-- not an alphabetic char.  By using lookbehind in a 'Monad' composition we can+-- avoid redundant work:+--+-- >>> data DigitOrAlpha = Digit Char | Alpha Char+--+-- >>> :{+-- lookbehind :: Monad m => ParserK Char m String+-- lookbehind = do+--     x1 <- ParserK.adapt $+--              Digit <$> Parser.satisfy isDigit+--          <|> Alpha <$> Parser.satisfy isAlpha+--     -- Note: the parse depends on what we parsed already+--     x2 <- ParserK.adapt $+--           case x1 of+--              Digit _ -> Parser.satisfy isAlpha+--              Alpha _ -> Parser.satisfy isDigit+--     return $ case x1 of+--         Digit x -> [x,x2]+--         Alpha x -> [x,x2]+-- :}+--+-- == Experimental APIs+--+-- Please refer to "Streamly.Internal.Data.ParserK" for functions that have+-- not yet been released.+-- module Streamly.Data.ParserK     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Parser Type       ParserK      -- * Parsers     -- ** Conversions-    , fromFold-    , fromParser+    , adapt+    , adaptC+    , adaptCG     -- , toParser      -- ** Without Input     , fromPure     , fromEffect     , die++    -- * Deprecated+    , fromFold+    , fromParser     )  where  import Control.Monad.IO.Class (MonadIO) import Streamly.Internal.Data.Fold (Fold)-import Streamly.Internal.Data.Unboxed (Unbox)-import qualified Streamly.Internal.Data.Parser.ParserD as ParserD+import Streamly.Internal.Data.Unbox (Unbox)+import Streamly.Internal.Data.Array (Array)+import qualified Streamly.Internal.Data.Parser as ParserD -import Streamly.Internal.Data.Parser.ParserK.Type+import Streamly.Internal.Data.ParserK.Type --- | Convert a 'Fold' to a 'ParserK'.---+#include "DocTestDataParserK.hs"++{-# DEPRECATED fromFold "Please use \"ParserK.adaptC . Parser.fromFold\" instead." #-} {-# INLINE fromFold #-}-fromFold :: (MonadIO m, Unbox a) => Fold m a b -> ParserK a m b-fromFold = fromParser . ParserD.fromFold+fromFold :: (MonadIO m, Unbox a) => Fold m a b -> ParserK (Array a) m b+fromFold = adaptC . ParserD.fromFold++{-# DEPRECATED fromParser "Please use \"adaptC\" instead." #-}+{-# INLINE fromParser #-}+fromParser ::+       (MonadIO m, Unbox a) => ParserD.Parser a m b -> ParserK (Array a) m b+fromParser = adaptC
src/Streamly/Data/Stream.hs view
@@ -8,15 +8,20 @@ -- Stability   : released -- Portability : GHC ----- Fast, composable stream producers with ability to terminate, supporting--- stream fusion.+-- Streams represented as state machines, that fuse together when composed+-- statically, eliminating function calls or intermediate constructor+-- allocations - generating tight, efficient loops. Suitable for high+-- performance looping operations. --+-- If you need to call these operations recursively in a loop (i.e. composed+-- dynamically) then it is recommended to use the continuation passing style+-- (CPS) stream operations from the "Streamly.Data.StreamK" module. 'Stream'+-- and 'StreamK' types are interconvertible.  See more details in the+-- documentation below regarding 'Stream' vs 'StreamK'.+-- -- Please refer to "Streamly.Internal.Data.Stream" for more functions that have -- not yet been released. ----- For continuation passing style (CPS) stream type, please refer to--- the "Streamly.Data.StreamK" module.--- -- Checkout the <https://github.com/composewell/streamly-examples> -- repository for many more real world examples of stream programming. @@ -37,15 +42,21 @@     -- * Construction     -- | Functions ending in the general shape @b -> Stream m a@.     ---    -- See also: "Streamly.Internal.Data.Stream.Generate" for-    -- @Pre-release@ functions.+    -- Useful Idioms:+    --+    -- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0+    -- >>> fromIndicesM f = Stream.mapM f $ Stream.enumerateFrom 0+    -- >>> fromListM = Stream.sequence . Stream.fromList+    -- >>> fromFoldable = StreamK.toStream . StreamK.fromFoldable+    -- >>> fromFoldableM = Stream.sequence . fromFoldable      -- ** Primitives-    -- | Primitives to construct a stream from pure values or monadic actions.-    -- All other stream construction and generation combinators described later-    -- can be expressed in terms of these primitives. However, the special-    -- versions provided in this module can be much more efficient in most-    -- cases. Users can create custom combinators using these primitives.+    -- | A fused 'Stream' is never constructed using these primitives, they are+    -- typically generated by converting containers like list into streams, or+    -- generated using custom functions provided in this module. The 'cons'+    -- primitive in this module has a rare use in fusing a small number of+    -- elements. On the other hand, it is common to construct 'StreamK' stream+    -- using the StreamK.'StreamK.cons' primitive.     , nil     , nilM     , cons@@ -103,8 +114,6 @@     -- | Functions ending in the general shape @Stream m a -> m b@ or @Stream m     -- a -> m (b, Stream m a)@     ---    -- See also: "Streamly.Internal.Data.Stream.Eliminate" for @Pre-release@-    -- functions.  -- EXPLANATION: In imperative terms a fold can be considered as a loop over the stream -- that reduces the stream to a single value.@@ -208,7 +217,16 @@     -- | Usually you can use the folds in "Streamly.Data.Fold". However, some     -- folds that may be commonly used or may have an edge in performance in     -- some cases are provided here.-    -- , drain+    --+    -- Useful idioms:+    --+    -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)+    -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)+    -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)+    -- >>> drain = Stream.fold Fold.drain+    -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)+    -- >>> length = Stream.fold Fold.length+    -- >>> head = Stream.fold Fold.one     , toList      -- * Mapping@@ -236,8 +254,6 @@     -- * Scanning     -- | Stateful one-to-one transformations.     ---    -- See also: "Streamly.Internal.Data.Stream.Transform" for-    -- @Pre-release@ functions.      {-     -- ** Left scans@@ -253,6 +269,14 @@     -}      -- ** Scanning By 'Fold'+    -- | Useful idioms:+    --+    -- >>> scanl' f z = Stream.scan (Fold.foldl' f z)+    -- >>> scanlM' f z = Stream.scan (Fold.foldlM' f z)+    -- >>> postscanl' f z = Stream.postscan (Fold.foldl' f z)+    -- >>> postscanlM' f z = Stream.postscan (Fold.foldlM' f z)+    -- >>> scanl1' f = Stream.catMaybes . Stream.scan (Fold.foldl1' f)+    -- >>> scanl1M' f = Stream.catMaybes . Stream.scan (Fold.foldlM1' f)     , scan     , postscan     -- XXX postscan1 can be implemented using Monoids or Refolds.@@ -298,6 +322,13 @@     -- filtering folds (folds returning a 'Maybe' type) in     -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to     -- perform stateful filtering operations in general.+    --+    -- Useful idioms:+    --+    -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)+    -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)+    -- >>> elemIndices a = findIndices (== a)+    -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))     , scanMaybe     , take     , takeWhile@@ -326,32 +357,31 @@     -- , elemIndices      -- * Combining Two Streams+    -- | Note that these operations are suitable for statically fusing a few+    -- streams, they have a quadratic O(n^2) time complexity wrt to the number+    -- of streams. If you want to compose many streams dynamically using binary+    -- combining operations see the corresponding operations in+    -- "Streamly.Data.StreamK".+    --+    -- When fusing more than two streams it is more efficient if the binary+    -- operations are composed as a balanced tree rather than a right+    -- associative or left associative one e.g.:+    --+    -- >>> s1 = Stream.fromList [1,2] `Stream.append` Stream.fromList [3,4]+    -- >>> s2 = Stream.fromList [4,5] `Stream.append` Stream.fromList [6,7]+    -- >>> s = s1 `Stream.append` s2+     -- ** Appending     , append      -- ** Interleaving-    -- | When interleaving more than two streams you may want to interleave-    -- them pairwise creating a balanced binary merge tree.     , interleave      -- ** Merging-    -- | When merging more than two streams you may want to merging them-    -- pairwise creating a balanced binary merge tree.-    ---    -- Merging of @n@ streams can be performed by combining the streams pair-    -- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used-    -- with 'concatMapWith' it will have O(n^2) performance.-     , mergeBy     , mergeByM      -- ** Zipping-    -- | When zipping more than two streams you may want to zip them-    -- pairwise creating a balanced binary tree.-    ---    -- Zipping of @n@ streams can be performed by combining the streams pair-    -- wise using 'mergeMapWith' with O(n * log n) time complexity. If used-    -- with 'concatMapWith' it will have O(n^2) performance.     , zipWith     , zipWithM     -- , ZipStream (..)@@ -399,10 +429,21 @@     , concatMapM      -- * Repeated Fold+    -- | Useful idioms:+    --+    -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)+    -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)+    -- >>> groupsBy eq f = Stream.parseMany (Parser.groupBy eq f)+    -- >>> groupsByRolling eq f = Stream.parseMany (Parser.groupByRolling eq f)+    -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)     , foldMany -- XXX Rename to foldRepeat+    , groupsOf     , parseMany-    , Array.chunksOf +    -- * Splitting+    , splitOn+    , wordsBy+     -- * Buffered Operations     -- | Operations that require buffering of the stream.     -- Reverse is essentially a left fold followed by an unfold.@@ -423,14 +464,20 @@     -- in Exception.Base      -- * Exceptions-    -- | Most of these combinators inhibit stream fusion, therefore, when+    -- | Note that the stream exception handling routines catch and handle+    -- exceptions only in the stream generation steps and not in the consumer+    -- of the stream. For example, if we are folding or parsing a stream - any+    -- exceptions in the fold or parse steps won't be observed by the stream+    -- exception handlers. Exceptions in the fold or parse steps can be handled+    -- using the fold or parse exception handling routines. You can wrap the+    -- stream elimination function in the monad exception handler to observe+    -- exceptions in the stream as well as the consumer.+    --+    -- Most of these combinators inhibit stream fusion, therefore, when     -- possible, they should be called in an outer loop to mitigate the cost.     -- For example, instead of calling them on a stream of chars call them on a     -- stream of arrays before flattening it to a stream of chars.     ---    -- See also: "Streamly.Internal.Data.Stream.Exception" for-    -- @Pre-release@ functions.-     , onException     , handle @@ -438,8 +485,19 @@     -- | 'bracket' is the most general resource management operation, all other     -- operations can be expressed using it. These functions have IO suffix     -- because the allocation and cleanup functions are IO actions. For-    -- generalized allocation and cleanup functions see the functions without+    -- generalized allocation and cleanup functions, see the functions without     -- the IO suffix in the "streamly" package.+    --+    -- Note that these operations bracket the stream generation only, they do+    -- not cover the stream consumer. This means if an exception occurs in+    -- the consumer of the stream (e.g. in a fold or parse step) then the+    -- exception won't be observed by the stream resource handlers, in that+    -- case the resource cleanup handler runs when the stream is garbage+    -- collected.+    --+    -- Monad level resource management can always be used around the stream+    -- elimination functions, such a function can observe exceptions in both+    -- the stream and its consumer.     , before     , afterIO     , finallyIO@@ -453,15 +511,14 @@     , runReaderT     , runStateT -    -- -- * Stream Types-    -- $serial-    -- , Interleave-    -- , Zip+    -- XXX Arrays could be different types, therefore, this should be in+    -- specific array module. Or maybe we should abstract over array types.+    -- * Stream of Arrays+    , Array.chunksOf     ) where -import qualified Streamly.Internal.Data.Array.Type as Array-import Streamly.Internal.Data.Stream.StreamD+import Streamly.Internal.Data.Stream import Prelude        hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,                foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,@@ -469,6 +526,8 @@                reverse, iterate, init, and, or, lookup, foldr1, (!!),                scanl, scanl1, repeat, replicate, concatMap, span) +import qualified Streamly.Internal.Data.Array.Type as Array+ #include "DocTestDataStream.hs"  -- $overview@@ -506,11 +565,11 @@ -- -- == Stream Fusion ----- The fused 'Stream' type employs stream fusion for C-like performance when--- looping over data. It represents a stream source or transformation by--- defining a state machine with explicit state, and a step function working on--- the state. A typical stream operation consumes elements from the previous--- state machine in the pipeline, transforms them and yields new values for the+-- The fused 'Stream' type in this module employs stream fusion for C-like+-- performance when looping over data. It represents the stream as a state+-- machine using an explicit state, and a step function working on the state. A+-- typical stream operation consumes elements from the previous state machine+-- in a stream pipeline, transforms the elements and yields new values for the -- next stage to consume. The stream operations are modular and represent a -- single task, they have no knowledge of previous or next operation on the -- elements.@@ -519,7 +578,7 @@ -- transformation operations and a stream consumer. All these operations taken -- together form a closed loop processing the stream elements. Elements are -- transferred between stages using a boxed data constructor. However, all the--- stages of the pipeline are fused together by GHC, eliminating the+-- stages of the pipeline are fused together by GHC, eliminating the boxing of -- intermediate constructors, and thus forming a tight C like loop without any -- boxed data being used in the loop. --@@ -534,48 +593,45 @@ -- -- == Stream vs StreamK ----- The fused stream model avoids constructor allocations or function call--- overheads. However, the stream is represented as a state machine and to--- generate elements it has to navigate the decision tree of the state machine.--- Moreover, the state machine is cranked for each element in the stream. This--- performs extremely well when the number of states are limited. The state--- machine starts getting expensive as the number of states increase. For--- example, generating a million element stream from a list requires a single--- state and is very efficient. However, using fused 'cons' to generate a--- million element stream would be a disaster.+-- The fused stream model avoids constructor allocations and function call+-- overheads. However, the stream is represented as a state machine, and to+-- generate stream elements it has to navigate the decision tree of the state+-- machine. Moreover, the state machine is cranked for each element in the+-- stream. This performs extremely well when the number of states are limited.+-- The state machine starts getting expensive as the number of states increase.+-- For example, generating a stream from a list requires a single state and is+-- very efficient, even if it has millions of elements. However, using 'cons'+-- to construct a million element stream would be a disaster. -- -- A typical worst case scenario for fused stream model is a large number of -- `cons` or `append` operations. A few static `cons` or `append` operations--- are very fast and much faster than a CPS style stream. However, if we--- construct a large stream using `cons` it introduces as many states in the--- state machine as the number of elements. If we compose the `cons` as a--- binary tree it will take @n * log n@ time to navigate the tree, and @n * n@--- if it is a right associative composition.+-- are very fast and much faster than a CPS style stream because CPS involves a+-- function call for each element whereas fused stream involves a few+-- conditional branches in the state machine. However, constructing a large+-- stream using `cons` introduces as many states in the state machine as the+-- number of elements. If we compose `cons` as a balanced binary tree it will+-- take @n * log n@ time to navigate the tree, and @n * n@ if it is a right+-- associative composition. ----- For quadratic cases of fused stream, after a certain threshold the CPS--- stream would perform much better and exhibit linear performance behavior. -- Operations like 'cons' or 'append'; are typically recursively called to -- construct a lazy infinite stream. For such use cases the CPS style 'StreamK'--- type is provided. CPS streams do not have a state machine that needs to be+-- should be used. CPS streams do not have a state machine that needs to be -- cranked for each element, past state has no effect on the future element--- processing. However, it incurs a function call overhead for each operation--- for each element, which could be very large overhead compared to fused state--- machines even if it has many states and cranks it for each element. But in--- some cases scales tip in favor of the CPS stream. In those cases even though--- CPS has a large constant overhead, it has a linear performance rather than--- quadratic.+-- processing. However, CPS incurs a function call overhead for each element+-- processed, the overhead could be large compared to a fused state machine+-- even if it has many states. However, because of its linear performance+-- characterstics, after a certain threshold of stream compositions the CPS+-- stream would perform much better than the quadratic fused stream operations. ----- As a general guideline, if you have to use 'cons' or 'append' or operations--- of similar nature, at a large scale, then 'StreamK' should be used. When you--- need to compose the stream dynamically or recursively, then 'StreamK' should--- be used. Typically you would use a dynamically generated 'StreamK' with--- chunks of data which can then be processed by statically fused stream--- pipeline operations.+-- As a general guideline, you need to use 'StreamK' when you have to use+-- 'cons', 'append' or other operations having quadratic complexity at a large+-- scale. Typically, in such cases you need to compose the stream recursively,+-- by calling an operation in a loop. The decision to compose the stream is+-- taken at run time rather than statically at compile time. --+-- Typically you would compose a 'StreamK' of chunks of data so that the+-- StreamK overhead is not high, and then process  the chunks using 'Stream' by+-- using statically fused stream pipeline operations on the chunks.+-- -- 'Stream' and 'StreamK' types can be interconverted. See -- "Streamly.Data.StreamK" module for conversion operations.------ == Useful Idioms------ >>> fromListM = Stream.sequence . Stream.fromList--- >>> fromIndices f = fmap f $ Stream.enumerateFrom 0
− src/Streamly/Data/Stream/Zip.hs
@@ -1,16 +0,0 @@--- |--- Module      : Streamly.Data.Stream.Zip--- Copyright   : (c) 2017 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : released--- Portability : GHC----module Streamly.Data.Stream.Zip-    (-      ZipStream (..)-    )-where--import Streamly.Internal.Data.Stream.Zip
src/Streamly/Data/StreamK.hs view
@@ -8,12 +8,30 @@ -- Stability   : released -- Portability : GHC ----- Streams using Continuation Passing Style (CPS). See the @Stream vs StreamK@--- section in the "Streamly.Data.Stream" module to know when to use this--- module.+-- Streams represented as chains of functions calls using Continuation Passing+-- Style (CPS), suitable for dynamically composing potentially large number of+-- streams. ----- Please refer to "Streamly.Internal.Data.Stream.StreamK" for more functions--- that have not yet been released.+-- Unlike the statically fused operations in "Streamly.Data.Stream", StreamK+-- operations are less efficient, involving a function call overhead for each+-- element, but they exhibit linear O(n) time complexity wrt to the number of+-- stream compositions. Therefore, they are suitable for dynamically composing+-- streams e.g. appending potentially infinite streams in recursive loops.+-- While fused streams can be used to efficiently process elements as small as+-- a single byte, CPS streams are typically used on bigger chunks of data to+-- avoid the larger overhead per element. For more details See the @Stream vs+-- StreamK@ section in the "Streamly.Data.Stream" module.+--+-- In addition to the combinators in this module, you can use operations from+-- "Streamly.Data.Stream" for StreamK as well by converting StreamK to Stream+-- ('toStream'), and vice-versa ('fromStream'). Please refer to+-- "Streamly.Internal.Data.StreamK" for more functions that have not yet been+-- released.+--+-- For documentation see the corresponding combinators in+-- "Streamly.Data.Stream". Documentation has been omitted in this module unless+-- there is a difference worth mentioning or if the combinator does not exist+-- in "Streamly.Data.Stream".  -- Notes: --@@ -44,6 +62,11 @@      -- * Construction     -- ** Primitives+    -- | Primitives to construct a stream from pure values or monadic actions.+    -- All other stream construction and generation combinators described later+    -- can be expressed in terms of these primitives. However, the special+    -- versions provided in this module can be much more efficient in some+    -- cases. Users can create custom combinators using these primitives.     , nil     , nilM     , cons@@ -54,6 +77,8 @@     , fromEffect      -- ** From Stream+    -- | Please note that 'Stream' type does not observe any exceptions from+    -- the consumer of the stream whereas 'StreamK' does.     , fromStream     , toStream @@ -70,7 +95,8 @@     -- , foldBreak      -- ** Parsing-    -- , parseBreak+    , parse+    , parseBreak     , parseBreakChunks     , parseChunks @@ -80,6 +106,11 @@     , take      -- * Combining Two Streams+    -- | Unlike the operations in "Streamly.Data.Stream", these operations can+    -- be used to dynamically compose large number of streams e.g. using the+    -- 'concatMapWith' and 'mergeMapWith' operations. They have a linear O(n)+    -- time complexity wrt to the number of streams being composed.+     -- ** Appending     , append @@ -103,6 +134,12 @@     -- , CrossStreamK (..)      -- * Stream of streams+    -- | Some useful idioms:+    --+    -- >>> concatFoldableWith f = Prelude.foldr f StreamK.nil+    -- >>> concatMapFoldableWith f g = Prelude.foldr (f . g) StreamK.nil+    -- >>> concatForFoldableWith f xs g = Prelude.foldr (f . g) StreamK.nil xs+    --     , concatEffect     -- , concatMap     , concatMapWith@@ -111,10 +148,20 @@     -- * Buffered Operations     , reverse     , sortBy++    -- * Exceptions+    -- | Please note that 'Stream' type does not observe any exceptions from+    -- the consumer of the stream whereas 'StreamK' does.+    , handle++    -- * Resource Management+    -- | Please note that 'Stream' type does not observe any exceptions from+    -- the consumer of the stream whereas 'StreamK' does.+    , bracketIO     ) where -import Streamly.Internal.Data.Stream.StreamK+import Streamly.Internal.Data.StreamK import Prelude hiding (reverse, zipWith, mapM, dropWhile, take)  #include "DocTestDataStreamK.hs"@@ -145,9 +192,3 @@ -- 1 -- 2 -- [1,2]------ == Exception Handling------ There are no native exception handling operations in the StreamK module,--- please convert to 'Stream' type and use exception handling operations from--- "Streamly.Data.Stream".
src/Streamly/Data/Unfold.hs view
@@ -53,6 +53,9 @@     , replicateM     , iterateM +    -- ** Enumeration+    , Enumerable (..)+     -- ** From Containers     , fromList     , fromListM@@ -62,6 +65,8 @@     -- ** Mapping on Input     , lmap     , lmapM+    , first+    , second      -- ** Mapping on Output     , mapM
src/Streamly/FileSystem/Handle.hs view
@@ -1,4 +1,4 @@-#include "inline.hs"+{-# LANGUAGE CPP #-}  -- | -- Module      : Streamly.FileSystem.Handle@@ -40,6 +40,12 @@ -- module Streamly.FileSystem.Handle     (+     -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Singleton IO     -- | Read or write a single buffer.       getChunk@@ -75,13 +81,13 @@     -- position of the file handle. The stream ends as soon as EOF is     -- encountered. -    -- -- *** Streams-    -- , read-    -- , readWith-    -- , readChunks-    -- , readChunksWith+    -- *** Streams+    , read+    , readWith+    , readChunks+    , readChunksWith -    -- -- *** Unfolds+    -- *** Unfolds     , reader     , readerWith     , chunkReader@@ -98,34 +104,14 @@     , writeChunks       -- * Deprecated-    , read     , readWithBufferOf-    , readChunks     , readChunksWithBufferOf     , writeChunksWithBufferOf     , writeWithBufferOf     ) where -import Control.Monad.IO.Class (MonadIO(..))-import Data.Word (Word8)-import Streamly.Internal.Data.Array.Type (Array)-import Streamly.Internal.Data.Unfold.Type (Unfold)-import System.IO (Handle)--import Streamly.Internal.FileSystem.Handle hiding (read, readChunks)+import Streamly.Internal.FileSystem.Handle import Prelude hiding (read) --- | Same as 'reader'----{-# DEPRECATED read "Please use 'reader' instead" #-}-{-# INLINE read #-}-read :: MonadIO m => Unfold m Handle Word8-read = reader---- | Same as 'chunkReader'----{-# DEPRECATED readChunks "Please use 'chunkReader' instead" #-}-{-# INLINE readChunks #-}-readChunks :: MonadIO m => Unfold m Handle (Array Word8)-readChunks = chunkReader+#include "DocTestFileSystemHandle.hs"
src/Streamly/Internal/Console/Stdio.hs view
@@ -47,12 +47,12 @@ import Prelude hiding (read)  import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Stream.StreamD (Stream)+import Streamly.Internal.Data.Stream (Stream) import Streamly.Internal.Data.Unfold (Unfold) import Streamly.Internal.Data.Fold (Fold)  import qualified Streamly.Internal.Data.Array as Array-import qualified Streamly.Internal.Data.Stream.StreamD as Stream+import qualified Streamly.Internal.Data.Stream as Stream     (intersperseMSuffix) import qualified Streamly.Internal.Data.Unfold as Unfold import qualified Streamly.Internal.FileSystem.Handle as Handle
src/Streamly/Internal/Data/Array.hs view
@@ -17,42 +17,24 @@     -- $design      -- * The Array Type-      Array+      module Streamly.Internal.Data.Array.Type      -- * Construction--    -- Pure List APIs-    , A.fromListN-    , A.fromList-     -- Stream Folds     , fromStreamN     , fromStream      -- Monadic Folds-    , A.writeN      -- drop new-    , A.writeNAligned-    , A.write       -- full buffer     , writeLastN -    -- * Elimination-    -- ** Conversion-    , A.toList--    -- ** Streams-    , A.read-    , A.readRev--    -- ** Unfolds+    -- * Unfolds     , reader     , readerUnsafe-    , A.readerRev     , producer -- experimental      -- * Random Access     -- , (!!)     , getIndex-    , A.unsafeIndex -- XXX Rename to getIndexUnsafe??     , getIndexRev     , last           -- XXX getIndexLast?     , getIndices@@ -64,7 +46,6 @@     -- , getIndicesUptoRev  -- read from end to the given position in file      -- * Size-    , length     , null      -- * Search@@ -77,10 +58,7 @@     , cast     , asBytes     , castUnsafe-    , asPtrUnsafe     , asCStringUnsafe-    , A.unsafeFreeze -- asImmutableUnsafe?-    , A.unsafeThaw   -- asMutableUnsafe?      -- * Subarrays     , getSliceUnsafe@@ -92,20 +70,22 @@     -- * Streaming Operations     , streamTransform -    -- ** Folding+    -- * Folding     , streamFold     , fold -    -- * Deprecated-    , A.toStream-    , A.toStreamRev+    -- * Serialization+    , encodeAs+    , serialize+    , pinnedSerialize+    , deserialize     ) where +#include "assert.hs" #include "inline.hs" #include "ArrayMacros.h" -import Control.Exception (assert) import Control.Monad (when) import Control.Monad.IO.Class (MonadIO(..)) import Data.Functor.Identity (Identity)@@ -114,34 +94,32 @@ import Foreign.C.String (CString) import Foreign.Ptr (castPtr) import Foreign.Storable (Storable)-import Streamly.Internal.Data.Unboxed-    ( Unbox-    , peekWith-    , sizeOf-    )+import Streamly.Internal.Data.Unbox (Unbox(..)) import Prelude hiding (length, null, last, map, (!!), read, concat) -import Streamly.Internal.Data.Array.Mut.Type (ArrayUnsafe(..))-import Streamly.Internal.Data.Array.Type-    (Array(..), length, asPtrUnsafe)+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))+import Streamly.Internal.Data.Serialize.Type (Serialize) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Internal.Data.Producer.Type (Producer(..))-import Streamly.Internal.Data.Stream.StreamD (Stream)+import Streamly.Internal.Data.Stream (Stream) import Streamly.Internal.Data.Tuple.Strict (Tuple3Fused'(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.System.IO (unsafeInlineIO) -import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Array.Mut as MA+import qualified Streamly.Internal.Data.Serialize.Type as Serialize+import qualified Streamly.Internal.Data.MutByteArray.Type as MBA+import qualified Streamly.Internal.Data.MutArray as MA import qualified Streamly.Internal.Data.Array.Type as A import qualified Streamly.Internal.Data.Fold as FL import qualified Streamly.Internal.Data.Producer.Type as Producer import qualified Streamly.Internal.Data.Producer as Producer-import qualified Streamly.Internal.Data.Ring.Unboxed as RB-import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamD as Stream+import qualified Streamly.Internal.Data.Ring as RB+import qualified Streamly.Internal.Data.Stream as D+import qualified Streamly.Internal.Data.Stream as Stream import qualified Streamly.Internal.Data.Unfold as Unfold +import Streamly.Internal.Data.Array.Type+ #include "DocTestDataArray.hs"  -- $design@@ -239,13 +217,12 @@             --             -- This should be safe as the array contents are guaranteed to be             -- evaluated/written to before we peek at them.-            let !x = unsafeInlineIO $ peekWith contents p+            let !x = unsafeInlineIO $ peekAt p contents             let !p1 = INDEX_NEXT(p,a)             return $ D.Yield x (ArrayUnsafe contents end p1)  -- | ----- >>> import qualified Streamly.Internal.Data.Array.Type as Array -- >>> null arr = Array.byteLength arr == 0 -- -- /Pre-release/@@ -263,12 +240,11 @@         $ do                 let elemPtr = RINDEX_OF(arrEnd arr, i, a)                 if i >= 0 && elemPtr >= arrStart arr-                then Just <$> peekWith (arrContents arr) elemPtr+                then Just <$> peekAt elemPtr (arrContents arr)                 else return Nothing  -- | ----- >>> import qualified Streamly.Internal.Data.Array as Array -- >>> last arr = Array.getIndexRev arr 0 -- -- /Pre-release/@@ -288,7 +264,7 @@        (Storable a, Unbox a, MonadIO m) => Int -> Fold m a (Array a) writeLastN n     | n <= 0 = fmap (const mempty) FL.drain-    | otherwise = A.unsafeFreeze <$> Fold step initial done+    | otherwise = A.unsafeFreeze <$> Fold step initial done done      where @@ -301,7 +277,7 @@          in fmap f $ liftIO $ RB.new n      done (Tuple3Fused' rb rh i) = do-        arr <- liftIO $ MA.newPinned n+        arr <- liftIO $ MA.new n         foldFunc i rh snoc' arr rb      -- XXX We should write a read unfold for ring.@@ -425,7 +401,7 @@         $ do                 let elemPtr = INDEX_OF(arrStart arr, i, a)                 if i >= 0 && INDEX_VALID(elemPtr, arrEnd arr, a)-                then Just <$> peekWith (arrContents arr) elemPtr+                then Just <$> peekAt elemPtr (arrContents arr)                 else return Nothing  -- | Given a stream of array indices, read the elements on those indices from@@ -538,7 +514,9 @@         then Nothing         else Just $ castUnsafe arr --- | Convert an array of any type into a null terminated CString Ptr.+-- | Convert an array of any type into a null terminated CString Ptr.  If the+-- array is unpinned it is first converted to a pinned array which requires a+-- copy. -- -- /Unsafe/ --@@ -548,8 +526,8 @@ -- asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b asCStringUnsafe arr act = do-    -- XXX Ensure a pinned allocation here.     let arr1 = asBytes arr <> A.fromList [0]+    -- asPtrUnsafe makes sure the array is pinned     asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)  -------------------------------------------------------------------------------@@ -571,3 +549,39 @@ {-# INLINE streamFold #-} streamFold :: (Monad m, Unbox a) => (Stream m a -> m b) -> Array a -> m b streamFold f arr = f (A.read arr)++--------------------------------------------------------------------------------+-- Serialization+--------------------------------------------------------------------------------++{-# INLINE encodeAs #-}+encodeAs :: forall a. Serialize a => PinnedState -> a -> Array Word8+encodeAs ps a =+    unsafeInlineIO $ do+        let len = Serialize.addSizeTo 0 a+        mbarr <- MBA.newBytesAs ps len+        off <- Serialize.serializeAt 0 mbarr a+        assertM(len == off)+        pure $ Array mbarr 0 off++{-# INLINE serialize #-}+serialize :: Serialize a => a -> Array Word8+serialize = encodeAs Unpinned++-- | Serialize a Haskell type to a pinned byte array. The array is allocated+-- using pinned memory so that it can be used directly in OS APIs for writing+-- to file or sending over the network.+{-# INLINE pinnedSerialize #-}+pinnedSerialize :: Serialize a => a -> Array Word8+pinnedSerialize = encodeAs Pinned++-- | Decode a Haskell type from a byte array containing its serialized+-- representation.+{-# INLINE deserialize #-}+deserialize :: Serialize a => Array Word8 -> a+deserialize arr@(Array {..}) = unsafeInlineIO $ do+    let lenArr = length arr+    (off, val) <-+        Serialize.deserializeAt arrStart arrContents (arrStart + lenArr)+    assertM(off == arrStart + lenArr)+    pure val
src/Streamly/Internal/Data/Array/Generic.hs view
@@ -4,7 +4,6 @@ -- -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com--- Stability   : pre-release -- Portability : GHC -- module Streamly.Internal.Data.Array.Generic@@ -19,10 +18,14 @@      , fromStreamN     , fromStream+    , fromPureStream+    , fromByteStr#      , fromListN     , fromList +    , chunksOf+     -- * Elimination     , length     , reader@@ -38,6 +41,7 @@      -- * Random Access     , getIndexUnsafe+    , getIndex     , getSliceUnsafe     , strip     )@@ -47,25 +51,28 @@  import Control.Monad (replicateM) import Control.Monad.IO.Class (MonadIO)+import Data.Functor.Identity (Identity(..))+import Data.Word (Word8) import GHC.Base (MutableArray#, RealWorld)+import GHC.Exts (Addr#) import GHC.IO (unsafePerformIO) import Text.Read (readPrec)  import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)+import Streamly.Internal.Data.Stream.Type (Stream) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.System.IO (unsafeInlineIO) -import qualified Streamly.Internal.Data.Array.Generic.Mut.Type as MArray+import qualified Streamly.Internal.Data.MutArray.Generic as MArray import qualified Streamly.Internal.Data.Fold.Type as FL import qualified Streamly.Internal.Data.Producer.Type as Producer import qualified Streamly.Internal.Data.Producer as Producer-import qualified Streamly.Internal.Data.Ring as RB-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D+import qualified Streamly.Internal.Data.Ring.Generic as RB+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Generate as D import qualified Text.ParserCombinators.ReadPrec as ReadPrec -import Prelude hiding (foldr, length, read)+import Prelude hiding (Foldable(..), read)  ------------------------------------------------------------------------------- -- Array Data Type@@ -113,7 +120,25 @@ write :: MonadIO m => Fold m a (Array a) write = fmap unsafeFreeze MArray.write +fromPureStream :: Stream Identity a -> Array a+fromPureStream x =+    unsafePerformIO $ fmap (unsafeFreeze) (MArray.fromPureStream x)+-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)+-- fromPureStream = fromList . runIdentity . D.toList++fromByteStr# :: Addr# -> Array Word8+fromByteStr# addr = fromPureStream (D.fromByteStr# addr)+ -------------------------------------------------------------------------------+-- Stream Ops+-------------------------------------------------------------------------------++{-# INLINE_NORMAL chunksOf #-}+chunksOf :: forall m a. MonadIO m+    => Int -> Stream m a -> Stream m (Array a)+chunksOf n strm = fmap unsafeFreeze $ MArray.chunksOf n strm++------------------------------------------------------------------------------- -- Construction - from streams ------------------------------------------------------------------------------- @@ -208,6 +233,26 @@ getIndexUnsafe i arr =     unsafePerformIO $ MArray.getIndexUnsafe i (unsafeThaw arr) +-- | Lookup the element at the given index. Index starts from 0.+--+{-# INLINE getIndex #-}+getIndex :: Int -> Array a -> Maybe a+getIndex i arr@Array {..} =+    if i >= 0 && i < arrLen+    then Just $ getIndexUnsafe i arr+    else Nothing++-- >>> import qualified Streamly.Data.Stream as Stream+-- >>> import qualified Streamly.Data.Fold as Fold+-- >>> import qualified Streamly.Internal.Data.Array.Generic as Array+-- >>> import Data.Function ((&))+-- >>> :{+--  Stream.fromList [1,2,3,4,5::Int]+--      & Stream.scan (Array.writeLastN 2)+--      & Stream.fold Fold.toList+--  :}+-- [fromList [],fromList [1],fromList [1,2],fromList [2,3],fromList [3,4],fromList [4,5]]+-- {-# INLINE writeLastN #-} writeLastN :: MonadIO m => Int -> Fold m a (Array a) writeLastN n = FL.rmapM f (RB.writeLastN n)@@ -215,7 +260,7 @@     where      f rb = do-        arr <- RB.toMutArray 0 n rb+        arr <- RB.copyToMutArray 0 n rb         return $ unsafeFreeze arr  {-# INLINE getSliceUnsafe #-}
− src/Streamly/Internal/Data/Array/Generic/Mut/Type.hs
@@ -1,796 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE UnboxedTuples #-}--- |--- Module      : Streamly.Internal.Data.Array.Generic.Mut.Type--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD3-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Array.Generic.Mut.Type-(-    -- * Type-    -- $arrayNotes-      MutArray (..)--    -- * Constructing and Writing-    -- ** Construction-    , nil--    -- *** Uninitialized Arrays-    , new-    -- , newArrayWith--    -- *** From streams-    , writeNUnsafe-    , writeN-    , writeWith-    , write--    -- , writeRevN-    -- , writeRev--    -- ** From containers-    -- , fromListN-    -- , fromList-    -- , fromStreamDN-    -- , fromStreamD--    -- * Random writes-    , putIndex-    , putIndexUnsafe-    , putIndices-    -- , putFromThenTo-    -- , putFrom -- start writing at the given position-    -- , putUpto -- write from beginning up to the given position-    -- , putFromTo-    -- , putFromRev-    -- , putUptoRev-    , modifyIndexUnsafe-    , modifyIndex-    -- , modifyIndices-    -- , modify-    -- , swapIndices--    -- * Growing and Shrinking-    -- Arrays grow only at the end, though it is possible to grow on both sides-    -- and therefore have a cons as well as snoc. But that will require two-    -- bounds in the array representation.--    -- ** Reallocation-    , realloc-    , uninit--    -- ** Appending elements-    , snocWith-    , snoc-    -- , snocLinear-    -- , snocMay-    , snocUnsafe--    -- ** Appending streams-    -- , writeAppendNUnsafe-    -- , writeAppendN-    -- , writeAppendWith-    -- , writeAppend--    -- ** Truncation-    -- These are not the same as slicing the array at the beginning, they may-    -- reduce the length as well as the capacity of the array.-    -- , truncateWith-    -- , truncate-    -- , truncateExp--    -- * Eliminating and Reading--    -- ** Unfolds-    , reader-    -- , readerRev-    , producerWith -- experimental-    , producer -- experimental--    -- ** To containers-    , toStreamD-    , readRev-    , toStreamK-    -- , toStreamKRev-    , toList--    -- ** Random reads-    , getIndex-    , getIndexUnsafe-    -- , getIndices-    -- , getFromThenTo-    -- , getIndexRev--    -- * Size-    , length--    -- * In-place Mutation Algorithms-    , strip-    -- , reverse-    -- , permute-    -- , partitionBy-    -- , shuffleBy-    -- , divideBy-    -- , mergeBy--    -- * Folding-    -- , foldl'-    -- , foldr-    , cmp-    , eq--    -- * Arrays of arrays-    --  We can add dimensionality parameter to the array type to get-    --  multidimensional arrays. Multidimensional arrays would just be a-    --  convenience wrapper on top of single dimensional arrays.--    -- | Operations dealing with multiple arrays, streams of arrays or-    -- multidimensional array representations.--    -- ** Construct from streams-    -- , chunksOf-    -- , arrayStreamKFromStreamD-    -- , writeChunks--    -- ** Eliminate to streams-    -- , flattenArrays-    -- , flattenArraysRev-    -- , fromArrayStreamK--    -- ** Construct from arrays-    -- get chunks without copying-    , getSliceUnsafe-    , getSlice-    -- , getSlicesFromLenN-    -- , splitAt -- XXX should be able to express using getSlice-    -- , breakOn--    -- ** Appending arrays-    -- , spliceCopy-    -- , spliceWith-    -- , splice-    -- , spliceExp-    , putSliceUnsafe-    -- , appendSlice-    -- , appendSliceFrom--    , clone-    )-where--#include "inline.hs"-#include "assert.hs"--import Control.Monad (when)-import Control.Monad.IO.Class (MonadIO(..))-import GHC.Base-    ( MutableArray#-    , RealWorld-    , copyMutableArray#-    , newArray#-    , readArray#-    , writeArray#-    )-import GHC.IO (IO(..))-import GHC.Int (Int(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Producer.Type (Producer (..))-import Streamly.Internal.Data.Unfold.Type (Unfold(..))--import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.Producer as Producer-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K--import Prelude hiding (read, length)--#include "DocTestDataMutArrayGeneric.hs"------------------------------------------------------------------------------------ MutArray Data Type----------------------------------------------------------------------------------data MutArray a =-    MutArray-        { arrContents# :: MutableArray# RealWorld a-          -- ^ The internal contents of the array representing the entire array.--        , arrStart :: {-# UNPACK #-}!Int-          -- ^ The starting index of this slice.--        , arrLen :: {-# UNPACK #-}!Int-          -- ^ The length of this slice.--        , arrTrueLen :: {-# UNPACK #-}!Int-          -- ^ This is the true length of the array. Coincidentally, this also-          -- represents the first index beyond the maximum acceptable index of-          -- the array. This is specific to the array contents itself and not-          -- dependent on the slice. This value should not change and is shared-          -- across all the slices.-        }--{-# INLINE bottomElement #-}-bottomElement :: a-bottomElement =-    error-        $ unwords-              [ funcName-              , "This is the bottom element of the array."-              , "This is a place holder and should never be reached!"-              ]--    where--    funcName = "Streamly.Internal.Data.Array.Generic.Mut.Type.bottomElement:"---- XXX Would be nice if GHC can provide something like newUninitializedArray# so--- that we do not have to write undefined or error in the whole array.---- | @new count@ allocates a zero length array that can be extended to hold--- up to 'count' items without reallocating.------ /Pre-release/-{-# INLINE new #-}-new :: MonadIO m => Int -> m (MutArray a)-new n@(I# n#) =-    liftIO-        $ IO-        $ \s# ->-              case newArray# n# bottomElement s# of-                  (# s1#, arr# #) ->-                      let ma = MutArray arr# 0 0 n-                       in (# s1#, ma #)---- XXX This could be pure?---- |--- Definition:------ >>> nil = MutArray.new 0-{-# INLINE nil #-}-nil :: MonadIO m => m (MutArray a)-nil = new 0------------------------------------------------------------------------------------ Random writes------------------------------------------------------------------------------------ | Write the given element to the given index of the array. Does not check if--- the index is out of bounds of the array.------ /Pre-release/-{-# INLINE putIndexUnsafe #-}-putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()-putIndexUnsafe i MutArray {..} x =-    assert (i >= 0 && i < arrLen)-    (liftIO-        $ IO-        $ \s# ->-              case i + arrStart of-                  I# n# ->-                      let s1# = writeArray# arrContents# n# x s#-                       in (# s1#, () #))--invalidIndex :: String -> Int -> a-invalidIndex label i =-    error $ label ++ ": invalid array index " ++ show i---- | /O(1)/ Write the given element at the given index in the array.--- Performs in-place mutation of the array.------ >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))------ /Pre-release/-{-# INLINE putIndex #-}-putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()-putIndex i arr@MutArray {..} x =-    if i >= 0 && i < arrLen-    then putIndexUnsafe i arr x-    else invalidIndex "putIndex" i---- | Write an input stream of (index, value) pairs to an array. Throws an--- error if any index is out of bounds.------ /Pre-release/-{-# INLINE putIndices #-}-putIndices :: MonadIO m-    => MutArray a -> Fold m (Int, a) ()-putIndices arr = FL.foldlM' step (return ())--    where--    step () (i, x) = liftIO (putIndex i arr x)---- | Modify a given index of an array using a modifier function without checking--- the bounds.------ Unsafe because it does not check the bounds of the array.------ /Pre-release/-modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndexUnsafe i MutArray {..} f = do-    liftIO-        $ IO-        $ \s# ->-              case i + arrStart of-                  I# n# ->-                      case readArray# arrContents# n# s# of-                          (# s1#, a #) ->-                              let (a1, b) = f a-                                  s2# = writeArray# arrContents# n# a1 s1#-                               in (# s2#, b #)---- | Modify a given index of an array using a modifier function.------ /Pre-release/-modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndex i arr@MutArray {..} f = do-    if i >= 0 && i < arrLen-    then modifyIndexUnsafe i arr f-    else invalidIndex "modifyIndex" i------------------------------------------------------------------------------------ Resizing------------------------------------------------------------------------------------ | Reallocates the array according to the new size. This is a safe function--- that always creates a new array and copies the old array into the new one.--- If the reallocated size is less than the original array it results in a--- truncated version of the original array.----realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)-realloc n arr = do-    arr1 <- new n-    let !newLen@(I# newLen#) = min n (arrLen arr)-        !(I# arrS#) = arrStart arr-        !(I# arr1S#) = arrStart arr1-        arrC# = arrContents# arr-        arr1C# = arrContents# arr1-    liftIO-        $ IO-        $ \s# ->-              let s1# = copyMutableArray# arrC# arrS# arr1C# arr1S# newLen# s#-               in (# s1#, arr1 {arrLen = newLen, arrTrueLen = n} #)--reallocWith ::-       MonadIO m => String -> (Int -> Int) -> Int -> MutArray a -> m (MutArray a)-reallocWith label sizer reqSize arr = do-    let oldSize = arrLen arr-        newSize = sizer oldSize-        safeSize = max newSize (oldSize + reqSize)-    assert (newSize >= oldSize + reqSize || error badSize) (return ())-    realloc safeSize arr--    where--    badSize = concat-        [ label-        , ": new array size is less than required size "-        , show reqSize-        , ". Please check the sizing function passed."-        ]------------------------------------------------------------------------------------ Snoc------------------------------------------------------------------------------------ XXX Not sure of the behavior of writeArray# if we specify an index which is--- out of bounds. This comment should be rewritten based on that.--- | Really really unsafe, appends the element into the first array, may--- cause silent data corruption or if you are lucky a segfault if the index--- is out of bounds.------ /Internal/-{-# INLINE snocUnsafe #-}-snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)-snocUnsafe arr@MutArray {..} a = do-    assert (arrStart + arrLen < arrTrueLen) (return ())-    let arr1 = arr {arrLen = arrLen + 1}-    putIndexUnsafe arrLen arr1 a-    return arr1---- NOINLINE to move it out of the way and not pollute the instruction cache.-{-# NOINLINE snocWithRealloc #-}-snocWithRealloc :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)-snocWithRealloc sizer arr x = do-    arr1 <- reallocWith "snocWithRealloc" sizer 1 arr-    snocUnsafe arr1 x---- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of--- the array increases by 1.------ If there is no reserved space available in @arr@ it is reallocated to a size--- in bytes determined by the @sizer oldSize@ function, where @oldSize@ is the--- original size of the array.------ Note that the returned array may be a mutated version of the original array.------ /Pre-release/-{-# INLINE snocWith #-}-snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)-snocWith sizer arr@MutArray {..} x = do-    if arrStart + arrLen < arrTrueLen-    then snocUnsafe arr x-    else snocWithRealloc sizer arr x---- XXX round it to next power of 2.---- | The array is mutated to append an additional element to it. If there is no--- reserved space available in the array then it is reallocated to double the--- original size.------ This is useful to reduce allocations when appending unknown number of--- elements.------ Note that the returned array may be a mutated version of the original array.------ >>> snoc = MutArray.snocWith (* 2)------ Performs O(n * log n) copies to grow, but is liberal with memory allocation.------ /Pre-release/-{-# INLINE snoc #-}-snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)-snoc = snocWith (* 2)---- | Make the uninitialized memory in the array available for use extending it--- by the supplied length beyond the current length of the array. The array may--- be reallocated.----{-# INLINE uninit #-}-uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)-uninit arr@MutArray{..} len =-    if arrStart + arrLen + len <= arrTrueLen-    then return $ arr {arrLen = arrLen + len}-    else realloc (arrLen + len) arr------------------------------------------------------------------------------------ Random reads------------------------------------------------------------------------------------ | Return the element at the specified index without checking the bounds.------ Unsafe because it does not check the bounds of the array.-{-# INLINE_NORMAL getIndexUnsafe #-}-getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a-getIndexUnsafe n MutArray {..} =-    liftIO-        $ IO-        $ \s# ->-              let !(I# i#) = arrStart + n-               in readArray# arrContents# i# s#---- | /O(1)/ Lookup the element at the given index. Index starts from 0.----{-# INLINE getIndex #-}-getIndex :: MonadIO m => Int -> MutArray a -> m a-getIndex i arr@MutArray {..} =-    if i >= 0 && i < arrLen-    then getIndexUnsafe i arr-    else invalidIndex "getIndex" i------------------------------------------------------------------------------------ Subarrays------------------------------------------------------------------------------------ XXX We can also get immutable slices.---- | /O(1)/ Slice an array in constant time.------ Unsafe: The bounds of the slice are not checked.------ /Unsafe/------ /Pre-release/-{-# INLINE getSliceUnsafe #-}-getSliceUnsafe-    :: Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSliceUnsafe index len arr@MutArray {..} =-    assert (index >= 0 && len >= 0 && index + len <= arrLen)-        $ arr {arrStart = arrStart + index, arrLen = len}---- | /O(1)/ Slice an array in constant time. Throws an error if the slice--- extends out of the array bounds.------ /Pre-release/-{-# INLINE getSlice #-}-getSlice-    :: Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSlice index len arr@MutArray{..} =-    if index >= 0 && len >= 0 && index + len <= arrLen-    then arr {arrStart = arrStart + index, arrLen = len}-    else error-             $ "getSlice: invalid slice, index "-             ++ show index ++ " length " ++ show len------------------------------------------------------------------------------------ to Lists and streams------------------------------------------------------------------------------------ XXX Maybe faster to create a list explicitly instead of mapM, if list fusion--- does not work well.---- | Convert an 'Array' into a list.------ /Pre-release/-{-# INLINE toList #-}-toList :: MonadIO m => MutArray a -> m [a]-toList arr@MutArray{..} = mapM (`getIndexUnsafe` arr) [0 .. (arrLen - 1)]---- | Use the 'read' unfold instead.------ @toStreamD = D.unfold read@------ We can try this if the unfold has any performance issues.-{-# INLINE_NORMAL toStreamD #-}-toStreamD :: MonadIO m => MutArray a -> D.Stream m a-toStreamD arr@MutArray{..} =-    D.mapM (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)---- Check equivalence with StreamK.fromStream . toStreamD and remove-{-# INLINE toStreamK #-}-toStreamK :: MonadIO m => MutArray a -> K.StreamK m a-toStreamK arr@MutArray{..} = K.unfoldrM step 0--    where--    step i-        | i == arrLen = return Nothing-        | otherwise = do-            x <- getIndexUnsafe i arr-            return $ Just (x, i + 1)--{-# INLINE_NORMAL readRev #-}-readRev :: MonadIO m => MutArray a -> D.Stream m a-readRev arr@MutArray{..} =-    D.mapM (`getIndexUnsafe` arr)-        $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0------------------------------------------------------------------------------------ Folds------------------------------------------------------------------------------------ XXX deduplicate this across unboxed array and this module?---- | The default chunk size by which the array creation routines increase the--- size of the array when the array is grown linearly.-arrayChunkSize :: Int-arrayChunkSize = 1024---- | Like 'writeN' but does not check the array bounds when writing. The fold--- driver must not call the step function more than 'n' times otherwise it will--- corrupt the memory and crash. This function exists mainly because any--- conditional in the step function blocks fusion causing 10x performance--- slowdown.------ /Pre-release/-{-# INLINE_NORMAL writeNUnsafe #-}-writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)-writeNUnsafe n = Fold step initial return--    where--    initial = FL.Partial <$> new (max n 0)--    step arr x = FL.Partial <$> snocUnsafe arr x---- | @writeN n@ folds a maximum of @n@ elements from the input stream to an--- 'Array'.------ >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)------ /Pre-release/-{-# INLINE_NORMAL writeN #-}-writeN :: MonadIO m => Int -> Fold m a (MutArray a)-writeN n = FL.take n $ writeNUnsafe n---- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.newPinned n)--- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)--- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)---- | @writeWith minCount@ folds the whole input to a single array. The array--- starts at a size big enough to hold minCount elements, the size is doubled--- every time the array needs to be grown.------ /Caution! Do not use this on infinite streams./------ /Pre-release/-{-# INLINE_NORMAL writeWith #-}-writeWith :: MonadIO m => Int -> Fold m a (MutArray a)--- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (newPinned n)-writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial--    where--    initial = do-        when (elemCount < 0) $ error "writeWith: elemCount is negative"-        liftIO $ new elemCount--    step arr@(MutArray _ start end bound) x-        | end == bound = do-        let oldSize = end - start-            newSize = max (oldSize * 2) 1-        arr1 <- liftIO $ realloc newSize arr-        snocUnsafe arr1 x-    step arr x = snocUnsafe arr x--    -- extract = liftIO . rightSize-    extract = return---- | Fold the whole input to a single array.------ Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes--- rounded up to the element size.------ /Caution! Do not use this on infinite streams./----{-# INLINE write #-}-write :: MonadIO m => Fold m a (MutArray a)-write = writeWith arrayChunkSize------------------------------------------------------------------------------------ Unfolds------------------------------------------------------------------------------------ | Resumable unfold of an array.----{-# INLINE_NORMAL producerWith #-}-producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a-producerWith liftio = Producer step inject extract--    where--    {-# INLINE inject #-}-    inject arr = return (arr, 0)--    {-# INLINE extract #-}-    extract (arr, i) =-        return $ arr {arrStart = arrStart arr + i, arrLen = arrLen arr - i}--    {-# INLINE_LATE step #-}-    step (arr, i)-        | assert (arrLen arr >= 0) (i == arrLen arr) = return D.Stop-    step (arr, i) = do-        x <- liftio $ getIndexUnsafe i arr-        return $ D.Yield x (arr, i + 1)---- | Resumable unfold of an array.----{-# INLINE_NORMAL producer #-}-producer :: MonadIO m => Producer m (MutArray a) a-producer = producerWith liftIO---- | Unfold an array into a stream.----{-# INLINE_NORMAL reader #-}-reader :: MonadIO m => Unfold m (MutArray a) a-reader = Producer.simplify producer------------------------------------------------------------------------------------- Appending arrays------------------------------------------------------------------------------------- | Put a sub range of a source array into a subrange of a destination array.--- This is not safe as it does not check the bounds.-{-# INLINE putSliceUnsafe #-}-putSliceUnsafe :: MonadIO m =>-    MutArray a -> Int -> MutArray a -> Int -> Int -> m ()-putSliceUnsafe src srcStart dst dstStart len = liftIO $ do-    assertM(len <= arrLen dst)-    assertM(len <= arrLen src)-    let !(I# srcStart#) = srcStart + arrStart src-        !(I# dstStart#) = dstStart + arrStart dst-        !(I# len#) = len-    let arrS# = arrContents# src-        arrD# = arrContents# dst-    IO $ \s# -> (# copyMutableArray#-                    arrS# srcStart# arrD# dstStart# len# s#-                , () #)--{-# INLINE clone #-}-clone :: MonadIO m => MutArray a -> m (MutArray a)-clone src = liftIO $ do-    let len = arrLen src-    dst <- new len-    putSliceUnsafe src 0 dst 0 len-    return dst------------------------------------------------------------------------------------ Size----------------------------------------------------------------------------------{-# INLINE length #-}-length :: MutArray a -> Int-length = arrLen------------------------------------------------------------------------------------ Equality------------------------------------------------------------------------------------ | Compare the length of the arrays. If the length is equal, compare the--- lexicographical ordering of two underlying byte arrays otherwise return the--- result of length comparison.------ /Pre-release/-{-# INLINE cmp #-}-cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering-cmp a1 a2 =-    case compare lenA1 lenA2 of-        EQ -> loop (lenA1 - 1)-        x -> return x--    where--    lenA1 = length a1-    lenA2 = length a2--    loop i-        | i < 0 = return EQ-        | otherwise = do-            v1 <- getIndexUnsafe i a1-            v2 <- getIndexUnsafe i a2-            case compare v1 v2 of-                EQ -> loop (i - 1)-                x -> return x--{-# INLINE eq #-}-eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool-eq a1 a2 =-    if lenA1 == lenA2-    then loop (lenA1 - 1)-    else return False--    where--    lenA1 = length a1-    lenA2 = length a2--    loop i-        | i < 0 = return True-        | otherwise = do-            v1 <- getIndexUnsafe i a1-            v2 <- getIndexUnsafe i a2-            if v1 == v2-            then loop (i - 1)-            else return False--{-# INLINE strip #-}-strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)-strip p arr = liftIO $ do-    let lastIndex = length arr - 1-    indexR <- getIndexR lastIndex -- last predicate failing index-    if indexR < 0-    then nil-    else do-        indexL <- getIndexL 0 -- first predicate failing index-        if indexL == 0 && indexR == lastIndex-        then return arr-        else-           let newLen = indexR - indexL + 1-            in return $ getSliceUnsafe indexL newLen arr--    where--    getIndexR idx-        | idx < 0 = return idx-        | otherwise = do-            r <- getIndexUnsafe idx arr-            if p r-            then getIndexR (idx - 1)-            else return idx--    getIndexL idx = do-        r <- getIndexUnsafe idx arr-        if p r-        then getIndexL (idx + 1)-        else return idx
− src/Streamly/Internal/Data/Array/Mut.hs
@@ -1,86 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Array.Mut--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Array.Mut-    (-      module Streamly.Internal.Data.Array.Mut.Type-    , splitOn-    , genSlicesFromLen-    , getSlicesFromLen-    , fromStream-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO(..))-import Streamly.Internal.Data.Unboxed (Unbox)-import Streamly.Internal.Data.Stream.StreamD (Stream)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))--import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Unfold as Unfold--import Prelude hiding (foldr, length, read, splitAt)-import Streamly.Internal.Data.Array.Mut.Type---- | Split the array into a stream of slices using a predicate. The element--- matching the predicate is dropped.------ /Pre-release/-{-# INLINE splitOn #-}-splitOn :: (MonadIO m, Unbox a) =>-    (a -> Bool) -> MutArray a -> Stream m (MutArray a)-splitOn predicate arr =-    fmap (\(i, len) -> getSliceUnsafe i len arr)-        $ D.sliceOnSuffix predicate (toStreamD arr)---- | Generate a stream of array slice descriptors ((index, len)) of specified--- length from an array, starting from the supplied array index. The last slice--- may be shorter than the requested length depending on the array length.------ /Pre-release/-{-# INLINE genSlicesFromLen #-}-genSlicesFromLen :: forall m a. (Monad m, Unbox a)-    => Int -- ^ from index-    -> Int -- ^ length of the slice-    -> Unfold m (MutArray a) (Int, Int)-genSlicesFromLen from len =-    let fromThenTo n = (from, from + len, n - 1)-        mkSlice n i = return (i, min len (n - i))-     in Unfold.lmap length-        $ Unfold.mapM2 mkSlice-        $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo---- | Generate a stream of slices of specified length from an array, starting--- from the supplied array index. The last slice may be shorter than the--- requested length depending on the array length.------ /Pre-release/-{-# INLINE getSlicesFromLen #-}-getSlicesFromLen :: forall m a. (Monad m, Unbox a)-    => Int -- ^ from index-    -> Int -- ^ length of the slice-    -> Unfold m (MutArray a) (MutArray a)-getSlicesFromLen from len =-    let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr-     in Unfold.mapM2 mkSlice (genSlicesFromLen from len)---- | Create an 'Array' from a stream. This is useful when we want to create a--- single array from a stream of unknown size. 'writeN' is at least twice--- as efficient when the size is already known.------ Note that if the input stream is too large memory allocation for the array--- may fail.  When the stream size is not known, `chunksOf` followed by--- processing of indvidual arrays in the resulting stream should be preferred.------ /Pre-release/-{-# INLINE fromStream #-}-fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)-fromStream = fromStreamD--- fromStream (Stream m) = P.fold write m
− src/Streamly/Internal/Data/Array/Mut/Stream.hs
@@ -1,323 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Array.Mut.Stream--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD3-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Combinators to efficiently manipulate streams of mutable arrays.----module Streamly.Internal.Data.Array.Mut.Stream-    (-    -- * Generation-      chunksOf--    -- * Compaction-    , packArraysChunksOf-    , SpliceState (..)-    , lpackArraysChunksOf-    , compact-    , compactLE-    , compactEQ-    , compactGE-    )-where--#include "inline.hs"-#include "ArrayMacros.h"--import Control.Monad.IO.Class (MonadIO(..))-import Control.Monad (when)-import Data.Bifunctor (first)-import Data.Proxy (Proxy(..))-import Streamly.Internal.Data.Unboxed (Unbox, sizeOf)-import Streamly.Internal.Data.Array.Mut.Type (MutArray(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Parser (ParseError)-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))--import qualified Streamly.Internal.Data.Array.Mut.Type as MArray-import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Parser.ParserD as ParserD---- | @chunksOf n stream@ groups the elements in the input stream into arrays of--- @n@ elements each.------ Same as the following but may be more efficient:------ > chunksOf n = Stream.foldMany (MArray.writeN n)------ /Pre-release/-{-# INLINE chunksOf #-}-chunksOf :: (MonadIO m, Unbox a)-    => Int -> Stream m a -> Stream m (MutArray a)-chunksOf = MArray.chunksOf------------------------------------------------------------------------------------ Compact----------------------------------------------------------------------------------data SpliceState s arr-    = SpliceInitial s-    | SpliceBuffering s arr-    | SpliceYielding arr (SpliceState s arr)-    | SpliceFinish---- XXX This can be removed once compactLEFold/compactLE are implemented.------ | This mutates the first array (if it has space) to append values from the--- second one. This would work for immutable arrays as well because an--- immutable array never has space so a new array is allocated instead of--- mutating it.------ | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- maximum specified size. Note that if a single array is bigger than the--- specified size we do not split it to fit. When we coalesce multiple arrays--- if the size would exceed the specified size we do not coalesce therefore the--- actual array size may be less than the specified chunk size.------ @since 0.7.0-{-# INLINE_NORMAL packArraysChunksOf #-}-packArraysChunksOf :: (MonadIO m, Unbox a)-    => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)-packArraysChunksOf n (D.Stream step state) =-    D.Stream step' (SpliceInitial state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (SpliceInitial st) = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.Array.Mut.Type.packArraysChunksOf: the size of "-                 ++ "arrays [" ++ show n ++ "] must be a natural number"-        r <- step gst st-        case r of-            D.Yield arr s -> return $-                let len = MArray.byteLength arr-                 in if len >= n-                    then D.Skip (SpliceYielding arr (SpliceInitial s))-                    else D.Skip (SpliceBuffering s arr)-            D.Skip s -> return $ D.Skip (SpliceInitial s)-            D.Stop -> return D.Stop--    step' gst (SpliceBuffering st buf) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                let len = MArray.byteLength buf + MArray.byteLength arr-                if len > n-                then return $-                    D.Skip (SpliceYielding buf (SpliceBuffering s arr))-                else do-                    buf' <- if MArray.byteCapacity buf < n-                            then liftIO $ MArray.realloc n buf-                            else return buf-                    buf'' <- MArray.splice buf' arr-                    return $ D.Skip (SpliceBuffering s buf'')-            D.Skip s -> return $ D.Skip (SpliceBuffering s buf)-            D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)--    step' _ SpliceFinish = return D.Stop--    step' _ (SpliceYielding arr next) = return $ D.Yield arr next---- XXX Remove this once compactLEFold is implemented--- lpackArraysChunksOf = Fold.many compactLEFold----{-# INLINE_NORMAL lpackArraysChunksOf #-}-lpackArraysChunksOf :: (MonadIO m, Unbox a)-    => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()-lpackArraysChunksOf n (Fold step1 initial1 extract1) =-    Fold step initial extract--    where--    initial = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.Array.Mut.Type.packArraysChunksOf: the size of "-                 ++ "arrays [" ++ show n ++ "] must be a natural number"--        r <- initial1-        return $ first (Tuple' Nothing) r--    extract (Tuple' Nothing r1) = extract1 r1-    extract (Tuple' (Just buf) r1) = do-        r <- step1 r1 buf-        case r of-            FL.Partial rr -> extract1 rr-            FL.Done _ -> return ()--    step (Tuple' Nothing r1) arr =-            let len = MArray.byteLength arr-             in if len >= n-                then do-                    r <- step1 r1 arr-                    case r of-                        FL.Done _ -> return $ FL.Done ()-                        FL.Partial s -> do-                            extract1 s-                            res <- initial1-                            return $ first (Tuple' Nothing) res-                else return $ FL.Partial $ Tuple' (Just arr) r1--    step (Tuple' (Just buf) r1) arr = do-            let len = MArray.byteLength buf + MArray.byteLength arr-            buf' <- if MArray.byteCapacity buf < len-                    then liftIO $ MArray.realloc (max n len) buf-                    else return buf-            buf'' <- MArray.splice buf' arr--            -- XXX this is common in both the equations of step-            if len >= n-            then do-                r <- step1 r1 buf''-                case r of-                    FL.Done _ -> return $ FL.Done ()-                    FL.Partial s -> do-                        extract1 s-                        res <- initial1-                        return $ first (Tuple' Nothing) res-            else return $ FL.Partial $ Tuple' (Just buf'') r1---- XXX Same as compactLE, to be removed once that is implemented.------ | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- maximum specified size in bytes.------ /Internal/-{-# INLINE compact #-}-compact :: (MonadIO m, Unbox a)-    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)-compact = packArraysChunksOf---- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- maximum specified size. Note that if a single array is bigger than the--- specified size we do not split it to fit. When we coalesce multiple arrays--- if the size would exceed the specified size we do not coalesce therefore the--- actual array size may be less than the specified chunk size.------ /Internal/-{-# INLINE_NORMAL compactLEParserD #-}-compactLEParserD ::-       forall m a. (MonadIO m, Unbox a)-    => Int -> ParserD.Parser (MutArray a) m (MutArray a)-compactLEParserD n = ParserD.Parser step initial extract--    where--    nBytes = n * SIZE_OF(a)--    initial =-        return-            $ if n <= 0-              then error-                       $ functionPath-                       ++ ": the size of arrays ["-                       ++ show n ++ "] must be a natural number"-              else ParserD.IPartial Nothing--    step Nothing arr =-        return-            $ let len = MArray.byteLength arr-               in if len >= nBytes-                  then ParserD.Done 0 arr-                  else ParserD.Partial 0 (Just arr)-    step (Just buf) arr =-        let len = MArray.byteLength buf + MArray.byteLength arr-         in if len > nBytes-            then return $ ParserD.Done 1 buf-            else do-                buf1 <--                    if MArray.byteCapacity buf < nBytes-                    then liftIO $ MArray.realloc nBytes buf-                    else return buf-                buf2 <- MArray.splice buf1 arr-                return $ ParserD.Partial 0 (Just buf2)--    extract Nothing = return $ ParserD.Done 0 MArray.nil-    extract (Just buf) = return $ ParserD.Done 0 buf--    functionPath =-        "Streamly.Internal.Data.Array.Mut.Stream.compactLEParserD"---- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- minimum specified size. Note that if all the arrays in the stream together--- are smaller than the specified size the resulting array will be smaller than--- the specified size. When we coalesce multiple arrays if the size would exceed--- the specified size we stop coalescing further.------ /Internal/-{-# INLINE_NORMAL compactGEFold #-}-compactGEFold ::-       forall m a. (MonadIO m, Unbox a)-    => Int -> FL.Fold m (MutArray a) (MutArray a)-compactGEFold n = Fold step initial extract--    where--    nBytes = n * SIZE_OF(a)--    initial =-        return-            $ if n < 0-              then error-                       $ functionPath-                       ++ ": the size of arrays ["-                       ++ show n ++ "] must be a natural number"-              else FL.Partial Nothing--    step Nothing arr =-        return-            $ let len = MArray.byteLength arr-               in if len >= nBytes-                  then FL.Done arr-                  else FL.Partial (Just arr)-    step (Just buf) arr = do-        let len = MArray.byteLength buf + MArray.byteLength arr-        buf1 <--            if MArray.byteCapacity buf < len-            then liftIO $ MArray.realloc (max len nBytes) buf-            else return buf-        buf2 <- MArray.splice buf1 arr-        if len >= n-        then return $ FL.Done buf2-        else return $ FL.Partial (Just buf2)--    extract Nothing = return MArray.nil-    extract (Just buf) = return buf--    functionPath =-        "Streamly.Internal.Data.Array.Mut.Stream.compactGEFold"---- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- maximum specified size in bytes.------ /Internal/-compactLE :: (MonadIO m, Unbox a) =>-    Int -> Stream m (MutArray a) -> Stream m (Either ParseError (MutArray a))-compactLE n = D.parseManyD (compactLEParserD n)---- | Like 'compactLE' but generates arrays of exactly equal to the size--- specified except for the last array in the stream which could be shorter.------ /Unimplemented/-{-# INLINE compactEQ #-}-compactEQ :: -- (MonadIO m, Unbox a) =>-    Int -> Stream m (MutArray a) -> Stream m (MutArray a)-compactEQ _n _xs = undefined-    -- IsStream.fromStreamD $ D.foldMany (compactEQFold n) (IsStream.toStreamD xs)---- | Like 'compactLE' but generates arrays of size greater than or equal to the--- specified except for the last array in the stream which could be shorter.------ /Internal/-{-# INLINE compactGE #-}-compactGE ::-       (MonadIO m, Unbox a)-    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)-compactGE n = D.foldMany (compactGEFold n)
− src/Streamly/Internal/Data/Array/Mut/Type.hs
@@ -1,2356 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE UnboxedTuples #-}--- |--- Module      : Streamly.Internal.Data.Array.Mut.Type--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD3-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Pinned and unpinned mutable array for 'Unboxed' types. Fulfils the following--- goals:------ * Random access (array)--- * Efficient storage (unboxed)--- * Performance (unboxed access)--- * Performance - in-place operations (mutable)--- * Performance - GC (pinned, mutable)--- * interfacing with OS (pinned)------ Stream and Fold APIs allow easy, efficient and convenient operations on--- arrays.------ Mutable arrays and file system files are quite similar, they can grow and--- their content is mutable. Therefore, both have similar APIs as well. We--- strive to keep the API consistent for both. Ideally, you should be able to--- replace one with another with little changes to the code.--module Streamly.Internal.Data.Array.Mut.Type-    (-    -- * Type-    -- $arrayNotes-      MutArray (..)-    , MutableByteArray-    , touch-    , pin-    , unpin--    -- * Constructing and Writing-    -- ** Construction-    , nil--    -- *** Uninitialized Arrays-    , newPinned-    , newPinnedBytes-    , newAlignedPinned-    , new-    , newArrayWith--    -- *** Initialized Arrays-    , withNewArrayUnsafe--    -- *** From streams-    , ArrayUnsafe (..)-    , writeNWithUnsafe-    , writeNWith-    , writeNUnsafe-    , writeN-    , writeNAligned--    , writeWith-    , write--    , writeRevN-    -- , writeRev--    -- ** From containers-    , fromListN-    , fromList-    , fromListRevN-    , fromListRev-    , fromStreamDN-    , fromStreamD--    -- * Random writes-    , putIndex-    , putIndexUnsafe-    , putIndices-    -- , putFromThenTo-    -- , putFrom -- start writing at the given position-    -- , putUpto -- write from beginning up to the given position-    -- , putFromTo-    -- , putFromRev-    -- , putUptoRev-    , modifyIndexUnsafe-    , modifyIndex-    , modifyIndices-    , modify-    , swapIndices-    , unsafeSwapIndices--    -- * Growing and Shrinking-    -- Arrays grow only at the end, though it is possible to grow on both sides-    -- and therefore have a cons as well as snoc. But that will require two-    -- bounds in the array representation.--    -- ** Appending elements-    , snocWith-    , snoc-    , snocLinear-    , snocMay-    , snocUnsafe--    -- ** Appending streams-    , writeAppendNUnsafe-    , writeAppendN-    , writeAppendWith-    , writeAppend--    -- * Eliminating and Reading--    -- ** To streams-    , reader-    , readerRevWith-    , readerRev--    -- ** To containers-    , toStreamDWith-    , toStreamDRevWith-    , toStreamKWith-    , toStreamKRevWith-    , toStreamD-    , toStreamDRev-    , toStreamK-    , toStreamKRev-    , toList--    -- experimental-    , producerWith-    , producer--    -- ** Random reads-    , getIndex-    , getIndexUnsafe-    , getIndices-    , getIndicesD-    -- , getFromThenTo-    , getIndexRev--    -- * Memory Management-    , blockSize-    , arrayChunkBytes-    , allocBytesToElemCount-    , realloc-    , resize-    , resizeExp-    , rightSize--    -- * Size-    , length-    , byteLength-    -- , capacity-    , byteCapacity-    , bytesFree--    -- * In-place Mutation Algorithms-    , strip-    , reverse-    , permute-    , partitionBy-    , shuffleBy-    , divideBy-    , mergeBy-    , bubble--    -- * Casting-    , cast-    , castUnsafe-    , asBytes-    , asPtrUnsafe--    -- * Folding-    , foldl'-    , foldr-    , cmp--    -- * Arrays of arrays-    --  We can add dimensionality parameter to the array type to get-    --  multidimensional arrays. Multidimensional arrays would just be a-    --  convenience wrapper on top of single dimensional arrays.--    -- | Operations dealing with multiple arrays, streams of arrays or-    -- multidimensional array representations.--    -- ** Construct from streams-    , chunksOf-    , arrayStreamKFromStreamD-    , writeChunks--    -- ** Eliminate to streams-    , flattenArrays-    , flattenArraysRev-    , fromArrayStreamK--    -- ** Construct from arrays-    -- get chunks without copying-    , getSliceUnsafe-    , getSlice-    -- , getSlicesFromLenN-    , splitAt -- XXX should be able to express using getSlice-    , breakOn--    -- ** Appending arrays-    , spliceCopy-    , spliceWith-    , splice-    , spliceExp-    , spliceUnsafe-    , putSliceUnsafe-    -- , putSlice-    -- , appendSlice-    -- , appendSliceFrom--    -- * Utilities-    , roundUpToPower2-    , memcpy-    , memcmp-    , c_memchr-    )-where--#include "assert.hs"-#include "inline.hs"-#include "ArrayMacros.h"-#include "MachDeps.h"--import Control.Monad (when, void)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Bits (shiftR, (.|.), (.&.))-import Data.Proxy (Proxy(..))-import Data.Word (Word8)-import Foreign.C.Types (CSize(..), CInt(..))-import Foreign.Ptr (plusPtr, minusPtr, nullPtr)-import Streamly.Internal.Data.Unboxed-    ( MutableByteArray(..)-    , Unbox-    , getMutableByteArray#-    , peekWith-    , pokeWith-    , sizeOf-    , touch-    )-import GHC.Base-    ( IO(..)-    , Int(..)-    , byteArrayContents#-    , compareByteArrays#-    , copyMutableByteArray#-    )-import GHC.Base (noinline)-import GHC.Exts (unsafeCoerce#)-import GHC.Ptr (Ptr(..))--import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Producer.Type (Producer (..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Stream.StreamK.Type (StreamK)-import Streamly.Internal.Data.SVar.Type (adaptState, defState)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))-import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)--import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.Producer as Producer-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Unboxed as Unboxed-import qualified Prelude--import Prelude hiding-    (length, foldr, read, unlines, splitAt, reverse, truncate)--#include "DocTestDataMutArray.hs"------------------------------------------------------------------------------------ Foreign helpers----------------------------------------------------------------------------------foreign import ccall unsafe "string.h memcpy" c_memcpy-    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)--foreign import ccall unsafe "string.h memchr" c_memchr-    :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)--foreign import ccall unsafe "string.h memcmp" c_memcmp-    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt---- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return--- how many elements of that type will completely fit in those bytes.----{-# INLINE bytesToElemCount #-}-bytesToElemCount :: forall a. Unbox a => a -> Int -> Int-bytesToElemCount _ n = n `div` SIZE_OF(a)---- XXX we are converting Int to CSize-memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()-memcpy dst src len = void (c_memcpy dst src (fromIntegral len))---- XXX we are converting Int to CSize--- return True if the memory locations have identical contents-{-# INLINE memcmp #-}-memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool-memcmp p1 p2 len = do-    r <- c_memcmp p1 p2 (fromIntegral len)-    return $ r == 0------------------------------------------------------------------------------------ MutArray Data Type------------------------------------------------------------------------------------ $arrayNotes------ We can use an 'Unboxed' constraint in the MutArray type and the constraint--- can be automatically provided to a function that pattern matches on the--- MutArray type. However, it has huge performance cost, so we do not use it.--- Investigate a GHC improvement possiblity.---- | An unboxed mutable array. An array is created with a given length--- and capacity. Length is the number of valid elements in the array.  Capacity--- is the maximum number of elements that the array can be expanded to without--- having to reallocate the memory.------ The elements in the array can be mutated in-place without changing the--- reference (constructor). However, the length of the array cannot be mutated--- in-place.  A new array reference is generated when the length changes.  When--- the length is increased (upto the maximum reserved capacity of the array),--- the array is not reallocated and the new reference uses the same underlying--- memory as the old one.------ Several routines in this module allow the programmer to control the capacity--- of the array. The programmer can control the trade-off between memory usage--- and performance impact due to reallocations when growing or shrinking the--- array.----data MutArray a =-#ifdef DEVBUILD-    Unbox a =>-#endif-    -- The array is a range into arrContents. arrContents may be a superset of-    -- the slice represented by the array. All offsets are in bytes.-    MutArray-    { arrContents :: {-# UNPACK #-} !MutableByteArray-    , arrStart :: {-# UNPACK #-} !Int  -- ^ index into arrContents-    , arrEnd   :: {-# UNPACK #-} !Int    -- ^ index into arrContents-                                       -- Represents the first invalid index of-                                       -- the array.-    , arrBound :: {-# UNPACK #-} !Int    -- ^ first invalid index of arrContents.-    }------------------------------------------------------------------------------------ Pinning & Unpinning----------------------------------------------------------------------------------{-# INLINE pin #-}-pin :: MutArray a -> IO (MutArray a)-pin arr@MutArray{..} = do-    contents <- Unboxed.pin arrContents-    return $ arr {arrContents = contents}--{-# INLINE unpin #-}-unpin :: MutArray a -> IO (MutArray a)-unpin arr@MutArray{..} = do-    contents <- Unboxed.unpin arrContents-    return $ arr {arrContents = contents}------------------------------------------------------------------------------------ Construction------------------------------------------------------------------------------------ XXX Change the names to use "new" instead of "newArray". That way we can use--- the same names for managed file system objects as well. For unmanaged ones--- we can use open/create etc as usual.------ A new array is similar to "touch" creating a zero length file. An mmapped--- array would be similar to a sparse file with holes. TBD: support mmapped--- files and arrays.---- GHC always guarantees word-aligned memory, alignment is important only when--- we need more than that.  See stg_newAlignedPinnedByteArrayzh and--- allocatePinned in GHC source.---- | @newArrayWith allocator alignment count@ allocates a new array of zero--- length and with a capacity to hold @count@ elements, using @allocator--- size alignment@ as the memory allocator function.------ Alignment must be greater than or equal to machine word size and a power of--- 2.------ Alignment is ignored if the allocator allocates unpinned memory.------ /Pre-release/-{-# INLINE newArrayWith #-}-newArrayWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> Int -> m MutableByteArray) -> Int -> Int -> m (MutArray a)-newArrayWith alloc alignSize count = do-    let size = max (count * SIZE_OF(a)) 0-    contents <- alloc size alignSize-    return $ MutArray-        { arrContents = contents-        , arrStart = 0-        , arrEnd   = 0-        , arrBound = size-        }--nil ::-#ifdef DEVBUILD-    Unbox a =>-#endif-    MutArray a-nil = MutArray Unboxed.nil 0 0 0----- | Allocates a pinned empty array that can hold 'count' items.  The memory of--- the array is uninitialized and the allocation is aligned as per the--- 'Unboxed' instance of the type.------ /Pre-release/-{-# INLINE newPinnedBytes #-}-newPinnedBytes :: MonadIO m =>-#ifdef DEVBUILD-    Unbox a =>-#endif-    Int -> m (MutArray a)-newPinnedBytes bytes = do-    contents <- liftIO $ Unboxed.newPinnedBytes bytes-    return $ MutArray-        { arrContents = contents-        , arrStart = 0-        , arrEnd   = 0-        , arrBound = bytes-        }---- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and--- the alignment is dictated by the 'Unboxed' instance of the type.------ /Internal/-{-# INLINE newAlignedPinned #-}-newAlignedPinned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)-newAlignedPinned =-    newArrayWith (\s a -> liftIO $ Unboxed.newAlignedPinnedBytes s a)---- XXX can unaligned allocation be more efficient when alignment is not needed?------ | Allocates an empty pinned array that can hold 'count' items.  The memory of--- the array is uninitialized and the allocation is aligned as per the 'Unboxed'--- instance of the type.----{-# INLINE newPinned #-}-newPinned :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)-newPinned =-    newArrayWith-        (\s _ -> liftIO $ Unboxed.newPinnedBytes s)-        (error "newPinned: alignSize is not used")---- | Allocates an empty unpinned array that can hold 'count' items.  The memory--- of the array is uninitialized.----{-# INLINE new #-}-new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)-new =-    newArrayWith-        (\s _ -> liftIO $ Unboxed.newUnpinnedBytes s)-        (error "new: alignment is not used in unpinned arrays.")---- XXX This should create a full length uninitialzed array so that the pointer--- can be used.---- | Allocate a pinned MutArray of the given size and run an IO action passing--- the array start pointer.------ /Internal/-{-# INLINE withNewArrayUnsafe #-}-withNewArrayUnsafe ::-       (MonadIO m, Unbox a) => Int -> (Ptr a -> m ()) -> m (MutArray a)-withNewArrayUnsafe count f = do-    arr <- newPinned count-    asPtrUnsafe arr-        $ \p -> f p >> return arr------------------------------------------------------------------------------------ Random writes------------------------------------------------------------------------------------ | Write the given element to the given index of the array. Does not check if--- the index is out of bounds of the array.------ /Pre-release/-{-# INLINE putIndexUnsafe #-}-putIndexUnsafe :: forall m a. (MonadIO m, Unbox a)-    => Int -> MutArray a -> a -> m ()-putIndexUnsafe i MutArray{..} x = do-    let index = INDEX_OF(arrStart, i, a)-    assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())-    liftIO $ pokeWith arrContents index x--invalidIndex :: String -> Int -> a-invalidIndex label i =-    error $ label ++ ": invalid array index " ++ show i---- | /O(1)/ Write the given element at the given index in the array.--- Performs in-place mutation of the array.------ >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))--- >>> f = MutArray.putIndices--- >>> putIndex ix arr val = Stream.fold (f arr) (Stream.fromPure (ix, val))----{-# INLINE putIndex #-}-putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()-putIndex i MutArray{..} x = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ pokeWith arrContents index x-    else invalidIndex "putIndex" i---- | Write an input stream of (index, value) pairs to an array. Throws an--- error if any index is out of bounds.------ /Pre-release/-{-# INLINE putIndices #-}-putIndices :: forall m a. (MonadIO m, Unbox a)-    => MutArray a -> Fold m (Int, a) ()-putIndices arr = FL.foldlM' step (return ())--    where--    step () (i, x) = liftIO (putIndex i arr x)---- | Modify a given index of an array using a modifier function.------ /Pre-release/-modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndexUnsafe i MutArray{..} f = liftIO $ do-        let index = INDEX_OF(arrStart,i,a)-        assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())-        r <- peekWith arrContents index-        let (x, res) = f r-        pokeWith arrContents index x-        return res---- | Modify a given index of an array using a modifier function.------ /Pre-release/-modifyIndex :: forall m a b. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> (a -> (a, b)) -> m b-modifyIndex i MutArray{..} f = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ do-        r <- peekWith arrContents index-        let (x, res) = f r-        pokeWith arrContents index x-        return res-    else invalidIndex "modifyIndex" i----- | Modify the array indices generated by the supplied stream.------ /Pre-release/-{-# INLINE modifyIndices #-}-modifyIndices :: forall m a . (MonadIO m, Unbox a)-    => MutArray a -> (Int -> a -> a) -> Fold m Int ()-modifyIndices arr f = FL.foldlM' step initial--    where--    initial = return ()--    step () i =-        let f1 x = (f i x, ())-         in modifyIndex i arr f1---- | Modify each element of an array using the supplied modifier function.------ /Pre-release/-modify :: forall m a. (MonadIO m, Unbox a)-    => MutArray a -> (a -> a) -> m ()-modify MutArray{..} f = liftIO $-    go arrStart--    where--    go i =-        when (INDEX_VALID(i,arrEnd,a)) $ do-            r <- peekWith arrContents i-            pokeWith arrContents i (f r)-            go (INDEX_NEXT(i,a))---- XXX We could specify the number of bytes to swap instead of Proxy. Need--- to ensure that the memory does not overlap.-{-# INLINE swapArrayByteIndices #-}-swapArrayByteIndices ::-       forall a. Unbox a-    => Proxy a-    -> MutableByteArray-    -> Int-    -> Int-    -> IO ()-swapArrayByteIndices _ arrContents i1 i2 = do-    r1 <- peekWith arrContents i1-    r2 <- peekWith arrContents i2-    pokeWith arrContents i1 (r2 :: a)-    pokeWith arrContents i2 (r1 :: a)---- | Swap the elements at two indices without validating the indices.------ /Unsafe/: This could result in memory corruption if indices are not valid.------ /Pre-release/-{-# INLINE unsafeSwapIndices #-}-unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> MutArray a -> m ()-unsafeSwapIndices i1 i2 MutArray{..} = liftIO $ do-        let t1 = INDEX_OF(arrStart,i1,a)-            t2 = INDEX_OF(arrStart,i2,a)-        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2---- | Swap the elements at two indices.------ /Pre-release/-swapIndices :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> MutArray a -> m ()-swapIndices i1 i2 MutArray{..} = liftIO $ do-        let t1 = INDEX_OF(arrStart,i1,a)-            t2 = INDEX_OF(arrStart,i2,a)-        when (i1 < 0 || INDEX_INVALID(t1,arrEnd,a))-            $ invalidIndex "swapIndices" i1-        when (i2 < 0 || INDEX_INVALID(t2,arrEnd,a))-            $ invalidIndex "swapIndices" i2-        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2------------------------------------------------------------------------------------ Rounding------------------------------------------------------------------------------------ XXX Should we use bitshifts in calculations or it gets optimized by the--- compiler/processor itself?------ | The page or block size used by the GHC allocator. Allocator allocates at--- least a block and then allocates smaller allocations from within a block.-blockSize :: Int-blockSize = 4 * 1024---- | Allocations larger than 'largeObjectThreshold' are in multiples of block--- size and are always pinned. The space beyond the end of a large object up to--- the end of the block is unused.-largeObjectThreshold :: Int-largeObjectThreshold = (blockSize * 8) `div` 10---- XXX Should be done only when we are using the GHC allocator.--- | Round up an array larger than 'largeObjectThreshold' to use the whole--- block.-{-# INLINE roundUpLargeArray #-}-roundUpLargeArray :: Int -> Int-roundUpLargeArray size =-    if size >= largeObjectThreshold-    then-        assert-            (blockSize /= 0 && ((blockSize .&. (blockSize - 1)) == 0))-            ((size + blockSize - 1) .&. negate blockSize)-    else size--{-# INLINE isPower2 #-}-isPower2 :: Int -> Bool-isPower2 n = n .&. (n - 1) == 0--{-# INLINE roundUpToPower2 #-}-roundUpToPower2 :: Int -> Int-roundUpToPower2 n =-#if WORD_SIZE_IN_BITS == 64-    1 + z6-#else-    1 + z5-#endif--    where--    z0 = n - 1-    z1 = z0 .|. z0 `shiftR` 1-    z2 = z1 .|. z1 `shiftR` 2-    z3 = z2 .|. z2 `shiftR` 4-    z4 = z3 .|. z3 `shiftR` 8-    z5 = z4 .|. z4 `shiftR` 16-    z6 = z5 .|. z5 `shiftR` 32---- | @allocBytesToBytes elem allocatedBytes@ returns the array size in bytes--- such that the real allocation is less than or equal to @allocatedBytes@,--- unless @allocatedBytes@ is less than the size of one array element in which--- case it returns one element's size.----{-# INLINE allocBytesToBytes #-}-allocBytesToBytes :: forall a. Unbox a => a -> Int -> Int-allocBytesToBytes _ n = max (arrayPayloadSize n) (SIZE_OF(a))---- | Given an 'Unboxed' type (unused first arg) and real allocation size--- (including overhead), return how many elements of that type will completely--- fit in it, returns at least 1.----{-# INLINE allocBytesToElemCount #-}-allocBytesToElemCount :: Unbox a => a -> Int -> Int-allocBytesToElemCount x bytes =-    let n = bytesToElemCount x (allocBytesToBytes x bytes)-     in assert (n >= 1) n---- | The default chunk size by which the array creation routines increase the--- size of the array when the array is grown linearly.-arrayChunkBytes :: Int-arrayChunkBytes = 1024------------------------------------------------------------------------------------ Resizing------------------------------------------------------------------------------------ | Round the second argument down to multiples of the first argument.-{-# INLINE roundDownTo #-}-roundDownTo :: Int -> Int -> Int-roundDownTo elemSize size = size - (size `mod` elemSize)---- XXX See if resizing can be implemented by reading the old array as a stream--- and then using writeN to the new array.------ NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.--- Since this is not inlined Unboxed consrraint leads to dictionary passing--- which complicates some inspection tests.----{-# NOINLINE reallocExplicit #-}-reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)-reallocExplicit elemSize newCapacityInBytes MutArray{..} = do-    assertM(arrEnd <= arrBound)--    -- Allocate new array-    let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes-    contents <- Unboxed.newPinnedBytes newCapMaxInBytes-    let !(MutableByteArray mbarrFrom#) = arrContents-        !(MutableByteArray mbarrTo#) = contents--    -- Copy old data-    let oldStart = arrStart-        !(I# oldStartInBytes#) = oldStart-        oldSizeInBytes = arrEnd - oldStart-        newCapInBytes = roundDownTo elemSize newCapMaxInBytes-        !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes-    assert (oldSizeInBytes `mod` elemSize == 0) (return ())-    assert (newLenInBytes >= 0) (return ())-    assert (newLenInBytes `mod` elemSize == 0) (return ())-    IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#-                        mbarrTo# 0# newLenInBytes# s#, () #)--    return $ MutArray-        { arrStart = 0-        , arrContents = contents-        , arrEnd   = newLenInBytes-        , arrBound = newCapInBytes-        }---- | @realloc newCapacity array@ reallocates the array to the specified--- capacity in bytes.------ If the new size is less than the original array the array gets truncated.--- If the new size is not a multiple of array element size then it is rounded--- down to multiples of array size.  If the new size is more than--- 'largeObjectThreshold' then it is rounded up to the block size (4K).----{-# INLINABLE realloc #-}-realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)-realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr---- | @reallocWith label capSizer minIncrBytes array@. The label is used--- in error messages and the capSizer is used to determine the capacity of the--- new array in bytes given the current byte length of the array.-reallocWith :: forall m a. (MonadIO m , Unbox a) =>-       String-    -> (Int -> Int)-    -> Int-    -> MutArray a-    -> m (MutArray a)-reallocWith label capSizer minIncrBytes arr = do-    let oldSizeBytes = arrEnd arr - arrStart arr-        newCapBytes = capSizer oldSizeBytes-        newSizeBytes = oldSizeBytes + minIncrBytes-        safeCapBytes = max newCapBytes newSizeBytes-    assertM(safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))--    realloc safeCapBytes arr--    where--    badSize newSize =-        concat-            [ label-            , ": new array size (in bytes) is less than required size "-            , show newSize-            , ". Please check the sizing function passed."-            ]---- | @resize newCapacity array@ changes the total capacity of the array so that--- it is enough to hold the specified number of elements.  Nothing is done if--- the specified capacity is less than the length of the array.------ If the capacity is more than 'largeObjectThreshold' then it is rounded up to--- the block size (4K).------ /Pre-release/-{-# INLINE resize #-}-resize :: forall m a. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> m (MutArray a)-resize nElems arr@MutArray{..} = do-    let req = SIZE_OF(a) * nElems-        len = arrEnd - arrStart-    if req < len-    then return arr-    else realloc req arr---- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'--- then it is rounded up to the closest power of 2.------ /Pre-release/-{-# INLINE resizeExp #-}-resizeExp :: forall m a. (MonadIO m, Unbox a) =>-    Int -> MutArray a -> m (MutArray a)-resizeExp nElems arr@MutArray{..} = do-    let req = roundUpLargeArray (SIZE_OF(a) * nElems)-        req1 =-            if req > largeObjectThreshold-            then roundUpToPower2 req-            else req-        len = arrEnd - arrStart-    if req1 < len-    then return arr-    else realloc req1 arr---- | Resize the allocated memory to drop any reserved free space at the end of--- the array and reallocate it to reduce wastage.------ Up to 25% wastage is allowed to avoid reallocations.  If the capacity is--- more than 'largeObjectThreshold' then free space up to the 'blockSize' is--- retained.------ /Pre-release/-{-# INLINE rightSize #-}-rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)-rightSize arr@MutArray{..} = do-    assert (arrEnd <= arrBound) (return ())-    let start = arrStart-        len = arrEnd - start-        capacity = arrBound - start-        target = roundUpLargeArray len-        waste = arrBound - arrEnd-    assert (target >= len) (return ())-    assert (len `mod` SIZE_OF(a) == 0) (return ())-    -- We trade off some wastage (25%) to avoid reallocations and copying.-    if target < capacity && len < 3 * waste-    then realloc target arr-    else return arr------------------------------------------------------------------------------------ Snoc------------------------------------------------------------------------------------ XXX We can possibly use a smallMutableByteArray to hold the start, end,--- bound pointers.  Using fully mutable handle will ensure that we do not have--- multiple references to the same array of different lengths lying around and--- potentially misused. In that case "snoc" need not return a new array (snoc--- :: MutArray a -> a -> m ()), it will just modify the old reference.  The array--- length will be mutable.  This means the length function would also be--- monadic.  Mutable arrays would behave more like files that grow in that--- case.---- | Snoc using a 'Ptr'. Low level reusable function.------ /Internal/-{-# INLINE snocNewEnd #-}-snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)-snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do-    assert (newEnd <= arrBound) (return ())-    pokeWith arrContents arrEnd x-    return $ arr {arrEnd = newEnd}---- | Really really unsafe, appends the element into the first array, may--- cause silent data corruption or if you are lucky a segfault if the first--- array does not have enough space to append the element.------ /Internal/-{-# INLINE snocUnsafe #-}-snocUnsafe :: forall m a. (MonadIO m, Unbox a) =>-    MutArray a -> a -> m (MutArray a)-snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr---- | Like 'snoc' but does not reallocate when pre-allocated array capacity--- becomes full.------ /Internal/-{-# INLINE snocMay #-}-snocMay :: forall m a. (MonadIO m, Unbox a) =>-    MutArray a -> a -> m (Maybe (MutArray a))-snocMay arr@MutArray{..} x = liftIO $ do-    let newEnd = INDEX_NEXT(arrEnd,a)-    if newEnd <= arrBound-    then Just <$> snocNewEnd newEnd arr x-    else return Nothing---- NOINLINE to move it out of the way and not pollute the instruction cache.-{-# NOINLINE snocWithRealloc #-}-snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>-       (Int -> Int)-    -> MutArray a-    -> a-    -> m (MutArray a)-snocWithRealloc sizer arr x = do-    arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr-    snocUnsafe arr1 x---- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of--- the array increases by 1.------ If there is no reserved space available in @arr@ it is reallocated to a size--- in bytes determined by the @sizer oldSizeBytes@ function, where--- @oldSizeBytes@ is the original size of the array in bytes.------ If the new array size is more than 'largeObjectThreshold' we automatically--- round it up to 'blockSize'.------ Note that the returned array may be a mutated version of the original array.------ /Pre-release/-{-# INLINE snocWith #-}-snocWith :: forall m a. (MonadIO m, Unbox a) =>-       (Int -> Int)-    -> MutArray a-    -> a-    -> m (MutArray a)-snocWith allocSize arr x = liftIO $ do-    let newEnd = INDEX_NEXT(arrEnd arr,a)-    if newEnd <= arrBound arr-    then snocNewEnd newEnd arr x-    else snocWithRealloc allocSize arr x---- | The array is mutated to append an additional element to it. If there--- is no reserved space available in the array then it is reallocated to grow--- it by 'arrayChunkBytes' rounded up to 'blockSize' when the size becomes more--- than 'largeObjectThreshold'.------ Note that the returned array may be a mutated version of the original array.------ Performs O(n^2) copies to grow but is thrifty on memory.------ /Pre-release/-{-# INLINE snocLinear #-}-snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)-snocLinear = snocWith (+ allocBytesToBytes (undefined :: a) arrayChunkBytes)---- | The array is mutated to append an additional element to it. If there is no--- reserved space available in the array then it is reallocated to double the--- original size.------ This is useful to reduce allocations when appending unknown number of--- elements.------ Note that the returned array may be a mutated version of the original array.------ >>> snoc = MutArray.snocWith (* 2)------ Performs O(n * log n) copies to grow, but is liberal with memory allocation.----{-# INLINE snoc #-}-snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)-snoc = snocWith f--    where--    f oldSize =-        if isPower2 oldSize-        then oldSize * 2-        else roundUpToPower2 oldSize * 2------------------------------------------------------------------------------------ Random reads------------------------------------------------------------------------------------ XXX Can this be deduplicated with array/foreign---- | Return the element at the specified index without checking the bounds.------ Unsafe because it does not check the bounds of the array.-{-# INLINE_NORMAL getIndexUnsafe #-}-getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a-getIndexUnsafe i MutArray{..} = do-    let index = INDEX_OF(arrStart,i,a)-    assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())-    liftIO $ peekWith arrContents index---- | /O(1)/ Lookup the element at the given index. Index starts from 0.----{-# INLINE getIndex #-}-getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a-getIndex i MutArray{..} = do-    let index = INDEX_OF(arrStart,i,a)-    if i >= 0 && INDEX_VALID(index,arrEnd,a)-    then liftIO $ peekWith arrContents index-    else invalidIndex "getIndex" i---- | /O(1)/ Lookup the element at the given index from the end of the array.--- Index starts from 0.------ Slightly faster than computing the forward index and using getIndex.----{-# INLINE getIndexRev #-}-getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a-getIndexRev i MutArray{..} = do-    let index = RINDEX_OF(arrEnd,i,a)-    if i >= 0 && index >= arrStart-    then liftIO $ peekWith arrContents index-    else invalidIndex "getIndexRev" i--data GetIndicesState contents start end st =-    GetIndicesState contents start end st---- | Given an unfold that generates array indices, read the elements on those--- indices from the supplied MutArray. An error is thrown if an index is out of--- bounds.------ /Pre-release/-{-# INLINE getIndicesD #-}-getIndicesD :: (Monad m, Unbox a) =>-    (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a-getIndicesD liftio (D.Stream stepi sti) = Unfold step inject--    where--    inject (MutArray contents start end _) =-        return $ GetIndicesState contents start end sti--    {-# INLINE_LATE step #-}-    step (GetIndicesState contents start end st) = do-        r <- stepi defState st-        case r of-            D.Yield i s -> do-                x <- liftio $ getIndex i (MutArray contents start end undefined)-                return $ D.Yield x (GetIndicesState contents start end s)-            D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)-            D.Stop -> return D.Stop--{-# INLINE getIndices #-}-getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a-getIndices = getIndicesD liftIO------------------------------------------------------------------------------------ Subarrays------------------------------------------------------------------------------------ XXX We can also get immutable slices.---- | /O(1)/ Slice an array in constant time.------ Unsafe: The bounds of the slice are not checked.------ /Unsafe/------ /Pre-release/-{-# INLINE getSliceUnsafe #-}-getSliceUnsafe :: forall a. Unbox a-    => Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSliceUnsafe index len (MutArray contents start e _) =-    let fp1 = INDEX_OF(start,index,a)-        end = fp1 + (len * SIZE_OF(a))-     in assert-            (index >= 0 && len >= 0 && end <= e)-            -- Note: In a slice we always use bound = end so that the slice-            -- user cannot overwrite elements beyond the end of the slice.-            (MutArray contents fp1 end end)---- | /O(1)/ Slice an array in constant time. Throws an error if the slice--- extends out of the array bounds.------ /Pre-release/-{-# INLINE getSlice #-}-getSlice :: forall a. Unbox a =>-       Int -- ^ from index-    -> Int -- ^ length of the slice-    -> MutArray a-    -> MutArray a-getSlice index len (MutArray contents start e _) =-    let fp1 = INDEX_OF(start,index,a)-        end = fp1 + (len * SIZE_OF(a))-     in if index >= 0 && len >= 0 && end <= e-        -- Note: In a slice we always use bound = end so that the slice user-        -- cannot overwrite elements beyond the end of the slice.-        then MutArray contents fp1 end end-        else error-                $ "getSlice: invalid slice, index "-                ++ show index ++ " length " ++ show len------------------------------------------------------------------------------------ In-place mutation algorithms------------------------------------------------------------------------------------ XXX consider the bulk update/accumulation/permutation APIs from vector.---- | You may not need to reverse an array because you can consume it in reverse--- using 'readerRev'. To reverse large arrays you can read in reverse and write--- to another array. However, in-place reverse can be useful to take adavantage--- of cache locality and when you do not want to allocate additional memory.----{-# INLINE reverse #-}-reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()-reverse MutArray{..} = liftIO $ do-    let l = arrStart-        h = INDEX_PREV(arrEnd,a)-     in swap l h--    where--    swap l h = do-        when (l < h) $ do-            swapArrayByteIndices (Proxy :: Proxy a) arrContents l h-            swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))---- | Generate the next permutation of the sequence, returns False if this is--- the last permutation.------ /Unimplemented/-{-# INLINE permute #-}-permute :: MutArray a -> m Bool-permute = undefined---- | Partition an array into two halves using a partitioning predicate. The--- first half retains values where the predicate is 'False' and the second half--- retains values where the predicate is 'True'.------ /Pre-release/-{-# INLINE partitionBy #-}-partitionBy :: forall m a. (MonadIO m, Unbox a)-    => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)-partitionBy f arr@MutArray{..} = liftIO $ do-    if arrStart >= arrEnd-    then return (arr, arr)-    else do-        ptr <- go arrStart (INDEX_PREV(arrEnd,a))-        let pl = MutArray arrContents arrStart ptr ptr-            pr = MutArray arrContents ptr arrEnd arrEnd-        return (pl, pr)--    where--    -- Invariant low < high on entry, and on return as well-    moveHigh low high = do-        h <- peekWith arrContents high-        if f h-        then-            -- Correctly classified, continue the loop-            let high1 = INDEX_PREV(high,a)-             in if low == high1-                then return Nothing-                else moveHigh low high1-        else return (Just (high, h)) -- incorrectly classified--    -- Keep a low pointer starting at the start of the array (first partition)-    -- and a high pointer starting at the end of the array (second partition).-    -- Keep incrementing the low ptr and decrementing the high ptr until both-    -- are wrongly classified, at that point swap the two and continue until-    -- the two pointer cross each other.-    ---    -- Invariants when entering this loop:-    -- low <= high-    -- Both low and high are valid locations within the array-    go low high = do-        l <- peekWith arrContents low-        if f l-        then-            -- low is wrongly classified-            if low == high-            then return low-            else do -- low < high-                r <- moveHigh low high-                case r of-                    Nothing -> return low-                    Just (high1, h) -> do -- low < high1-                        pokeWith arrContents low h-                        pokeWith arrContents high1 l-                        let low1 = INDEX_NEXT(low,a)-                            high2 = INDEX_PREV(high1,a)-                        if low1 <= high2-                        then go low1 high2-                        else return low1 -- low1 > high2--        else do-            -- low is correctly classified-            let low1 = INDEX_NEXT(low,a)-            if low == high-            then return low1-            else go low1 high---- | Shuffle corresponding elements from two arrays using a shuffle function.--- If the shuffle function returns 'False' then do nothing otherwise swap the--- elements. This can be used in a bottom up fold to shuffle or reorder the--- elements.------ /Unimplemented/-{-# INLINE shuffleBy #-}-shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()-shuffleBy = undefined---- XXX we can also make the folds partial by stopping at a certain level.------ | @divideBy level partition array@  performs a top down hierarchical--- recursive partitioning fold of items in the container using the given--- function as the partition function.  Level indicates the level in the tree--- where the fold would stop.------ This performs a quick sort if the partition function is--- 'partitionBy (< pivot)'.------ /Unimplemented/-{-# INLINABLE divideBy #-}-divideBy ::-    Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()-divideBy = undefined---- | @mergeBy level merge array@ performs a pairwise bottom up fold recursively--- merging the pairs using the supplied merge function. Level indicates the--- level in the tree where the fold would stop.------ This performs a random shuffle if the merge function is random.  If we--- stop at level 0 and repeatedly apply the function then we can do a bubble--- sort.------ /Unimplemented/-mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()-mergeBy = undefined------------------------------------------------------------------------------------ Size------------------------------------------------------------------------------------ | /O(1)/ Get the byte length of the array.----{-# INLINE byteLength #-}-byteLength :: MutArray a -> Int-byteLength MutArray{..} =-    let len = arrEnd - arrStart-    in assert (len >= 0) len---- Note: try to avoid the use of length in performance sensitive internal--- routines as it involves a costly 'div' operation. Instead use the end ptr--- in the array to check the bounds etc.------ | /O(1)/ Get the length of the array i.e. the number of elements in the--- array.------ Note that 'byteLength' is less expensive than this operation, as 'length'--- involves a costly division operation.----{-# INLINE length #-}-length :: forall a. Unbox a => MutArray a -> Int-length arr =-    let elemSize = SIZE_OF(a)-        blen = byteLength arr-     in assert (blen `mod` elemSize == 0) (blen `div` elemSize)---- | Get the total capacity of an array. An array may have space reserved--- beyond the current used length of the array.------ /Pre-release/-{-# INLINE byteCapacity #-}-byteCapacity :: MutArray a -> Int-byteCapacity MutArray{..} =-    let len = arrBound - arrStart-    in assert (len >= 0) len---- | The remaining capacity in the array for appending more elements without--- reallocation.------ /Pre-release/-{-# INLINE bytesFree #-}-bytesFree :: MutArray a -> Int-bytesFree MutArray{..} =-    let n = arrBound - arrEnd-    in assert (n >= 0) n------------------------------------------------------------------------------------ Streams of arrays - Creation----------------------------------------------------------------------------------data GroupState s contents start end bound-    = GroupStart s-    | GroupBuffer s contents start end bound-    | GroupYield-        contents start end bound (GroupState s contents start end bound)-    | GroupFinish---- | @chunksOf n stream@ groups the input stream into a stream of--- arrays of size n.------ @chunksOf n = StreamD.foldMany (MutArray.writeN n)@------ /Pre-release/-{-# INLINE_NORMAL chunksOf #-}-chunksOf :: forall m a. (MonadIO m, Unbox a)-    => Int -> D.Stream m a -> D.Stream m (MutArray a)--- XXX the idiomatic implementation leads to large regression in the D.reverse'--- benchmark. It seems it has difficulty producing optimized code when--- converting to StreamK. Investigate GHC optimizations.--- chunksOf n = D.foldMany (writeN n)-chunksOf n (D.Stream step state) =-    D.Stream step' (GroupStart state)--    where--    {-# INLINE_LATE step' #-}-    step' _ (GroupStart st) = do-        when (n <= 0) $-            -- XXX we can pass the module string from the higher level API-            error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "-                    ++ "the size of arrays [" ++ show n-                    ++ "] must be a natural number"-        (MutArray contents start end bound :: MutArray a) <- liftIO $ newPinned n-        return $ D.Skip (GroupBuffer st contents start end bound)--    step' gst (GroupBuffer st contents start end bound) = do-        r <- step (adaptState gst) st-        case r of-            D.Yield x s -> do-                liftIO $ pokeWith contents end x-                let end1 = INDEX_NEXT(end,a)-                return $-                    if end1 >= bound-                    then D.Skip-                            (GroupYield-                                contents start end1 bound (GroupStart s))-                    else D.Skip (GroupBuffer s contents start end1 bound)-            D.Skip s ->-                return $ D.Skip (GroupBuffer s contents start end bound)-            D.Stop ->-                return-                    $ D.Skip (GroupYield contents start end bound GroupFinish)--    step' _ (GroupYield contents start end bound next) =-        return $ D.Yield (MutArray contents start end bound) next--    step' _ GroupFinish = return D.Stop---- XXX buffer to a list instead?--- | Buffer the stream into arrays in memory.-{-# INLINE arrayStreamKFromStreamD #-}-arrayStreamKFromStreamD :: forall m a. (MonadIO m, Unbox a) =>-    D.Stream m a -> m (StreamK m (MutArray a))-arrayStreamKFromStreamD =-    let n = allocBytesToElemCount (undefined :: a) defaultChunkSize-     in D.foldr K.cons K.nil . chunksOf n------------------------------------------------------------------------------------ Streams of arrays - Flattening----------------------------------------------------------------------------------data FlattenState s contents a =-      OuterLoop s-    | InnerLoop s contents !Int !Int---- | Use the "reader" unfold instead.------ @flattenArrays = unfoldMany reader@------ We can try this if there are any fusion issues in the unfold.----{-# INLINE_NORMAL flattenArrays #-}-flattenArrays :: forall m a. (MonadIO m, Unbox a)-    => D.Stream m (MutArray a) -> D.Stream m a-flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield MutArray{..} s ->-                D.Skip (InnerLoop s arrContents arrStart arrEnd)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p end) | assert (p <= end) (p == end) =-        return $ D.Skip $ OuterLoop st--    step' _ (InnerLoop st contents p end) = do-        x <- liftIO $ peekWith contents p-        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)---- | Use the "readerRev" unfold instead.------ @flattenArrays = unfoldMany readerRev@------ We can try this if there are any fusion issues in the unfold.----{-# INLINE_NORMAL flattenArraysRev #-}-flattenArraysRev :: forall m a. (MonadIO m, Unbox a)-    => D.Stream m (MutArray a) -> D.Stream m a-flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield MutArray{..} s ->-                let p = INDEX_PREV(arrEnd,a)-                 in D.Skip (InnerLoop s arrContents p arrStart)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p start) | p < start =-        return $ D.Skip $ OuterLoop st--    step' _ (InnerLoop st contents p start) = do-        x <- liftIO $ peekWith contents p-        let cur = INDEX_PREV(p,a)-        return $ D.Yield x (InnerLoop st contents cur start)------------------------------------------------------------------------------------ Unfolds----------------------------------------------------------------------------------data ArrayUnsafe a = ArrayUnsafe-    {-# UNPACK #-} !MutableByteArray   -- contents-    {-# UNPACK #-} !Int                -- index 1-    {-# UNPACK #-} !Int                -- index 2--toArrayUnsafe :: MutArray a -> ArrayUnsafe a-toArrayUnsafe (MutArray contents start end _) = ArrayUnsafe contents start end--fromArrayUnsafe ::-#ifdef DEVBUILD-    Unbox a =>-#endif-    ArrayUnsafe a -> MutArray a-fromArrayUnsafe (ArrayUnsafe contents start end) =-         MutArray contents start end end--{-# INLINE_NORMAL producerWith #-}-producerWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> Producer m (MutArray a) a-producerWith liftio = Producer step (return . toArrayUnsafe) extract-    where--    {-# INLINE_LATE step #-}-    step (ArrayUnsafe _ cur end)-        | assert (cur <= end) (cur == end) = return D.Stop-    step (ArrayUnsafe contents cur end) = do-            -- When we use a purely lazy Monad like Identity, we need to force a-            -- few actions for correctness and execution order sanity. We want-            -- the peek to occur right here and not lazily at some later point-            -- because we want the peek to be ordered with respect to the touch.-            !x <- liftio $ peekWith contents cur-            return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)--    extract = return . fromArrayUnsafe---- | Resumable unfold of an array.----{-# INLINE_NORMAL producer #-}-producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a-producer = producerWith liftIO---- | Unfold an array into a stream.----{-# INLINE_NORMAL reader #-}-reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a-reader = Producer.simplify producer--{-# INLINE_NORMAL readerRevWith #-}-readerRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> Unfold m (MutArray a) a-readerRevWith liftio = Unfold step inject-    where--    inject (MutArray contents start end _) =-        let p = INDEX_PREV(end,a)-         in return $ ArrayUnsafe contents start p--    {-# INLINE_LATE step #-}-    step (ArrayUnsafe _ start p) | p < start = return D.Stop-    step (ArrayUnsafe contents start p) = do-        !x <- liftio $ peekWith contents p-        return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))---- | Unfold an array into a stream in reverse order.----{-# INLINE_NORMAL readerRev #-}-readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a-readerRev = readerRevWith liftIO------------------------------------------------------------------------------------ to Lists and streams----------------------------------------------------------------------------------{---- Use foldr/build fusion to fuse with list consumers--- This can be useful when using the IsList instance-{-# INLINE_LATE toListFB #-}-toListFB :: forall a b. Unbox a => (a -> b -> b) -> b -> MutArray a -> b-toListFB c n MutArray{..} = go arrStart-    where--    go p | assert (p <= arrEnd) (p == arrEnd) = n-    go p =-        -- unsafeInlineIO allows us to run this in Identity monad for pure-        -- toList/foldr case which makes them much faster due to not-        -- accumulating the list and fusing better with the pure consumers.-        ---        -- This should be safe as the array contents are guaranteed to be-        -- evaluated/written to before we peek at them.-        -- XXX-        let !x = unsafeInlineIO $ do-                    r <- peekWith arrContents p-                    return r-        in c x (go (PTR_NEXT(p,a)))--}---- XXX Monadic foldr/build fusion?--- Reference: https://www.researchgate.net/publication/220676509_Monadic_augment_and_generalised_short_cut_fusion---- | Convert a 'MutArray' into a list.----{-# INLINE toList #-}-toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]-toList MutArray{..} = liftIO $ go arrStart-    where--    go p | assert (p <= arrEnd) (p == arrEnd) = return []-    go p = do-        x <- peekWith arrContents p-        (:) x <$> go (INDEX_NEXT(p,a))--{-# INLINE_NORMAL toStreamDWith #-}-toStreamDWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a-toStreamDWith liftio MutArray{..} = D.Stream step arrStart--    where--    {-# INLINE_LATE step #-}-    step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop-    step _ p = liftio $ do-        r <- peekWith arrContents p-        return $ D.Yield r (INDEX_NEXT(p,a))---- | Use the 'reader' unfold instead.------ @toStreamD = D.unfold reader@------ We can try this if the unfold has any performance issues.-{-# INLINE_NORMAL toStreamD #-}-toStreamD :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a-toStreamD = toStreamDWith liftIO--{-# INLINE toStreamKWith #-}-toStreamKWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a-toStreamKWith liftio MutArray{..} = go arrStart--    where--    go p | assert (p <= arrEnd) (p == arrEnd) = K.nil-         | otherwise =-        let elemM = peekWith arrContents p-        in liftio elemM `K.consM` go (INDEX_NEXT(p,a))--{-# INLINE toStreamK #-}-toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a-toStreamK = toStreamKWith liftIO--{-# INLINE_NORMAL toStreamDRevWith #-}-toStreamDRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a-toStreamDRevWith liftio MutArray{..} =-    let p = INDEX_PREV(arrEnd,a)-    in D.Stream step p--    where--    {-# INLINE_LATE step #-}-    step _ p | p < arrStart = return D.Stop-    step _ p = liftio $ do-        r <- peekWith arrContents p-        return $ D.Yield r (INDEX_PREV(p,a))---- | Use the 'readerRev' unfold instead.------ @toStreamDRev = D.unfold readerRev@------ We can try this if the unfold has any perf issues.-{-# INLINE_NORMAL toStreamDRev #-}-toStreamDRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a-toStreamDRev = toStreamDRevWith liftIO--{-# INLINE toStreamKRevWith #-}-toStreamKRevWith ::-       forall m a. (Monad m, Unbox a)-    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a-toStreamKRevWith liftio MutArray {..} =-    let p = INDEX_PREV(arrEnd,a)-    in go p--    where--    go p | p < arrStart = K.nil-         | otherwise =-        let elemM = peekWith arrContents p-        in liftio elemM `K.consM` go (INDEX_PREV(p,a))--{-# INLINE toStreamKRev #-}-toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a-toStreamKRev = toStreamKRevWith liftIO------------------------------------------------------------------------------------ Folding------------------------------------------------------------------------------------ XXX Need something like "MutArray m a" enforcing monadic action to avoid the--- possibility of such APIs.------ | Strict left fold of an array.-{-# INLINE_NORMAL foldl' #-}-foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b-foldl' f z arr = D.foldl' f z $ toStreamD arr---- | Right fold of an array.-{-# INLINE_NORMAL foldr #-}-foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b-foldr f z arr = D.foldr f z $ toStreamD arr------------------------------------------------------------------------------------ Folds------------------------------------------------------------------------------------ Note: Arrays may be allocated with a specific alignment at the beginning of--- the array. If you need to maintain that alignment on reallocations then you--- can resize the array manually before append, using an aligned resize--- operation.---- XXX Keep the bound intact to not lose any free space? Perf impact?---- | @writeAppendNUnsafe n alloc@ appends up to @n@ input items to the supplied--- array.------ Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory--- corruption or segfault.------ Any free space left in the array after appending @n@ elements is lost.------ /Internal/-{-# INLINE_NORMAL writeAppendNUnsafe #-}-writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>-       Int-    -> m (MutArray a)-    -> Fold m a (MutArray a)-writeAppendNUnsafe n action =-    fmap fromArrayUnsafe $ FL.foldlM' step initial--    where--    initial = do-        assert (n >= 0) (return ())-        arr@(MutArray _ _ end bound) <- action-        let free = bound - end-            needed = n * SIZE_OF(a)-        -- XXX We can also reallocate if the array has too much free space,-        -- otherwise we lose that space.-        arr1 <--            if free < needed-            then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr-            else return arr-        return $ toArrayUnsafe arr1--    step (ArrayUnsafe contents start end) x = do-        liftIO $ pokeWith contents end x-        return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))---- | Append @n@ elements to an existing array. Any free space left in the array--- after appending @n@ elements is lost.------ >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)----{-# INLINE_NORMAL writeAppendN #-}-writeAppendN :: forall m a. (MonadIO m, Unbox a) =>-    Int -> m (MutArray a) -> Fold m a (MutArray a)-writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)---- | @writeAppendWith realloc action@ mutates the array generated by @action@ to--- append the input stream. If there is no reserved space available in the--- array it is reallocated to a size in bytes  determined by @realloc oldSize@,--- where @oldSize@ is the current size of the array in bytes.------ Note that the returned array may be a mutated version of original array.------ >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)------ /Pre-release/-{-# INLINE writeAppendWith #-}-writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>-    (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)-writeAppendWith sizer = FL.foldlM' (snocWith sizer)---- | @append action@ mutates the array generated by @action@ to append the--- input stream. If there is no reserved space available in the array it is--- reallocated to double the size.------ Note that the returned array may be a mutated version of original array.------ >>> writeAppend = MutArray.writeAppendWith (* 2)----{-# INLINE writeAppend #-}-writeAppend :: forall m a. (MonadIO m, Unbox a) =>-    m (MutArray a) -> Fold m a (MutArray a)-writeAppend = writeAppendWith (* 2)---- XXX We can carry bound as well in the state to make sure we do not lose the--- remaining capacity. Need to check perf impact.------ | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function--- as argument.------ >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n------ /Pre-release/-{-# INLINE_NORMAL writeNWithUnsafe #-}-writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial--    where--    initial = toArrayUnsafe <$> alloc (max n 0)--    step (ArrayUnsafe contents start end) x = do-        liftIO $ pokeWith contents end x-        return-          $ ArrayUnsafe contents start (INDEX_NEXT(end,a))---- | Like 'writeN' but does not check the array bounds when writing. The fold--- driver must not call the step function more than 'n' times otherwise it will--- corrupt the memory and crash. This function exists mainly because any--- conditional in the step function blocks fusion causing 10x performance--- slowdown.------ >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.newPinned----{-# INLINE_NORMAL writeNUnsafe #-}-writeNUnsafe :: forall m a. (MonadIO m, Unbox a)-    => Int -> Fold m a (MutArray a)-writeNUnsafe = writeNWithUnsafe newPinned---- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array--- allocated using the @alloc@ function.------ >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)--- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n----{-# INLINE_NORMAL writeNWith #-}-writeNWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)---- | @writeN n@ folds a maximum of @n@ elements from the input stream to an--- 'MutArray'.------ >>> writeN = MutArray.writeNWith MutArray.newPinned--- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)--- >>> writeN n = MutArray.writeAppendN n (MutArray.newPinned n)----{-# INLINE_NORMAL writeN #-}-writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)-writeN = writeNWith newPinned---- | Like writeNWithUnsafe but writes the array in reverse order.------ /Internal/-{-# INLINE_NORMAL writeRevNWithUnsafe #-}-writeRevNWithUnsafe :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial--    where--    toArrayUnsafeRev (MutArray contents _ _ bound) =-         ArrayUnsafe contents bound bound--    initial = toArrayUnsafeRev <$> alloc (max n 0)--    step (ArrayUnsafe contents start end) x = do-        let ptr = INDEX_PREV(start,a)-        liftIO $ pokeWith contents ptr x-        return-          $ ArrayUnsafe contents ptr end---- | Like writeNWith but writes the array in reverse order.------ /Internal/-{-# INLINE_NORMAL writeRevNWith #-}-writeRevNWith :: forall m a. (MonadIO m, Unbox a)-    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)-writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)---- | Like writeN but writes the array in reverse order.------ /Pre-release/-{-# INLINE_NORMAL writeRevN #-}-writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)-writeRevN = writeRevNWith newPinned---- | @writeNAligned align n@ folds a maximum of @n@ elements from the input--- stream to a 'MutArray' aligned to the given size.------ >>> writeNAligned align = MutArray.writeNWith (MutArray.newAlignedPinned align)--- >>> writeNAligned align n = MutArray.writeAppendN n (MutArray.newAlignedPinned align n)------ /Pre-release/----{-# INLINE_NORMAL writeNAligned #-}-writeNAligned :: forall m a. (MonadIO m, Unbox a)-    => Int -> Int -> Fold m a (MutArray a)-writeNAligned align = writeNWith (newAlignedPinned align)---- XXX Buffer to a list instead?------ | Buffer a stream into a stream of arrays.------ >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK------ Breaking an array into an array stream  can be useful to consume a large--- array sequentially such that memory of the array is released incrementatlly.------ See also: 'arrayStreamKFromStreamD'.------ /Unimplemented/----{-# INLINE_NORMAL writeChunks #-}-writeChunks :: (MonadIO m, Unbox a) =>-    Int -> Fold m a (StreamK n (MutArray a))-writeChunks n = FL.many (writeN n) FL.toStreamK---- XXX Compare writeWith with fromStreamD which uses an array of streams--- implementation. We can write this using writeChunks above if that is faster.--- If writeWith is faster then we should use that to implement--- fromStreamD.------ XXX The realloc based implementation needs to make one extra copy if we use--- shrinkToFit.  On the other hand, the stream of arrays implementation may--- buffer the array chunk pointers in memory but it does not have to shrink as--- we know the exact size in the end. However, memory copying does not seem to--- be as expensive as the allocations. Therefore, we need to reduce the number--- of allocations instead. Also, the size of allocations matters, right sizing--- an allocation even at the cost of copying sems to help.  Should be measured--- on a big stream with heavy calls to toArray to see the effect.------ XXX check if GHC's memory allocator is efficient enough. We can try the C--- malloc to compare against.---- | @writeWith minCount@ folds the whole input to a single array. The array--- starts at a size big enough to hold minCount elements, the size is doubled--- every time the array needs to be grown.------ /Caution! Do not use this on infinite streams./------ >>> f n = MutArray.writeAppendWith (* 2) (MutArray.newPinned n)--- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)--- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)------ /Pre-release/-{-# INLINE_NORMAL writeWith #-}-writeWith :: forall m a. (MonadIO m, Unbox a)-    => Int -> Fold m a (MutArray a)--- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (newPinned n)-writeWith elemCount =-    FL.rmapM extract $ FL.foldlM' step initial--    where--    initial = do-        when (elemCount < 0) $ error "writeWith: elemCount is negative"-        liftIO $ newPinned elemCount--    step arr@(MutArray _ start end bound) x-        | INDEX_NEXT(end,a) > bound = do-        let oldSize = end - start-            newSize = max (oldSize * 2) 1-        arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr-        snocUnsafe arr1 x-    step arr x = snocUnsafe arr x--    extract = liftIO . rightSize---- | Fold the whole input to a single array.------ Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes--- rounded up to the element size.------ /Caution! Do not use this on infinite streams./----{-# INLINE write #-}-write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)-write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)------------------------------------------------------------------------------------ construct from streams, known size------------------------------------------------------------------------------------ | Use the 'writeN' fold instead.------ >>> fromStreamDN n = Stream.fold (MutArray.writeN n)----{-# INLINE_NORMAL fromStreamDN #-}-fromStreamDN :: forall m a. (MonadIO m, Unbox a)-    => Int -> D.Stream m a -> m (MutArray a)--- fromStreamDN n = D.fold (writeN n)-fromStreamDN limit str = do-    (arr :: MutArray a) <- liftIO $ newPinned limit-    end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str-    return $ arr {arrEnd = end}--    where--    fwrite arrContents ptr x = do-        liftIO $ pokeWith arrContents ptr x-        return $ INDEX_NEXT(ptr,a)---- | Create a 'MutArray' from the first N elements of a list. The array is--- allocated to size N, if the list terminates before N elements then the--- array may hold less than N elements.----{-# INLINABLE fromListN #-}-fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)-fromListN n xs = fromStreamDN n $ D.fromList xs---- | Like fromListN but writes the array in reverse order.------ /Pre-release/-{-# INLINE fromListRevN #-}-fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)-fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs------------------------------------------------------------------------------------ convert stream to a single array----------------------------------------------------------------------------------{-# INLINE arrayStreamKLength #-}-arrayStreamKLength :: (Monad m, Unbox a) => StreamK m (MutArray a) -> m Int-arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)---- | Convert an array stream to an array. Note that this requires peak memory--- that is double the size of the array stream.----{-# INLINE fromArrayStreamK #-}-fromArrayStreamK :: (Unbox a, MonadIO m) =>-    StreamK m (MutArray a) -> m (MutArray a)-fromArrayStreamK as = do-    len <- arrayStreamKLength as-    fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as---- CAUTION: a very large number (millions) of arrays can degrade performance--- due to GC overhead because we need to buffer the arrays before we flatten--- all the arrays.------ XXX Compare if this is faster or "fold write".------ | We could take the approach of doubling the memory allocation on each--- overflow. This would result in more or less the same amount of copying as in--- the chunking approach. However, if we have to shrink in the end then it may--- result in an extra copy of the entire data.------ >>> fromStreamD = StreamD.fold MutArray.write----{-# INLINE fromStreamD #-}-fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)-fromStreamD m = arrayStreamKFromStreamD m >>= fromArrayStreamK---- | Create a 'MutArray' from a list. The list must be of finite size.----{-# INLINE fromList #-}-fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)-fromList xs = fromStreamD $ D.fromList xs---- XXX We are materializing the whole list first for getting the length. Check--- if the 'fromList' like chunked implementation would fare better.---- | Like 'fromList' but writes the contents of the list in reverse order.-{-# INLINE fromListRev #-}-fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)-fromListRev xs = fromListRevN (Prelude.length xs) xs------------------------------------------------------------------------------------ Combining------------------------------------------------------------------------------------ | Put a sub range of a source array into a subrange of a destination array.--- This is not safe as it does not check the bounds.-{-# INLINE putSliceUnsafe #-}-putSliceUnsafe :: MonadIO m => MutArray a -> Int -> MutArray a -> Int -> Int -> m ()-putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do-    assertM(lenBytes <= arrBound dst - dstStartBytes)-    assertM(lenBytes <= arrEnd src - srcStartBytes)-    let !(I# srcStartBytes#) = srcStartBytes-        !(I# dstStartBytes#) = dstStartBytes-        !(I# lenBytes#) = lenBytes-    let arrS# = getMutableByteArray# (arrContents src)-        arrD# = getMutableByteArray# (arrContents dst)-    IO $ \s# -> (# copyMutableByteArray#-                    arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#-                , () #)---- | Copy two arrays into a newly allocated array.-{-# INLINE spliceCopy #-}-spliceCopy :: forall m a. MonadIO m =>-#ifdef DEVBUILD-    Unbox a =>-#endif-    MutArray a -> MutArray a -> m (MutArray a)-spliceCopy arr1 arr2 = liftIO $ do-    let start1 = arrStart arr1-        start2 = arrStart arr2-        len1 = arrEnd arr1 - start1-        len2 = arrEnd arr2 - start2-    newArrContents <- liftIO $ Unboxed.newPinnedBytes (len1 + len2)-    let len = len1 + len2-        newArr = MutArray newArrContents 0 len len-    putSliceUnsafe arr1 start1 newArr 0 len1-    putSliceUnsafe arr2 start2 newArr len1 len2-    return newArr---- | Really really unsafe, appends the second array into the first array. If--- the first array does not have enough space it may cause silent data--- corruption or if you are lucky a segfault.-{-# INLINE spliceUnsafe #-}-spliceUnsafe :: MonadIO m =>-    MutArray a -> MutArray a -> m (MutArray a)-spliceUnsafe dst src =-    liftIO $ do-         let startSrc = arrStart src-             srcLen = arrEnd src - startSrc-             endDst = arrEnd dst-         assertM(endDst + srcLen <= arrBound dst)-         putSliceUnsafe src startSrc dst endDst srcLen-         return $ dst {arrEnd = endDst + srcLen}---- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no--- reserved space available in @dst@ it is reallocated to a size determined by--- the @sizer dstBytes srcBytes@ function, where @dstBytes@ is the size of the--- first array and @srcBytes@ is the size of the second array, in bytes.------ Note that the returned array may be a mutated version of first array.------ /Pre-release/-{-# INLINE spliceWith #-}-spliceWith :: forall m a. (MonadIO m, Unbox a) =>-    (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)-spliceWith sizer dst@(MutArray _ start end bound) src = do-{--    let f = writeAppendWith (`sizer` byteLength src) (return dst)-     in D.fold f (toStreamD src)--}-    assert (end <= bound) (return ())-    let srcBytes = arrEnd src - arrStart src--    dst1 <--        if end + srcBytes >= bound-        then do-            let dstBytes = end - start-                newSizeInBytes = sizer dstBytes srcBytes-            when (newSizeInBytes < dstBytes + srcBytes)-                $ error-                    $ "splice: newSize is less than the total size "-                    ++ "of arrays being appended. Please check the "-                    ++ "sizer function passed."-            liftIO $ realloc newSizeInBytes dst-        else return dst-    spliceUnsafe dst1 src---- | The first array is mutated to append the second array. If there is no--- reserved space available in the first array a new allocation of exact--- required size is done.------ Note that the returned array may be a mutated version of first array.------ >>> splice = MutArray.spliceWith (+)------ /Pre-release/-{-# INLINE splice #-}-splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)-splice = spliceWith (+)---- | Like 'append' but the growth of the array is exponential. Whenever a new--- allocation is required the previous array size is at least doubled.------ This is useful to reduce allocations when folding many arrays together.------ Note that the returned array may be a mutated version of first array.------ >>> spliceExp = MutArray.spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))------ /Pre-release/-{-# INLINE spliceExp #-}-spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)-spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))------------------------------------------------------------------------------------ Splitting------------------------------------------------------------------------------------ | Drops the separator byte-{-# INLINE breakOn #-}-breakOn :: MonadIO m-    => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))-breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do-    -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted-    -- Addr# to memchr and it should be safe (from ghc 8.4).-    -- XXX We do not need memchr here, we can use a Haskell equivalent.-    loc <- c_memchr p sep (fromIntegral $ byteLength arr)-    let sepIndex = loc `minusPtr` p-    return $-        if loc == nullPtr-        then (arr, Nothing)-        else-            ( MutArray-                { arrContents = arrContents-                , arrStart = arrStart-                , arrEnd = arrStart + sepIndex -- exclude the separator-                , arrBound = arrStart + sepIndex-                }-            , Just $ MutArray-                    { arrContents = arrContents-                    , arrStart = arrStart + (sepIndex + 1)-                    , arrEnd = arrEnd-                    , arrBound = arrBound-                    }-            )---- | Create two slices of an array without copying the original array. The--- specified index @i@ is the first index of the second slice.----splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)-splitAt i arr@MutArray{..} =-    let maxIndex = length arr - 1-    in  if i < 0-        then error "sliceAt: negative array index"-        else if i > maxIndex-             then error $ "sliceAt: specified array index " ++ show i-                        ++ " is beyond the maximum index " ++ show maxIndex-             else let off = i * SIZE_OF(a)-                      p = arrStart + off-                in ( MutArray-                  { arrContents = arrContents-                  , arrStart = arrStart-                  , arrEnd = p-                  , arrBound = p-                  }-                , MutArray-                  { arrContents = arrContents-                  , arrStart = p-                  , arrEnd = arrEnd-                  , arrBound = arrBound-                  }-                )------------------------------------------------------------------------------------ Casting------------------------------------------------------------------------------------ | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The array size must be a multiple of the size of type @b@--- otherwise accessing the last element of the array may result into a crash or--- a random value.------ /Pre-release/----castUnsafe ::-#ifdef DEVBUILD-    Unbox b =>-#endif-    MutArray a -> MutArray b-castUnsafe (MutArray contents start end bound) =-    MutArray contents start end bound---- | Cast an @MutArray a@ into an @MutArray Word8@.----asBytes :: MutArray a -> MutArray Word8-asBytes = castUnsafe---- | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The length of the array should be a multiple of the size of the--- target element otherwise 'Nothing' is returned.----cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)-cast arr =-    let len = byteLength arr-        r = len `mod` SIZE_OF(b)-     in if r /= 0-        then Nothing-        else Just $ castUnsafe arr---- XXX We can provide another API for "unsafe" FFI calls passing an unlifted--- pointer to the FFI call. For unsafe calls we do not need to pin the array.--- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in--- before the pointer is wrapped.------ From the GHC manual:------ GHC, since version 8.4, guarantees that garbage collection will never occur--- during an unsafe call, even in the bytecode interpreter, and further--- guarantees that unsafe calls will be performed in the calling thread. Making--- it safe to pass heap-allocated objects to unsafe functions.---- Unsafe because of direct pointer operations. The user must ensure that they--- are writing within the legal bounds of the array. Should we just name it--- asPtr, the unsafety is implicit for any pointer operations. And we are safe--- from Haskell perspective because we will be pinning the memory.---- | Use an @MutArray a@ as @Ptr a@. This is useful when we want to pass an array--- as a pointer to some operating system call or to a "safe" FFI call.------ If the array is not pinned it is copied to pinned memory before passing it--- to the monadic action.------ /Performance Notes:/ Forces a copy if the array is not pinned. It is advised--- that the programmer keeps this in mind and creates a pinned array--- opportunistically before this operation occurs, to avoid the cost of a copy--- if possible.------ /Unsafe/------ /Pre-release/----asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b-asPtrUnsafe arr f = do-  let contents = arrContents arr-      !ptr = Ptr (byteArrayContents#-                     (unsafeCoerce# (getMutableByteArray# contents)))-  -- XXX Check if the array is pinned, if not, copy it to a pinned array-  -- XXX We should probably pass to the IO action the byte length of the array-  -- as well so that bounds can be checked.-  r <- f (ptr `plusPtr` arrStart arr)-  liftIO $ touch contents-  return r------------------------------------------------------------------------------------ Equality------------------------------------------------------------------------------------ | Compare the length of the arrays. If the length is equal, compare the--- lexicographical ordering of two underlying byte arrays otherwise return the--- result of length comparison.------ /Pre-release/-{-# INLINE cmp #-}-cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering-cmp arr1 arr2 =-    liftIO-        $ do-            let marr1 = getMutableByteArray# (arrContents arr1)-                marr2 = getMutableByteArray# (arrContents arr2)-                !(I# st1#) = arrStart arr1-                !(I# st2#) = arrStart arr2-                !(I# len#) = byteLength arr1-            case compare (byteLength arr1) (byteLength arr2) of-                EQ -> do-                    r <- liftIO $ IO $ \s# ->-                             let res =-                                     I#-                                         (compareByteArrays#-                                              (unsafeCoerce# marr1)-                                              st1#-                                              (unsafeCoerce# marr2)-                                              st2#-                                              len#)-                              in (# s#, res #)-                    return $ compare r 0-                x -> return x------------------------------------------------------------------------------------ NFData------------------------------------------------------------------------------------ | Strip elements which match with predicate from both ends.------ /Pre-release/-{-# INLINE strip #-}-strip :: forall a m. (Unbox a, MonadIO m) =>-    (a -> Bool) -> MutArray a -> m (MutArray a)-strip eq arr@MutArray{..} = liftIO $ do-    st <- getStart arrStart-    end <- getLast arrEnd st-    return arr {arrStart = st, arrEnd = end, arrBound = end}--    where--    {--    -- XXX This should have the same perf but it does not, investigate.-    getStart = do-        r <- liftIO $ D.head $ D.findIndices (not . eq) $ toStreamD arr-        pure $-            case r of-                Nothing -> arrEnd-                Just i -> PTR_INDEX(arrStart,i,a)-    -}--    getStart cur = do-        if cur < arrEnd-        then do-            r <- peekWith arrContents cur-            if eq r-            then getStart (INDEX_NEXT(cur,a))-            else return cur-        else return cur--    getLast cur low = do-        if cur > low-        then do-            let prev = INDEX_PREV(cur,a)-            r <- peekWith arrContents prev-            if eq r-            then getLast prev low-            else return cur-        else return cur---- | Given an array sorted in ascending order except the last element being out--- of order, use bubble sort to place the last element at the right place such--- that the array remains sorted in ascending order.------ /Pre-release/-{-# INLINE bubble #-}-bubble :: (MonadIO m, Unbox a) => (a -> a -> Ordering) -> MutArray a -> m ()-bubble cmp0 arr =-    when (l > 1) $ do-        x <- getIndexUnsafe (l - 1) arr-        go x (l - 2)--        where--        l = length arr--        go x i =-            if i >= 0-            then do-                x1 <- getIndexUnsafe i arr-                case x `cmp0` x1 of-                    LT -> do-                        putIndexUnsafe (i + 1) arr x1-                        go x (i - 1)-                    _ -> putIndexUnsafe (i + 1) arr x-            else putIndexUnsafe (i + 1) arr x
+ src/Streamly/Internal/Data/Array/Stream.hs view
@@ -0,0 +1,1232 @@+{-# OPTIONS_GHC -Wno-deprecations #-}+-- |+-- Module      : Streamly.Internal.Data.Array.Stream+-- Copyright   : (c) 2019 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--+-- Combinators to efficiently manipulate streams of immutable arrays.+--+-- We can either push these in the MutArray module with a "chunks" prefix or+-- keep this as a separate module and release it.+--+module Streamly.Internal.Data.Array.Stream+    (+    -- * Creation+      Array.chunksOf+    , Array.pinnedChunksOf+    , Array.bufferChunks++    -- * Flattening to elements+    , concat+    , Array.flattenArrays+    , concatRev+    , Array.flattenArraysRev+    , interpose+    , interposeSuffix+    , intercalateSuffix+    , unlines++    -- * Elimination+    -- ** Element Folds+    -- The byte level foldBreak can work as efficiently as the chunk level. We+    -- can flatten the stream to byte stream and use that. But if we want the+    -- remaining stream to be a chunk stream then this could be handy. But it+    -- could also be implemented using parseBreak.+    , foldBreak+    , foldBreakD+    -- The byte level parseBreak cannot work efficiently. Because the stream+    -- will have to be a StreamK for backtracking, StreamK at byte level would+    -- not be efficient.+    , parseBreak -- StreamK.parseBreakChunks+    -- , parseBreakD+    -- , foldManyChunks+    -- , parseManyChunks+    , K.parseBreakChunks+    , K.parseChunks++    -- ** Array Folds+    -- XXX Use parseBreakChunks/parseChunks instead+    -- foldBreak can be implemented using parseBreak. Use StreamK.+    , runArrayFold+    , runArrayFoldBreak+    -- , parseArr+    , runArrayParserDBreak -- StreamK.parseBreakChunks+    , runArrayFoldMany++    , toArray++    -- * Compaction+    -- We can use something like foldManyChunks, parseManyChunks with a take+    -- fold.+    , lpackArraysChunksOf -- Fold.compactChunks+    , compact -- rechunk, compactChunks++    -- * Splitting+    -- We can use something like foldManyChunks, parseManyChunks with an+    -- appropriate splitting fold.+    , splitOn       -- Stream.rechunkOn+    , splitOnSuffix -- Stream.rechunkOnSuffix+    )+where++#include "ArrayMacros.h"+#include "inline.hs"++import Data.Bifunctor (second)+import Control.Exception (assert)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Proxy (Proxy(..))+import Data.Word (Word8)+import Streamly.Internal.Data.Unbox (Unbox(..))+import Fusion.Plugin.Types (Fuse(..))+import GHC.Exts (SpecConstrAnnotation(..))+import GHC.Types (SPEC(..))+import Prelude hiding (null, last, (!!), read, concat, unlines)++import Streamly.Data.Fold (Fold)+import Streamly.Internal.Data.Array.Type (Array(..))+import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))+import Streamly.Internal.Data.Parser (ParseError(..))+import Streamly.Internal.Data.Stream (Stream)+import Streamly.Internal.Data.StreamK (StreamK, fromStream, toStream)+import Streamly.Internal.Data.SVar.Type (adaptState, defState)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))++import qualified Streamly.Data.Fold as FL+import qualified Streamly.Internal.Data.Array as A+import qualified Streamly.Internal.Data.Array as Array+import qualified Streamly.Internal.Data.MutArray.Type as MA+import qualified Streamly.Internal.Data.MutArray.Stream as AS+import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))+import qualified Streamly.Internal.Data.Parser as PR+import qualified Streamly.Internal.Data.Parser as PRD+    (Parser(..), Initial(..))+import qualified Streamly.Internal.Data.Stream as D+import qualified Streamly.Internal.Data.StreamK as K++-- XXX Since these are immutable arrays MonadIO constraint can be removed from+-- most places.++-------------------------------------------------------------------------------+-- Append+-------------------------------------------------------------------------------++-- XXX efficiently compare two streams of arrays. Two streams can have chunks+-- of different sizes, we can handle that in the stream comparison abstraction.+-- This could be useful e.g. to fast compare whether two files differ.++-- | Convert a stream of arrays into a stream of their elements.+--+-- Same as the following:+--+-- > concat = Stream.unfoldMany Array.read+--+-- @since 0.7.0+{-# INLINE concat #-}+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+-- concat m = fromStreamD $ A.flattenArrays (toStreamD m)+-- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)+concat = D.unfoldMany A.reader++-- | Convert a stream of arrays into a stream of their elements reversing the+-- contents of each array before flattening.+--+-- > concatRev = Stream.unfoldMany Array.readerRev+--+-- @since 0.7.0+{-# INLINE concatRev #-}+concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a+-- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)+concatRev = D.unfoldMany A.readerRev++-------------------------------------------------------------------------------+-- Intersperse and append+-------------------------------------------------------------------------------++-- | Flatten a stream of arrays after inserting the given element between+-- arrays.+--+-- /Pre-release/+{-# INLINE interpose #-}+interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a+interpose x = D.interpose x A.reader++{-# INLINE intercalateSuffix #-}+intercalateSuffix :: (Monad m, Unbox a)+    => Array a -> Stream m (Array a) -> Stream m a+intercalateSuffix = D.intercalateSuffix A.reader++-- | Flatten a stream of arrays appending the given element after each+-- array.+--+-- @since 0.7.0+{-# INLINE interposeSuffix #-}+interposeSuffix :: (Monad m, Unbox a)+    => a -> Stream m (Array a) -> Stream m a+-- interposeSuffix x = fromStreamD . A.unlines x . toStreamD+interposeSuffix x = D.interposeSuffix x A.reader++data FlattenState s =+      OuterLoop s+    | InnerLoop s !MA.MutableByteArray !Int !Int++-- XXX This is a special case of interposeSuffix, can be removed.+-- XXX Remove monadIO constraint+{-# INLINE_NORMAL unlines #-}+unlines :: forall m a. (MonadIO m, Unbox a)+    => a -> D.Stream m (Array a) -> D.Stream m a+unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)+    where+    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield Array{..} s ->+                D.Skip (InnerLoop s arrContents arrStart arrEnd)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p end) | p == end =+        return $ D.Yield sep $ OuterLoop st++    step' _ (InnerLoop st contents p end) = do+        x <- liftIO $ peekByteIndex p contents+        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)++-------------------------------------------------------------------------------+-- Compact+-------------------------------------------------------------------------------++-- XXX These would not be needed once we implement compactLEFold, see+-- module Streamly.Internal.Data.Stream.MutChunked+--+-- XXX Note that this thaws immutable arrays for appending, that may be+-- problematic if multiple users do the same thing, however, immutable arrays+-- would usually have no capacity to append, therefore, a copy will be forced+-- anyway. Confirm this. We can forcefully trim the array capacity before thaw+-- to ensure this.+{-# INLINE_NORMAL packArraysChunksOf #-}+packArraysChunksOf :: (MonadIO m, Unbox a)+    => Int -> D.Stream m (Array a) -> D.Stream m (Array a)+packArraysChunksOf n str =+    D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str++-- XXX instead of writing two different versions of this operation, we should+-- write it as a pipe.+--+-- XXX Confirm that immutable arrays won't be modified.+{-# INLINE_NORMAL lpackArraysChunksOf #-}+lpackArraysChunksOf :: (MonadIO m, Unbox a)+    => Int -> Fold m (Array a) () -> Fold m (Array a) ()+lpackArraysChunksOf n fld =+    FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)++-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- maximum specified size in bytes.+--+-- @since 0.7.0+{-# INLINE compact #-}+compact :: (MonadIO m, Unbox a)+    => Int -> Stream m (Array a) -> Stream m (Array a)+compact = packArraysChunksOf++-------------------------------------------------------------------------------+-- Split+-------------------------------------------------------------------------------++data SplitState s arr+    = Initial s+    | Buffering s arr+    | Splitting s arr+    | Yielding arr (SplitState s arr)+    | Finishing++-- | Split a stream of arrays on a given separator byte, dropping the separator+-- and coalescing all the arrays between two separators into a single array.+--+-- @since 0.7.0+{-# INLINE_NORMAL _splitOn #-}+_splitOn+    :: MonadIO m+    => Word8+    -> D.Stream m (Array Word8)+    -> D.Stream m (Array Word8)+_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (Initial st) = do+        r <- step gst st+        case r of+            D.Yield arr s -> do+                (arr1, marr2) <- A.breakOn byte arr+                return $ case marr2 of+                    Nothing   -> D.Skip (Buffering s arr1)+                    Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))+            D.Skip s -> return $ D.Skip (Initial s)+            D.Stop -> return D.Stop++    step' gst (Buffering st buf) = do+        r <- step gst st+        case r of+            D.Yield arr s -> do+                (arr1, marr2) <- A.breakOn byte arr+                buf' <- A.splice buf arr1+                return $ case marr2 of+                    Nothing -> D.Skip (Buffering s buf')+                    Just x -> D.Skip (Yielding buf' (Splitting s x))+            D.Skip s -> return $ D.Skip (Buffering s buf)+            D.Stop -> return $+                if A.byteLength buf == 0+                then D.Stop+                else D.Skip (Yielding buf Finishing)++    step' _ (Splitting st buf) = do+        (arr1, marr2) <- A.breakOn byte buf+        return $ case marr2 of+                Nothing -> D.Skip $ Buffering st arr1+                Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)++    step' _ (Yielding arr next) = return $ D.Yield arr next+    step' _ Finishing = return D.Stop++-- XXX Remove MonadIO constraint.+-- | Split a stream of arrays on a given separator byte, dropping the separator+-- and coalescing all the arrays between two separators into a single array.+--+-- @since 0.7.0+{-# INLINE splitOn #-}+splitOn+    :: (MonadIO m)+    => Word8+    -> Stream m (Array Word8)+    -> Stream m (Array Word8)+splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice++{-# INLINE splitOnSuffix #-}+splitOnSuffix+    :: (MonadIO m)+    => Word8+    -> Stream m (Array Word8)+    -> Stream m (Array Word8)+-- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s+splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice++-------------------------------------------------------------------------------+-- Elimination - Running folds+-------------------------------------------------------------------------------++{-# INLINE_NORMAL foldBreakD #-}+foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>+    Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))+foldBreakD (FL.Fold fstep initial _ final) stream@(D.Stream step state) = do+    res <- initial+    case res of+        FL.Partial fs -> go SPEC state fs+        FL.Done fb -> return $! (fb, stream)++    where++    {-# INLINE go #-}+    go !_ st !fs = do+        r <- step defState st+        case r of+            D.Yield (Array contents start end) s ->+                let fp = Tuple' end contents+                 in goArray SPEC s fp start fs+            D.Skip s -> go SPEC s fs+            D.Stop -> do+                b <- final fs+                return (b, D.nil)++    goArray !_ s (Tuple' end _) !cur !fs+        | cur == end = do+            go SPEC s fs+    goArray !_ st fp@(Tuple' end contents) !cur !fs = do+        x <- liftIO $ peekByteIndex cur contents+        res <- fstep fs x+        let next = INDEX_NEXT(cur,a)+        case res of+            FL.Done b -> do+                let arr = Array contents next end+                return $! (b, D.cons arr (D.Stream step st))+            FL.Partial fs1 -> goArray SPEC st fp next fs1++{-# INLINE_NORMAL foldBreakK #-}+foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>+    Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))+foldBreakK (FL.Fold fstep initial _ final) stream = do+    res <- initial+    case res of+        FL.Partial fs -> go fs stream+        FL.Done fb -> return (fb, stream)++    where++    {-# INLINE go #-}+    go !fs st = do+        let stop = (, K.nil) <$> final fs+            single a = yieldk a K.nil+            yieldk (Array contents start end) r =+                let fp = Tuple' end contents+                 in goArray fs r fp start+         in K.foldStream defState yieldk single stop st++    goArray !fs st (Tuple' end _) !cur+        | cur == end = do+            go fs st+    goArray !fs st fp@(Tuple' end contents) !cur = do+        x <- liftIO $ peekByteIndex cur contents+        res <- fstep fs x+        let next = INDEX_NEXT(cur,a)+        case res of+            FL.Done b -> do+                let arr = Array contents next end+                return $! (b, K.cons arr st)+            FL.Partial fs1 -> goArray fs1 st fp next++-- | Fold an array stream using the supplied 'Fold'. Returns the fold result+-- and the unconsumed stream.+--+-- > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)+--+-- /Internal/+--+{-# INLINE_NORMAL foldBreak #-}+foldBreak ::+       (MonadIO m, Unbox a)+    => Fold m a b+    -> StreamK m (A.Array a)+    -> m (b, StreamK m (A.Array a))+-- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)+foldBreak = foldBreakK+-- If foldBreak performs better than runArrayFoldBreak we can use a rewrite+-- rule to rewrite runArrayFoldBreak to fold.+-- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)++-------------------------------------------------------------------------------+-- Fold to a single Array+-------------------------------------------------------------------------------++-- When we have to take an array partially, take the last part of the array.+{-# INLINE takeArrayListRev #-}+takeArrayListRev :: forall a. Unbox a => Int -> [Array a] -> [Array a]+takeArrayListRev = go++    where++    go _ [] = []+    go n _ | n <= 0 = []+    go n (x:xs) =+        let len = Array.length x+        in if n > len+           then x : go (n - len) xs+           else if n == len+           then [x]+           else let !(Array contents _ end) = x+                    !start = end - (n * SIZE_OF(a))+                 in [Array contents start end]++-- When we have to take an array partially, take the last part of the array in+-- the first split.+{-# INLINE splitAtArrayListRev #-}+splitAtArrayListRev ::+    forall a. Unbox a => Int -> [Array a] -> ([Array a],[Array a])+splitAtArrayListRev n ls+  | n <= 0 = ([], ls)+  | otherwise = go n ls+    where+        go :: Int -> [Array a] -> ([Array a], [Array a])+        go _  []     = ([], [])+        go m (x:xs) =+            let len = Array.length x+                (xs', xs'') = go (m - len) xs+             in if m > len+                then (x:xs', xs'')+                else if m == len+                then ([x],xs)+                else let !(Array contents start end) = x+                         end1 = end - (m * SIZE_OF(a))+                         arr2 = Array contents start end1+                         arr1 = Array contents end1 end+                      in ([arr1], arr2:xs)++-------------------------------------------------------------------------------+-- Fold to a single Array+-------------------------------------------------------------------------------++{-++-- XXX Both of these implementations of splicing seem to perform equally well.+-- We need to perform benchmarks over a range of sizes though.++-- CAUTION! length must more than equal to lengths of all the arrays in the+-- stream.+{-# INLINE spliceArraysLenUnsafe #-}+spliceArraysLenUnsafe :: (MonadIO m, Unbox a)+    => Int -> Stream m (MutArray a) -> m (MutArray a)+spliceArraysLenUnsafe len buffered = do+    -- XXX The new array's pinned state should depend on the first element+    -- of the stream. We should uncons the stream and do the required.+    arr <- undefined+    D.foldlM' MA.spliceUnsafe (return arr) buffered++{-# INLINE _spliceArrays #-}+_spliceArrays :: (MonadIO m, Unbox a)+    => Stream m (Array a) -> m (Array a)+_spliceArrays s = do+    buffered <- D.foldr K.cons K.nil s+    len <- K.fold FL.sum (fmap Array.length buffered)+    -- XXX The new array's pinned state should depend on the first element+    -- of the stream. We should uncons the stream and do the required.+    arr <- undefined+    final <- D.foldlM' writeArr (return arr) (toStream buffered)+    return $ A.unsafeFreeze final++    where++    writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)++{-# INLINE _spliceArraysBuffered #-}+_spliceArraysBuffered :: (MonadIO m, Unbox a)+    => Stream m (Array a) -> m (Array a)+_spliceArraysBuffered s = do+    buffered <- D.foldr K.cons K.nil s+    len <- K.fold FL.sum (fmap Array.length buffered)+    A.unsafeFreeze <$>+        spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))++-}++{-# INLINE spliceArraysRealloced #-}+spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)+    => Stream m (Array a) -> m (Array a)+spliceArraysRealloced s = do+    res <- D.uncons s+    case res of+        Just (a, strm) -> do+            arr <-+                D.foldlM'+                    MA.spliceExp+                    (pure (A.unsafeThaw a))+                    (fmap A.unsafeThaw strm)+            liftIO $ A.unsafeFreeze <$> MA.rightSize arr+        Nothing -> pure A.nil++-- XXX This should just be "fold A.write"+--+-- | Given a stream of arrays, splice them all together to generate a single+-- array. The stream must be /finite/.+--+-- @since 0.7.0+{-# INLINE toArray #-}+toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)+toArray = spliceArraysRealloced+-- spliceArrays = _spliceArraysBuffered++-- exponentially increasing sizes of the chunks upto the max limit.+-- XXX this will be easier to implement with parsers/terminating folds+-- With this we should be able to reduce the number of chunks/allocations.+-- The reallocation/copy based toArray can also be implemented using this.+--+{-+{-# INLINE toArraysInRange #-}+toArraysInRange :: (MonadIO m, Unbox a)+    => Int -> Int -> Fold m (Array a) b -> Fold m a b+toArraysInRange low high (Fold step initial extract) =+-}++{-+-- | Fold the input to a pure buffered stream (List) of arrays.+{-# INLINE _toArraysOf #-}+_toArraysOf :: (MonadIO m, Unbox a)+    => Int -> Fold m a (Stream Identity (Array a))+_toArraysOf n = FL.groupsOf n (A.writeNF n) FL.toStream+-}++-------------------------------------------------------------------------------+-- Elimination - running element parsers+-------------------------------------------------------------------------------++-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need+-- to make a newtype.+{-# ANN type List NoSpecConstr #-}+newtype List a = List {getList :: [a]}++{-+-- This can be generalized to any type provided it can be unfolded to a stream+-- and it can be combined using a semigroup operation.+--+-- XXX This should be written using CPS (as parseK) if we want it to scale wrt+-- to the number of times it can be called on the same stream.+{-# INLINE_NORMAL parseBreakD #-}+parseBreakD ::+       forall m a b. (MonadIO m, MonadThrow m, Unbox a)+    => PRD.Parser a m b+    -> D.Stream m (Array.Array a)+    -> m (b, D.Stream m (Array.Array a))+parseBreakD+    (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do++    res <- initial+    case res of+        PRD.IPartial s -> go SPEC state (List []) s+        PRD.IDone b -> return (b, stream)+        PRD.IError err -> throwM $ ParseError err++    where++    -- "backBuf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go !_ st backBuf !pst = do+        r <- step defState st+        case r of+            D.Yield (Array contents start end) s ->+                gobuf SPEC s backBuf+                    (Tuple' end contents) start pst+            D.Skip s -> go SPEC s backBuf pst+            D.Stop -> do+                b <- extract pst+                return (b, D.nil)++    -- Use strictness on "cur" to keep it unboxed+    gobuf !_ s backBuf (Tuple' end _) !cur !pst+        | cur == end = do+            go SPEC s backBuf pst+    gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do+        x <- liftIO $ peekByteIndex contents cur+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            PR.Partial 0 pst1 ->+                 gobuf SPEC s (List []) fp next pst1+            PR.Partial n pst1 -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let src0 = Prelude.take n (x:getList backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                let !(Array cont1 start end1) = src+                    fp1 = Tuple' end1 cont1+                gobuf SPEC s (List []) fp1 start pst1+            PR.Continue 0 pst1 ->+                gobuf SPEC s (List (x:getList backBuf)) fp next pst1+            PR.Continue n pst1 -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let (src0, buf1) = splitAt n (x:getList backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                let !(Array cont1 start end1) = src+                    fp1 = Tuple' end1 cont1+                gobuf SPEC s (List buf1) fp1 start pst1+            PR.Done 0 b -> do+                let arr = Array contents next end+                return (b, D.cons arr (D.Stream step s))+            PR.Done n b -> do+                assert (n <= Prelude.length (x:getList backBuf)) (return ())+                let src0 = Prelude.take n (x:getList backBuf)+                    -- XXX create the array in reverse instead+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    -- XXX Use StreamK to avoid adding arbitrary layers of+                    -- constructors every time.+                    str = D.cons arr0 (D.cons arr1 (D.Stream step s))+                return (b, str)+            PR.Error err -> throwM $ ParseError err+-}++{-# INLINE_NORMAL parseBreakK #-}+parseBreakK ::+       forall m a b. (MonadIO m, Unbox a)+    => PRD.Parser a m b+    -> K.StreamK m (Array.Array a)+    -> m (Either ParseError b, K.StreamK m (Array.Array a))+parseBreakK (PRD.Parser pstep initial extract) stream = do+    res <- initial+    case res of+        PRD.IPartial s -> go s stream []+        PRD.IDone b -> return (Right b, stream)+        PRD.IError err -> return (Left (ParseError err), stream)++    where++    -- "backBuf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go !pst st backBuf = do+        let stop = goStop pst backBuf -- (, K.nil) <$> extract pst+            single a = yieldk a K.nil+            yieldk arr r = goArray pst backBuf r arr+         in K.foldStream defState yieldk single stop st++    -- Use strictness on "cur" to keep it unboxed+    goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf+    goArray !pst backBuf st (Array contents cur end) = do+        x <- liftIO $ peekByteIndex cur contents+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            PR.Partial 0 s ->+                 goArray s [] st (Array contents next end)+            PR.Partial n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goArray s [] st src+            PR.Continue 0 s ->+                goArray s (x:backBuf) st (Array contents next end)+            PR.Continue n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let (src0, buf1) = splitAt n (x:backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goArray s buf1 st src+            PR.Done 0 b -> do+                let arr = Array contents next end+                return (Right b, K.cons arr st)+            PR.Done n b -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    str = K.cons arr0 (K.cons arr1 st)+                return (Right b, str)+            PR.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = A.fromListN n (Prelude.reverse backBuf)+                    arr1 = Array contents cur end+                    str = K.cons arr0 (K.cons arr1 stream)+                return (Left (ParseError err), str)++    -- This is a simplified goArray+    goExtract !pst backBuf (Array _ cur end)+        | cur == end = goStop pst backBuf+    goExtract !pst backBuf (Array contents cur end) = do+        x <- liftIO $ peekByteIndex cur contents+        pRes <- pstep pst x+        let next = INDEX_NEXT(cur,a)+        case pRes of+            PR.Partial 0 s ->+                 goExtract s [] (Array contents next end)+            PR.Partial n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let src0 = Prelude.take n (x:backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goExtract s [] src+            PR.Continue 0 s ->+                goExtract s backBuf (Array contents next end)+            PR.Continue n s -> do+                assert (n <= Prelude.length (x:backBuf)) (return ())+                let (src0, buf1) = splitAt n (x:backBuf)+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    src = arr0 <> arr1+                goExtract s buf1 src+            PR.Done 0 b -> do+                let arr = Array contents next end+                return (Right b, K.fromPure arr)+            PR.Done n b -> do+                assert (n <= Prelude.length backBuf) (return ())+                let src0 = Prelude.take n backBuf+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = A.fromListN n (Prelude.reverse src0)+                    arr1 = Array contents next end+                    str = K.cons arr0 (K.fromPure arr1)+                return (Right b, str)+            PR.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = A.fromListN n (Prelude.reverse backBuf)+                    arr1 = Array contents cur end+                    str = K.cons arr0 (K.cons arr1 stream)+                return (Left (ParseError err), str)++    -- This is a simplified goExtract+    {-# INLINE goStop #-}+    goStop !pst backBuf = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"+            PR.Continue 0 s ->+                goStop s backBuf+            PR.Continue n s -> do+                assert (n <= Prelude.length backBuf) (return ())+                let (src0, buf1) = splitAt n backBuf+                    arr = A.fromListN n (Prelude.reverse src0)+                goExtract s buf1 arr+            PR.Done 0 b ->+                return (Right b, K.nil)+            PR.Done n b -> do+                assert (n <= Prelude.length backBuf) (return ())+                let src0 = Prelude.take n backBuf+                    -- XXX Use fromListRevN once implemented+                    -- arr0 = A.fromListRevN n src0+                    arr0 = A.fromListN n (Prelude.reverse src0)+                return (Right b, K.fromPure arr0)+            PR.Error err -> do+                let n = Prelude.length backBuf+                    arr0 = A.fromListN n (Prelude.reverse backBuf)+                return (Left (ParseError err), K.fromPure arr0)++-- | Parse an array stream using the supplied 'Parser'.  Returns the parse+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.+--+-- /Internal/+--+{-# INLINE_NORMAL parseBreak #-}+parseBreak ::+       (MonadIO m, Unbox a)+    => PR.Parser a m b+    -> StreamK m (A.Array a)+    -> m (Either ParseError b, StreamK m (A.Array a))+{-+parseBreak p s =+    fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)+-}+parseBreak = parseBreakK++-------------------------------------------------------------------------------+-- Elimination - Running Array Folds and parsers+-------------------------------------------------------------------------------++-- | Note that this is not the same as using a @Parser (Array a) m b@ with the+-- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse+-- would consume the input arrays as single unit. This parser parses in the way+-- as described in the ChunkFold module. The input arrays are treated as @n@+-- element units and can be consumed partially. The remaining elements are+-- inserted in the source stream as an array.+--+{-# INLINE_NORMAL runArrayParserDBreak #-}+runArrayParserDBreak ::+       forall m a b. (MonadIO m, Unbox a)+    => PRD.Parser (Array a) m b+    -> D.Stream m (Array.Array a)+    -> m (Either ParseError b, D.Stream m (Array.Array a))+runArrayParserDBreak+    (PRD.Parser pstep initial extract)+    stream@(D.Stream step state) = do++    res <- initial+    case res of+        PRD.IPartial s -> go SPEC state (List []) s+        PRD.IDone b -> return (Right b, stream)+        PRD.IError err -> return (Left (ParseError err), stream)++    where++    -- "backBuf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go _ st backBuf !pst = do+        r <- step defState st+        case r of+            D.Yield x s -> gobuf SPEC [x] s backBuf pst+            D.Skip s -> go SPEC s backBuf pst+            D.Stop -> goStop backBuf pst++    gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst+    gobuf !_ (x:xs) s backBuf !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                 gobuf SPEC xs s (List []) pst1+            PR.Partial n pst1 -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let src0 = takeArrayListRev n (x:getList backBuf)+                    src  = Prelude.reverse src0 ++ xs+                gobuf SPEC src s (List []) pst1+            PR.Continue 0 pst1 ->+                gobuf SPEC xs s (List (x:getList backBuf)) pst1+            PR.Continue n pst1 -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)+                    src  = Prelude.reverse src0 ++ xs+                gobuf SPEC src s (List buf1) pst1+            PR.Done 0 b -> do+                let str = D.append (D.fromList xs) (D.Stream step s)+                return (Right b, str)+            PR.Done n b -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let src0 = takeArrayListRev n (x:getList backBuf)+                    src = Prelude.reverse src0 ++ xs+                return (Right b, D.append (D.fromList src) (D.Stream step s))+            PR.Error err -> do+                let src0 = x:getList backBuf+                    src = Prelude.reverse src0 ++ x:xs+                    strm = D.append (D.fromList src) (D.Stream step s)+                return (Left (ParseError err), strm)++    -- This is a simplified gobuf+    goExtract _ [] backBuf !pst = goStop backBuf pst+    goExtract _ (x:xs) backBuf !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                 goExtract SPEC xs (List []) pst1+            PR.Partial n pst1 -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let src0 = takeArrayListRev n (x:getList backBuf)+                    src  = Prelude.reverse src0 ++ xs+                goExtract SPEC src (List []) pst1+            PR.Continue 0 pst1 ->+                goExtract SPEC xs (List (x:getList backBuf)) pst1+            PR.Continue n pst1 -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)+                    src  = Prelude.reverse src0 ++ xs+                goExtract SPEC src (List buf1) pst1+            PR.Done 0 b ->+                return (Right b, D.fromList xs)+            PR.Done n b -> do+                assert+                    (n <= sum (map Array.length (x:getList backBuf)))+                    (return ())+                let src0 = takeArrayListRev n (x:getList backBuf)+                    src = Prelude.reverse src0 ++ xs+                return (Right b, D.fromList src)+            PR.Error err -> do+                let src0 = getList backBuf+                    src = Prelude.reverse src0 ++ x:xs+                return (Left (ParseError err), D.fromList src)++    -- This is a simplified goExtract+    {-# INLINE goStop #-}+    goStop backBuf pst = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "Bug: runArrayParserDBreak: Partial in extract"+            PR.Continue 0 pst1 ->+                goStop backBuf pst1+            PR.Continue n pst1 -> do+                assert+                    (n <= sum (map Array.length (getList backBuf)))+                    (return ())+                let (src0, buf1) = splitAtArrayListRev n (getList backBuf)+                    src = Prelude.reverse src0+                goExtract SPEC src (List buf1) pst1+            PR.Done 0 b -> return (Right b, D.nil)+            PR.Done n b -> do+                assert+                    (n <= sum (map Array.length (getList backBuf)))+                    (return ())+                let src0 = takeArrayListRev n (getList backBuf)+                    src = Prelude.reverse src0+                return (Right b, D.fromList src)+            PR.Error err -> do+                let src0 = getList backBuf+                    src = Prelude.reverse src0+                return (Left (ParseError err), D.fromList src)++{-+-- | Parse an array stream using the supplied 'Parser'.  Returns the parse+-- result and the unconsumed stream. Throws 'ParseError' if the parse fails.+--+-- /Internal/+--+{-# INLINE parseArr #-}+parseArr ::+       (MonadIO m, MonadThrow m, Unbox a)+    => ASF.Parser a m b+    -> Stream m (A.Array a)+    -> m (b, Stream m (A.Array a))+parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)+-}++-- | Fold an array stream using the supplied array stream 'Fold'.+--+-- /Pre-release/+--+{-# INLINE runArrayFold #-}+runArrayFold :: (MonadIO m, Unbox a) =>+    ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b)+runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStream s)++-- | Like 'fold' but also returns the remaining stream.+--+-- /Pre-release/+--+{-# INLINE runArrayFoldBreak #-}+runArrayFoldBreak :: (MonadIO m, Unbox a) =>+    ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b, StreamK m (A.Array a))+runArrayFoldBreak (ChunkFold p) s =+    second fromStream <$> runArrayParserDBreak p (toStream s)++{-# ANN type ParseChunksState Fuse #-}+data ParseChunksState x inpBuf st pst =+      ParseChunksInit inpBuf st+    | ParseChunksInitBuf inpBuf+    | ParseChunksInitLeftOver inpBuf+    | ParseChunksStream st inpBuf !pst+    | ParseChunksStop inpBuf !pst+    | ParseChunksBuf inpBuf st inpBuf !pst+    | ParseChunksExtract inpBuf inpBuf !pst+    | ParseChunksYield x (ParseChunksState x inpBuf st pst)++{-# INLINE_NORMAL runArrayFoldManyD #-}+runArrayFoldManyD+    :: (Monad m, Unbox a)+    => ChunkFold m a b+    -> D.Stream m (Array a)+    -> D.Stream m (Either ParseError b)+runArrayFoldManyD+    (ChunkFold (PRD.Parser pstep initial extract)) (D.Stream step state) =++    D.Stream stepOuter (ParseChunksInit [] state)++    where++    {-# INLINE_LATE stepOuter #-}+    -- Buffer is empty, get the first element from the stream, initialize the+    -- fold and then go to stream processing loop.+    stepOuter gst (ParseChunksInit [] st) = do+        r <- step (adaptState gst) st+        case r of+            D.Yield x s -> do+                res <- initial+                case res of+                    PRD.IPartial ps ->+                        return $ D.Skip $ ParseChunksBuf [x] s [] ps+                    PRD.IDone pb -> do+                        let next = ParseChunksInit [x] s+                        return $ D.Skip $ ParseChunksYield (Right pb) next+                    PRD.IError err -> do+                        let next = ParseChunksInitLeftOver []+                        return+                            $ D.Skip+                            $ ParseChunksYield (Left (ParseError err)) next+            D.Skip s -> return $ D.Skip $ ParseChunksInit [] s+            D.Stop   -> return D.Stop++    -- Buffer is not empty, go to buffered processing loop+    stepOuter _ (ParseChunksInit src st) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ D.Skip $ ParseChunksBuf src st [] ps+            PRD.IDone pb ->+                let next = ParseChunksInit src st+                 in return $ D.Skip $ ParseChunksYield (Right pb) next+            PRD.IError err -> do+                let next = ParseChunksInitLeftOver []+                return+                    $ D.Skip+                    $ ParseChunksYield (Left (ParseError err)) next++    -- This is a simplified ParseChunksInit+    stepOuter _ (ParseChunksInitBuf src) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ D.Skip $ ParseChunksExtract src [] ps+            PRD.IDone pb ->+                let next = ParseChunksInitBuf src+                 in return $ D.Skip $ ParseChunksYield (Right pb) next+            PRD.IError err -> do+                let next = ParseChunksInitLeftOver []+                return+                    $ D.Skip+                    $ ParseChunksYield (Left (ParseError err)) next++    -- XXX we just discard any leftover input at the end+    stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop++    -- Buffer is empty, process elements from the stream+    stepOuter gst (ParseChunksStream st backBuf pst) = do+        r <- step (adaptState gst) st+        case r of+            D.Yield x s -> do+                pRes <- pstep pst x+                case pRes of+                    PR.Partial 0 pst1 ->+                        return $ D.Skip $ ParseChunksStream s [] pst1+                    PR.Partial n pst1 -> do+                        assert+                            (n <= sum (map Array.length (x:backBuf)))+                            (return ())+                        let src0 = takeArrayListRev n (x:backBuf)+                            src  = Prelude.reverse src0+                        return $ D.Skip $ ParseChunksBuf src s [] pst1+                    PR.Continue 0 pst1 ->+                        return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1+                    PR.Continue n pst1 -> do+                        assert+                            (n <= sum (map Array.length (x:backBuf)))+                            (return ())+                        let (src0, buf1) = splitAtArrayListRev n (x:backBuf)+                            src  = Prelude.reverse src0+                        return $ D.Skip $ ParseChunksBuf src s buf1 pst1+                    PR.Done 0 b -> do+                        return $ D.Skip $+                            ParseChunksYield (Right b) (ParseChunksInit [] s)+                    PR.Done n b -> do+                        assert+                            (n <= sum (map Array.length (x:backBuf)))+                            (return ())+                        let src0 = takeArrayListRev n (x:backBuf)+                            src = Prelude.reverse src0+                            next = ParseChunksInit src s+                        return+                            $ D.Skip+                            $ ParseChunksYield (Right b) next+                    PR.Error err -> do+                        let next = ParseChunksInitLeftOver []+                        return+                            $ D.Skip+                            $ ParseChunksYield (Left (ParseError err)) next++            D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst+            D.Stop -> return $ D.Skip $ ParseChunksStop backBuf pst++    -- go back to stream processing mode+    stepOuter _ (ParseChunksBuf [] s buf pst) =+        return $ D.Skip $ ParseChunksStream s buf pst++    -- buffered processing loop+    stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ D.Skip $ ParseChunksBuf xs s [] pst1+            PR.Partial n pst1 -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let src0 = takeArrayListRev n (x:backBuf)+                    src  = Prelude.reverse src0 ++ xs+                return $ D.Skip $ ParseChunksBuf src s [] pst1+            PR.Continue 0 pst1 ->+                return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1+            PR.Continue n pst1 -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let (src0, buf1) = splitAtArrayListRev n (x:backBuf)+                    src  = Prelude.reverse src0 ++ xs+                return $ D.Skip $ ParseChunksBuf src s buf1 pst1+            PR.Done 0 b ->+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInit xs s)+            PR.Done n b -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let src0 = takeArrayListRev n (x:backBuf)+                    src = Prelude.reverse src0 ++ xs+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInit src s)+            PR.Error err -> do+                let next = ParseChunksInitLeftOver []+                return+                    $ D.Skip+                    $ ParseChunksYield (Left (ParseError err)) next++    -- This is a simplified ParseChunksBuf+    stepOuter _ (ParseChunksExtract [] buf pst) =+        return $ D.Skip $ ParseChunksStop buf pst++    stepOuter _ (ParseChunksExtract (x:xs) backBuf pst) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ D.Skip $ ParseChunksExtract xs [] pst1+            PR.Partial n pst1 -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let src0 = takeArrayListRev n (x:backBuf)+                    src  = Prelude.reverse src0 ++ xs+                return $ D.Skip $ ParseChunksExtract src [] pst1+            PR.Continue 0 pst1 ->+                return $ D.Skip $ ParseChunksExtract xs (x:backBuf) pst1+            PR.Continue n pst1 -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let (src0, buf1) = splitAtArrayListRev n (x:backBuf)+                    src  = Prelude.reverse src0 ++ xs+                return $ D.Skip $ ParseChunksExtract src buf1 pst1+            PR.Done 0 b ->+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)+            PR.Done n b -> do+                assert (n <= sum (map Array.length (x:backBuf))) (return ())+                let src0 = takeArrayListRev n (x:backBuf)+                    src = Prelude.reverse src0 ++ xs+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)+            PR.Error err -> do+                let next = ParseChunksInitLeftOver []+                return+                    $ D.Skip+                    $ ParseChunksYield (Left (ParseError err)) next+++    -- This is a simplified ParseChunksExtract+    stepOuter _ (ParseChunksStop backBuf pst) = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "runArrayFoldManyD: Partial in extract"+            PR.Continue 0 pst1 ->+                return $ D.Skip $ ParseChunksStop backBuf pst1+            PR.Continue n pst1 -> do+                assert (n <= sum (map Array.length backBuf)) (return ())+                let (src0, buf1) = splitAtArrayListRev n backBuf+                    src  = Prelude.reverse src0+                return $ D.Skip $ ParseChunksExtract src buf1 pst1+            PR.Done 0 b ->+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitLeftOver [])+            PR.Done n b -> do+                assert (n <= sum (map Array.length backBuf)) (return ())+                let src0 = takeArrayListRev n backBuf+                    src = Prelude.reverse src0+                return+                    $ D.Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)+            PR.Error err -> do+                let next = ParseChunksInitLeftOver []+                return+                    $ D.Skip+                    $ ParseChunksYield (Left (ParseError err)) next++    stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next++-- | Apply an 'ChunkFold' repeatedly on an array stream and emit the+-- fold outputs in the output stream.+--+-- See "Streamly.Data.Stream.foldMany" for more details.+--+-- /Pre-release/+{-# INLINE runArrayFoldMany #-}+runArrayFoldMany+    :: (Monad m, Unbox a)+    => ChunkFold m a b+    -> StreamK m (Array a)+    -> StreamK m (Either ParseError b)+runArrayFoldMany p m = fromStream $ runArrayFoldManyD p (toStream m)
src/Streamly/Internal/Data/Array/Type.hs view
@@ -8,39 +8,50 @@ -- Stability   : experimental -- Portability : GHC ----- See notes in "Streamly.Internal.Data.Array.Mut.Type"+-- See notes in "Streamly.Internal.Data.MutArray.Type" -- module Streamly.Internal.Data.Array.Type     (+    -- ** Type     -- $arrayNotes       Array (..)     , asPtrUnsafe+    , nil -    -- * Freezing and Thawing+    -- ** Freezing and Thawing     , unsafeFreeze     , unsafeFreezeWithShrink     , unsafeThaw -    -- * Pinning and Unpinning+    -- ** Pinning and Unpinning     , pin     , unpin+    , isPinned -    -- * Construction+    -- ** Construction     , splice      , fromList+    , pinnedFromList     , fromListN+    , pinnedFromListN     , fromListRev     , fromListRevN     , fromStreamDN     , fromStreamD+    , fromPureStream+    , fromByteStr# -    -- * Split+    -- ** Split     , breakOn -    -- * Elimination+    -- ** Cloning arrays+    , clone+    , pinnedClone++    -- ** Elimination     , unsafeIndexIO-    , unsafeIndex -- getIndexUnsafe+    , getIndexUnsafe     , byteLength     , length @@ -59,19 +70,27 @@     , readerRev     , toList -    -- * Folds+    -- ** Folds     , writeWith     , writeN+    , pinnedWriteN     , writeNUnsafe+    , pinnedWriteNUnsafe     , MA.ArrayUnsafe (..)-    , writeNAligned+    , pinnedWriteNAligned     , write+    , pinnedWrite+    , unsafeMakePure -    -- * Streams of arrays+    -- ** Streams of arrays     , chunksOf+    , pinnedChunksOf     , bufferChunks     , flattenArrays     , flattenArraysRev++    -- ** Deprecated+    , unsafeIndex     ) where @@ -85,24 +104,25 @@ import Data.Proxy (Proxy(..)) import Data.Word (Word8) import GHC.Base (build)-import GHC.Exts (IsList, IsString(..))+import GHC.Exts (IsList, IsString(..), Addr#)  import GHC.IO (unsafePerformIO) import GHC.Ptr (Ptr(..))-import Streamly.Internal.Data.Array.Mut.Type (MutArray(..), MutableByteArray)+import Streamly.Internal.Data.MutArray.Type (MutArray(..), MutByteArray) import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Unboxed (Unbox, peekWith, sizeOf)+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Unbox (Unbox(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Text.Read (readPrec) -import Prelude hiding (length, foldr, read, unlines, splitAt)+import Prelude hiding (Foldable(..), read, unlines, splitAt)  import qualified GHC.Exts as Exts-import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Unboxed as Unboxed+import qualified Streamly.Internal.Data.MutArray.Type as MA+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Generate as D+import qualified Streamly.Internal.Data.StreamK.Type as K+import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed import qualified Streamly.Internal.Data.Unfold.Type as Unfold import qualified Text.ParserCombinators.ReadPrec as ReadPrec @@ -111,6 +131,24 @@ #include "DocTestDataArray.hs"  -------------------------------------------------------------------------------+-- Notes+-------------------------------------------------------------------------------++-- IMPORTANT:++-- We need to be careful while using unsafePerformIO when array creation is+-- involved.+--+-- * We need to make sure the unsafe IO line does not float out of the binding.+-- * The order of the IO actions should be sane. For example, `touch` after `f`.+--+-- Assume the unsafe IO action floats up. If it makes sense given this+-- assumption, it's probably OK to use usafe IO.+--+-- A general approach should be never to use unsafe IO where Array creation is+-- involved or touch is involved.++------------------------------------------------------------------------------- -- Array Data Type ------------------------------------------------------------------------------- @@ -127,7 +165,7 @@ #endif     -- All offsets are in terms of bytes from the start of arraycontents     Array-    { arrContents :: {-# UNPACK #-} !MutableByteArray+    { arrContents :: {-# UNPACK #-} !MutByteArray     , arrStart :: {-# UNPACK #-} !Int -- offset     , arrEnd   :: {-# UNPACK #-} !Int   -- offset + len     }@@ -144,6 +182,7 @@ -- -- /Pre-release/ --+{-# INLINE asPtrUnsafe #-} asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr) @@ -192,14 +231,23 @@ -- Pinning & Unpinning ------------------------------------------------------------------------------- +-- | Return a copy of the 'Array' in pinned memory if unpinned, else return the+-- original array. {-# INLINE pin #-} pin :: Array a -> IO (Array a) pin = fmap unsafeFreeze . MA.pin . unsafeThaw +-- | Return a copy of the 'Array' in unpinned memory if pinned, else return the+-- original array. {-# INLINE unpin #-} unpin :: Array a -> IO (Array a) unpin = fmap unsafeFreeze . MA.unpin . unsafeThaw +-- | Return 'True' if the array is allocated in pinned memory.+{-# INLINE isPinned #-}+isPinned :: Array a -> Bool+isPinned = MA.isPinned . unsafeThaw+ ------------------------------------------------------------------------------- -- Construction -------------------------------------------------------------------------------@@ -218,6 +266,12 @@ fromListN :: Unbox a => Int -> [a] -> Array a fromListN n xs = unsafePerformIO $ unsafeFreeze <$> MA.fromListN n xs +-- | Like 'fromListN' but creates a pinned array.+{-# INLINABLE pinnedFromListN #-}+pinnedFromListN :: Unbox a => Int -> [a] -> Array a+pinnedFromListN n xs =+    unsafePerformIO $ unsafeFreeze <$> MA.pinnedFromListN n xs+ -- | Create an 'Array' from the first N elements of a list in reverse order. -- The array is allocated to size N, if the list terminates before N elements -- then the array may hold less than N elements.@@ -233,6 +287,11 @@ fromList :: Unbox a => [a] -> Array a fromList xs = unsafePerformIO $ unsafeFreeze <$> MA.fromList xs +-- | Like 'fromList' but creates a pinned array.+{-# INLINE pinnedFromList #-}+pinnedFromList :: Unbox a => [a] -> Array a+pinnedFromList xs = unsafePerformIO $ unsafeFreeze <$> MA.pinnedFromList xs+ -- | Create an 'Array' from a list in reverse order. The list must be of finite -- size. --@@ -252,6 +311,30 @@ fromStreamD str = unsafeFreeze <$> MA.fromStreamD str  -------------------------------------------------------------------------------+-- Cloning+-------------------------------------------------------------------------------++{-# INLINE clone #-}+clone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => Array a -> m (Array a)+clone = fmap unsafeFreeze . MA.clone . unsafeThaw++{-# INLINE pinnedClone #-}+pinnedClone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => Array a -> m (Array a)+pinnedClone = fmap unsafeFreeze . MA.pinnedClone . unsafeThaw++------------------------------------------------------------------------------- -- Streams of arrays ------------------------------------------------------------------------------- @@ -273,6 +356,12 @@     => Int -> D.Stream m a -> D.Stream m (Array a) chunksOf n str = D.map unsafeFreeze $ MA.chunksOf n str +-- | Like 'chunksOf' but creates pinned arrays.+{-# INLINE_NORMAL pinnedChunksOf #-}+pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> D.Stream m (Array a)+pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str+ -- | Use the "read" unfold instead. -- -- @flattenArrays = unfoldMany read@@@ -315,9 +404,14 @@ unsafeIndexIO i arr = MA.getIndexUnsafe i (unsafeThaw arr)  -- | Return element at the specified index without checking the bounds.+{-# INLINE_NORMAL getIndexUnsafe #-}+getIndexUnsafe :: forall a. Unbox a => Int -> Array a -> a+getIndexUnsafe i arr = let !r = unsafeInlineIO $ unsafeIndexIO i arr in r++{-# DEPRECATED unsafeIndex "Please use 'getIndexUnsafe' instead" #-} {-# INLINE_NORMAL unsafeIndex #-} unsafeIndex :: forall a. Unbox a => Int -> Array a -> a-unsafeIndex i arr = let !r = unsafeInlineIO $ unsafeIndexIO i arr in r+unsafeIndex = getIndexUnsafe  -- | /O(1)/ Get the byte length of the array. --@@ -421,8 +515,8 @@         -- accumulating the list and fusing better with the pure consumers.         --         -- This should be safe as the array contents are guaranteed to be-        -- evaluated/written to before we peekWith at them.-        let !x = unsafeInlineIO $ peekWith arrContents p+        -- evaluated/written to before we peekAt at them.+        let !x = unsafeInlineIO $ peekAt p arrContents         in c x (go (INDEX_NEXT(p,a)))  -- | Convert an 'Array' into a list.@@ -442,15 +536,20 @@ writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a) writeN = fmap unsafeFreeze . MA.writeN --- | @writeNAligned alignment n@ folds a maximum of @n@ elements from the input+-- | Like 'fromListN' but creates a pinned array.+{-# INLINE_NORMAL pinnedWriteN #-}+pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)+pinnedWriteN = fmap unsafeFreeze . MA.pinnedWriteN++-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input -- stream to an 'Array' aligned to the given size. -- -- /Pre-release/ ---{-# INLINE_NORMAL writeNAligned #-}-writeNAligned :: forall m a. (MonadIO m, Unbox a)+{-# INLINE_NORMAL pinnedWriteNAligned #-}+pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)     => Int -> Int -> Fold m a (Array a)-writeNAligned alignSize = fmap unsafeFreeze . MA.writeNAligned alignSize+pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize  -- | Like 'writeN' but does not check the array bounds when writing. The fold -- driver must not call the step function more than 'n' times otherwise it will@@ -463,6 +562,11 @@     => Int -> Fold m a (Array a) writeNUnsafe n = unsafeFreeze <$> MA.writeNUnsafe n +{-# INLINE_NORMAL pinnedWriteNUnsafe #-}+pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (Array a)+pinnedWriteNUnsafe n = unsafeFreeze <$> MA.pinnedWriteNUnsafe n+ {-# INLINE_NORMAL writeWith #-} writeWith :: forall m a. (MonadIO m, Unbox a)     => Int -> Fold m a (Array a)@@ -477,6 +581,65 @@ write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a) write = fmap unsafeFreeze MA.write +-- | Like 'write' but creates a pinned array.+{-# INLINE pinnedWrite #-}+pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)+pinnedWrite = fmap unsafeFreeze MA.pinnedWrite++-- | Fold "step" has a dependency on "initial", and each step is dependent on+-- the previous invocation of step due to state passing, finally extract+-- depends on the result of step, therefore, as long as the fold is driven in+-- the correct order the operations would be correctly ordered. We need to+-- ensure that we strictly evaluate the previous step completely before the+-- next step.+--+-- To not share the same array we need to make sure that the result of+-- "initial" is not shared. Existential type ensures that it does not get+-- shared across different folds. However, if we invoke "initial" multiple+-- times for the same fold, there is a possiblity of sharing the two because+-- the compiler would consider it as a pure value. One such example is the+-- chunksOf combinator, or using an array creation fold with foldMany+-- combinator. Is there a proper way in GHC to tell it to not share a pure+-- expression in a particular case?+--+-- For this reason array creation folds have a MonadIO constraint. Pure folds+-- could be unsafe and dangerous. This is dangerous especially when used with+-- foldMany like operations.+--+-- >>> unsafePureWrite = Array.unsafeMakePure Array.write+--+{-# INLINE unsafeMakePure #-}+unsafeMakePure :: Monad m => Fold IO a b -> Fold m a b+unsafeMakePure (Fold step initial extract final) =+    Fold (\x a -> return $! unsafeInlineIO (step x a))+         (return $! unsafePerformIO initial)+         (\s -> return $! unsafeInlineIO $ extract s)+         (\s -> return $! unsafeInlineIO $ final s)++-- | Convert a pure stream in Identity monad to an immutable array.+--+-- Same as the following but with better performance:+--+-- >>> fromPureStream = Array.fromList . runIdentity . Stream.toList+--+fromPureStream :: Unbox a => Stream Identity a -> Array a+fromPureStream x = unsafePerformIO $ fmap (unsafeFreeze) (MA.fromPureStream x)+-- fromPureStream = runIdentity . D.fold (unsafeMakePure write)+-- fromPureStream = fromList . runIdentity . D.toList++-- | Copy a null terminated immutable 'Addr#' Word8 sequence into an array.+--+-- /Unsafe:/ The caller is responsible for safe addressing.+--+-- Note that this is completely safe when reading from Haskell string+-- literals because they are guaranteed to be NULL terminated:+--+-- >>> Array.toList $ Array.fromByteStr# "\1\2\3\0"#+-- [1,2,3]+--+fromByteStr# :: Addr# -> Array Word8+fromByteStr# addr = fromPureStream (D.fromByteStr# addr)+ ------------------------------------------------------------------------------- -- Instances -------------------------------------------------------------------------------@@ -554,7 +717,7 @@     {-# INLINE_LATE step #-}     step _ p | p == arrEnd = return D.Stop     step _ p = liftIO $ do-        x <- peekWith arrContents p+        x <- peekAt p arrContents         return $ D.Yield x (p + size)  {-
+ src/Streamly/Internal/Data/Binary/Parser.hs view
@@ -0,0 +1,399 @@+-- |+-- Module      : Streamly.Internal.Data.Binary.Parser+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--+-- Decode Haskell data types from byte streams.+--+-- It would be inefficient to use this to compose parsers for general algebraic+-- data types. For general deserialization of ADTs please use the Serialize+-- type class instances. The fastest way to deserialize byte streams+-- representing Haskell data types is to write them to arrays and deserialize+-- the array using the Serialize type class.++module Streamly.Internal.Data.Binary.Parser+    (+    -- * Type class+      FromBytes (..)++    -- * Decoders+    , unit+    , bool+    , ordering+    , eqWord8 -- XXX rename to word8Eq+    , word8+    , word16be+    , word16le+    , word32be+    , word32le+    , word64be+    , word64le+    , word64host+    , int8+    , int16be+    , int16le+    , int32be+    , int32le+    , int64be+    , int64le+    , float32be+    , float32le+    , double64be+    , double64le+    , charLatin1+    )+where++import Control.Monad.IO.Class (MonadIO)+import Data.Bits ((.|.), unsafeShiftL)+import Data.Char (chr)+import Data.Int (Int8, Int16, Int32, Int64)+import GHC.Float (castWord32ToFloat, castWord64ToDouble)+import Data.Word (Word8, Word16, Word32, Word64)+import Streamly.Internal.Data.Parser (Parser)+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))+import Streamly.Internal.Data.Tuple.Strict (Tuple' (..))+import qualified Streamly.Data.Array as A+import qualified Streamly.Internal.Data.Array as A+    (getIndexUnsafe, castUnsafe)+import qualified Streamly.Internal.Data.Parser as PR+    (fromPure, either, satisfy, takeEQ)+import qualified Streamly.Internal.Data.Parser as PRD+    (Parser(..), Initial(..), Step(..))++-- Note: The () type does not need to have an on-disk representation in theory.+-- But we use a concrete representation for it so that we count how many ()+-- types we have. Or when we have an array of units the array a concrete+-- length.++-- | A value of type '()' is encoded as @0@ in binary encoding.+--+-- @+-- 0 ==> ()+-- @+--+-- /Pre-release/+--+{-# INLINE unit #-}+unit :: Monad m => Parser Word8 m ()+unit = eqWord8 0 *> PR.fromPure ()++{-# INLINE word8ToBool #-}+word8ToBool :: Word8 -> Either String Bool+word8ToBool 0 = Right False+word8ToBool 1 = Right True+word8ToBool w = Left ("Invalid Bool encoding " ++ Prelude.show w)++-- | A value of type 'Bool' is encoded as follows in binary encoding.+--+-- @+-- 0 ==> False+-- 1 ==> True+-- @+--+-- /Pre-release/+--+{-# INLINE bool #-}+bool :: Monad m => Parser Word8 m Bool+bool = PR.either word8ToBool++{-# INLINE word8ToOrdering #-}+word8ToOrdering :: Word8 -> Either String Ordering+word8ToOrdering 0 = Right LT+word8ToOrdering 1 = Right EQ+word8ToOrdering 2 = Right GT+word8ToOrdering w = Left ("Invalid Ordering encoding " ++ Prelude.show w)++-- | A value of type 'Ordering' is encoded as follows in binary encoding.+--+-- @+-- 0 ==> LT+-- 1 ==> EQ+-- 2 ==> GT+-- @+--+-- /Pre-release/+--+{-# INLINE ordering #-}+ordering :: Monad m => Parser Word8 m Ordering+ordering = PR.either word8ToOrdering++-- XXX should go in a Word8 parser module?+-- | Accept the input byte only if it is equal to the specified value.+--+-- /Pre-release/+--+{-# INLINE eqWord8 #-}+eqWord8 :: Monad m => Word8 -> Parser Word8 m Word8+eqWord8 b = PR.satisfy (== b)++-- | Accept any byte.+--+-- /Pre-release/+--+{-# INLINE word8 #-}+word8 :: Monad m => Parser Word8 m Word8+word8 = PR.satisfy (const True)++-- | Big endian (MSB first) Word16+{-# INLINE word16beD #-}+word16beD :: Monad m => PRD.Parser Word8 m Word16+word16beD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial Nothing'++    step Nothing' a =+        -- XXX We can use a non-failing parser or a fold so that we do not+        -- have to buffer for backtracking which is inefficient.+        return $ PRD.Continue 0 (Just' (fromIntegral a `unsafeShiftL` 8))+    step (Just' w) a =+        return $ PRD.Done 0 (w .|. fromIntegral a)++    extract _ = return $ PRD.Error "word16be: end of input"++-- | Parse two bytes as a 'Word16', the first byte is the MSB of the Word16 and+-- second byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word16be #-}+word16be :: Monad m => Parser Word8 m Word16+word16be = word16beD++-- | Little endian (LSB first) Word16+{-# INLINE word16leD #-}+word16leD :: Monad m => PRD.Parser Word8 m Word16+word16leD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial Nothing'++    step Nothing' a =+        return $ PRD.Continue 0 (Just' (fromIntegral a))+    step (Just' w) a =+        return $ PRD.Done 0 (w .|. fromIntegral a `unsafeShiftL` 8)++    extract _ = return $ PRD.Error "word16le: end of input"++-- | Parse two bytes as a 'Word16', the first byte is the LSB of the Word16 and+-- second byte is the MSB (little endian representation).+--+-- /Pre-release/+--+{-# INLINE word16le #-}+word16le :: Monad m => Parser Word8 m Word16+word16le = word16leD++-- | Big endian (MSB first) Word32+{-# INLINE word32beD #-}+word32beD :: Monad m => PRD.Parser Word8 m Word32+word32beD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial $ Tuple' 0 24++    step (Tuple' w sh) a = return $+        if sh /= 0+        then+            let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)+             in PRD.Continue 0 (Tuple' w1 (sh - 8))+        else PRD.Done 0 (w .|. fromIntegral a)++    extract _ = return $ PRD.Error "word32beD: end of input"++-- | Parse four bytes as a 'Word32', the first byte is the MSB of the Word32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word32be #-}+word32be :: Monad m => Parser Word8 m Word32+word32be = word32beD++-- | Little endian (LSB first) Word32+{-# INLINE word32leD #-}+word32leD :: Monad m => PRD.Parser Word8 m Word32+word32leD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial $ Tuple' 0 0++    step (Tuple' w sh) a = return $+        let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)+         in if sh /= 24+            then PRD.Continue 0 (Tuple' w1 (sh + 8))+            else PRD.Done 0 w1++    extract _ = return $ PRD.Error "word32leD: end of input"++-- | Parse four bytes as a 'Word32', the first byte is the MSB of the Word32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word32le #-}+word32le :: Monad m => Parser Word8 m Word32+word32le = word32leD++-- | Big endian (MSB first) Word64+{-# INLINE word64beD #-}+word64beD :: Monad m => PRD.Parser Word8 m Word64+word64beD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial $ Tuple' 0 56++    step (Tuple' w sh) a = return $+        if sh /= 0+        then+            let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)+             in PRD.Continue 0 (Tuple' w1 (sh - 8))+        else PRD.Done 0 (w .|. fromIntegral a)++    extract _ = return $ PRD.Error "word64beD: end of input"++-- | Parse eight bytes as a 'Word64', the first byte is the MSB of the Word64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word64be #-}+word64be :: Monad m => Parser Word8 m Word64+word64be = word64beD++-- | Little endian (LSB first) Word64+{-# INLINE word64leD #-}+word64leD :: Monad m => PRD.Parser Word8 m Word64+word64leD = PRD.Parser step initial extract++    where++    initial = return $ PRD.IPartial $ Tuple' 0 0++    step (Tuple' w sh) a = return $+        let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)+         in if sh /= 56+            then PRD.Continue 0 (Tuple' w1 (sh + 8))+            else PRD.Done 0 w1++    extract _ = return $ PRD.Error "word64leD: end of input"++-- | Parse eight bytes as a 'Word64', the first byte is the MSB of the Word64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word64le #-}+word64le :: Monad m => Parser Word8 m Word64+word64le = word64leD++{-# INLINE int8 #-}+int8 :: Monad m => Parser Word8 m Int8+int8 = fromIntegral <$> word8++-- | Parse two bytes as a 'Int16', the first byte is the MSB of the Int16 and+-- second byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int16be #-}+int16be :: Monad m => Parser Word8 m Int16+int16be = fromIntegral <$> word16be++-- | Parse two bytes as a 'Int16', the first byte is the LSB of the Int16 and+-- second byte is the MSB (little endian representation).+--+-- /Pre-release/+--+{-# INLINE int16le #-}+int16le :: Monad m => Parser Word8 m Int16+int16le = fromIntegral <$> word16le++-- | Parse four bytes as a 'Int32', the first byte is the MSB of the Int32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int32be #-}+int32be :: Monad m => Parser Word8 m Int32+int32be = fromIntegral <$> word32be++-- | Parse four bytes as a 'Int32', the first byte is the MSB of the Int32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int32le #-}+int32le :: Monad m => Parser Word8 m Int32+int32le = fromIntegral <$> word32le++-- | Parse eight bytes as a 'Int64', the first byte is the MSB of the Int64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int64be #-}+int64be :: Monad m => Parser Word8 m Int64+int64be = fromIntegral <$> word64be++-- | Parse eight bytes as a 'Int64', the first byte is the MSB of the Int64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int64le #-}+int64le :: Monad m => Parser Word8 m Int64+int64le = fromIntegral <$> word64le++{-# INLINE float32be #-}+float32be :: MonadIO m => Parser Word8 m Float+float32be = castWord32ToFloat <$> word32be++{-# INLINE float32le #-}+float32le :: MonadIO m => Parser Word8 m Float+float32le = castWord32ToFloat <$> word32le++{-# INLINE double64be #-}+double64be :: MonadIO m => Parser Word8 m Double+double64be =  castWord64ToDouble <$> word64be++{-# INLINE double64le #-}+double64le :: MonadIO m => Parser Word8 m Double+double64le = castWord64ToDouble <$> word64le++-- | Accept any byte.+--+-- /Pre-release/+--+{-# INLINE charLatin1 #-}+charLatin1 :: Monad m => Parser Word8 m Char+charLatin1 = fmap (chr . fromIntegral) word8++-------------------------------------------------------------------------------+-- Host byte order+-------------------------------------------------------------------------------++-- | Parse eight bytes as a 'Word64' in the host byte order.+--+-- /Pre-release/+--+{-# INLINE word64host #-}+word64host :: MonadIO m => Parser Word8 m Word64+word64host =+    fmap (A.getIndexUnsafe 0 . A.castUnsafe) $ PR.takeEQ 8 (A.writeN 8)++-------------------------------------------------------------------------------+-- Type class+-------------------------------------------------------------------------------++class FromBytes a where+    -- | Decode a byte stream to a Haskell type.+    fromBytes :: Parser Word8 m a
+ src/Streamly/Internal/Data/Binary/Stream.hs view
@@ -0,0 +1,383 @@+-- |+-- Module      : Streamly.Internal.Data.Binary.Stream+-- Copyright   : (c) 2022 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--+-- Encode Haskell data types to byte streams.+--+-- The primary purpose of this module is to serialize primitive Haskell types+-- to streams for convenient byte by byte processing when such a need arises.+--+-- It would be inefficient to use this to build byte streams from algebraic+-- data types. For general serialization of ADTs please use the Serialize type+-- class instances. The fastest way to convert general Haskell types to byte+-- streams is to serialize them to an array and then stream the array.++-- XXX remove unit, bool, ordering, and the type class as well++module Streamly.Internal.Data.Binary.Stream+    (+    -- * Type class+      ToBytes (..)++    -- * Encoders+    , unit+    , bool+    , ordering+    , word8+    , word16be+    , word16le+    , word32be+    , word32le+    , word64be+    , word64le+    , word64host+    , int8+    , int16be+    , int16le+    , int32be+    , int32le+    , int64be+    , int64le+    , float32be+    , float32le+    , double64be+    , double64le+    , charLatin1+    , charUtf8+    )+where++#include "MachDeps.h"++import Data.Bits (shiftR)+import Data.Char (ord)+import Data.Int (Int8, Int16, Int32, Int64)+import Data.Word (Word8, Word16, Word32, Word64)+import GHC.Float (castDoubleToWord64, castFloatToWord32)+import Streamly.Internal.Data.Stream (Stream)+import Streamly.Internal.Data.Stream (Step(..))+import Streamly.Internal.Unicode.Stream (readCharUtf8)++import qualified Streamly.Internal.Data.Stream as Stream+import qualified Streamly.Internal.Data.Stream as D++-- XXX Use StreamD directly?++-- | A value of type '()' is encoded as @0@ in binary encoding.+--+-- @+-- 0 ==> ()+-- @+--+-- /Pre-release/+--+{-# INLINE unit #-}+unit :: Applicative m => Stream m Word8+unit = Stream.fromPure 0++{-# INLINE boolToWord8 #-}+boolToWord8 :: Bool -> Word8+boolToWord8 False = 0+boolToWord8 True = 1++-- | A value of type 'Bool' is encoded as follows in binary encoding.+--+-- @+-- 0 ==> False+-- 1 ==> True+-- @+--+-- /Pre-release/+--+{-# INLINE bool #-}+bool :: Applicative m => Bool -> Stream m Word8+bool = Stream.fromPure . boolToWord8++{-# INLINE orderingToWord8 #-}+orderingToWord8 :: Ordering -> Word8+orderingToWord8 LT = 0+orderingToWord8 EQ = 1+orderingToWord8 GT = 2++-- | A value of type 'Ordering' is encoded as follows in binary encoding.+--+-- @+-- 0 ==> LT+-- 1 ==> EQ+-- 2 ==> GT+-- @+--+-- /Pre-release/+--+{-# INLINE ordering #-}+ordering :: Applicative m => Ordering -> Stream m Word8+ordering = Stream.fromPure . orderingToWord8++-- | Stream a 'Word8'.+--+-- /Pre-release/+--+{-# INLINE word8 #-}+word8 :: Applicative m => Word8 -> Stream m Word8+word8 = Stream.fromPure++data W16State = W16B1 | W16B2 | W16Done++{-# INLINE word16beD #-}+word16beD :: Applicative m => Word16 -> D.Stream m Word8+word16beD w = D.Stream step W16B1++    where++    step _ W16B1 = pure $ Yield (fromIntegral (shiftR w 8) :: Word8) W16B2+    step _ W16B2 = pure $ Yield (fromIntegral w :: Word8) W16Done+    step _ W16Done = pure Stop++-- | Stream a 'Word16' as two bytes, the first byte is the MSB of the Word16+-- and second byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word16be #-}+word16be :: Monad m => Word16 -> Stream m Word8+word16be = word16beD++-- | Little endian (LSB first) Word16+{-# INLINE word16leD #-}+word16leD :: Applicative m => Word16 -> D.Stream m Word8+word16leD w = D.Stream step W16B1++    where++    step _ W16B1 = pure $ Yield (fromIntegral w :: Word8) W16B2+    step _ W16B2 = pure $ Yield (fromIntegral (shiftR w 8) :: Word8) W16Done+    step _ W16Done = pure Stop++-- | Stream a 'Word16' as two bytes, the first byte is the LSB of the Word16+-- and second byte is the MSB (little endian representation).+--+-- /Pre-release/+--+{-# INLINE word16le #-}+word16le :: Monad m => Word16 -> Stream m Word8+word16le = word16leD++data W32State = W32B1 | W32B2 | W32B3 | W32B4 | W32Done++-- | Big endian (MSB first) Word32+{-# INLINE word32beD #-}+word32beD :: Applicative m => Word32 -> D.Stream m Word8+word32beD w = D.Stream step W32B1++    where++    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s++    step _ W32B1 = yield 24 W32B2+    step _ W32B2 = yield 16 W32B3+    step _ W32B3 = yield 8 W32B4+    step _ W32B4 = pure $ Yield (fromIntegral w :: Word8) W32Done+    step _ W32Done = pure Stop++-- | Stream a 'Word32' as four bytes, the first byte is the MSB of the Word32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word32be #-}+word32be :: Monad m => Word32 -> Stream m Word8+word32be = word32beD++-- | Little endian (LSB first) Word32+{-# INLINE word32leD #-}+word32leD :: Applicative m => Word32 -> D.Stream m Word8+word32leD w = D.Stream step W32B1++    where++    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s++    step _ W32B1 = pure $ Yield (fromIntegral w :: Word8) W32B2+    step _ W32B2 = yield 8 W32B3+    step _ W32B3 = yield 16 W32B4+    step _ W32B4 = yield 24 W32Done+    step _ W32Done = pure Stop++-- | Stream a 'Word32' as four bytes, the first byte is the MSB of the Word32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word32le #-}+word32le :: Monad m => Word32 -> Stream m Word8+word32le = word32leD++data W64State =+    W64B1 | W64B2 | W64B3 | W64B4 | W64B5 | W64B6 | W64B7 | W64B8 | W64Done++-- | Big endian (MSB first) Word64+{-# INLINE word64beD #-}+word64beD :: Applicative m => Word64 -> D.Stream m Word8+word64beD w = D.Stream step W64B1++    where++    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s++    step _ W64B1 = yield 56 W64B2+    step _ W64B2 = yield 48 W64B3+    step _ W64B3 = yield 40 W64B4+    step _ W64B4 = yield 32 W64B5+    step _ W64B5 = yield 24 W64B6+    step _ W64B6 = yield 16 W64B7+    step _ W64B7 = yield  8 W64B8+    step _ W64B8 = pure $ Yield (fromIntegral w :: Word8) W64Done+    step _ W64Done = pure Stop++-- | Stream a 'Word64' as eight bytes, the first byte is the MSB of the Word64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word64be #-}+word64be :: Monad m => Word64 -> Stream m Word8+word64be = word64beD++-- | Little endian (LSB first) Word64+{-# INLINE word64leD #-}+word64leD :: Applicative m => Word64 -> D.Stream m Word8+word64leD w = D.Stream step W64B1++    where++    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s++    step _ W64B1 = pure $ Yield (fromIntegral w :: Word8) W64B2+    step _ W64B2 = yield  8 W64B3+    step _ W64B3 = yield 16 W64B4+    step _ W64B4 = yield 24 W64B5+    step _ W64B5 = yield 32 W64B6+    step _ W64B6 = yield 40 W64B7+    step _ W64B7 = yield 48 W64B8+    step _ W64B8 = yield 56 W64Done+    step _ W64Done = pure Stop++-- | Stream a 'Word64' as eight bytes, the first byte is the MSB of the Word64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE word64le #-}+word64le :: Monad m => Word64 -> Stream m Word8+word64le = word64leD++{-# INLINE int8 #-}+int8 :: Applicative m => Int8 -> Stream m Word8+int8 i = word8 (fromIntegral i :: Word8)++-- | Stream a 'Int16' as two bytes, the first byte is the MSB of the Int16+-- and second byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int16be #-}+int16be :: Monad m => Int16 -> Stream m Word8+int16be i = word16be (fromIntegral i :: Word16)++-- | Stream a 'Int16' as two bytes, the first byte is the LSB of the Int16+-- and second byte is the MSB (little endian representation).+--+-- /Pre-release/+--+{-# INLINE int16le #-}+int16le :: Monad m => Int16 -> Stream m Word8+int16le i = word16le (fromIntegral i :: Word16)++-- | Stream a 'Int32' as four bytes, the first byte is the MSB of the Int32+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int32be #-}+int32be :: Monad m => Int32 -> Stream m Word8+int32be i = word32be (fromIntegral i :: Word32)++{-# INLINE int32le #-}+int32le :: Monad m => Int32 -> Stream m Word8+int32le i = word32le (fromIntegral i :: Word32)++-- | Stream a 'Int64' as eight bytes, the first byte is the MSB of the Int64+-- and last byte is the LSB (big endian representation).+--+-- /Pre-release/+--+{-# INLINE int64be #-}+int64be :: Monad m => Int64 -> Stream m Word8+int64be i = word64be (fromIntegral i :: Word64)++-- | Stream a 'Int64' as eight bytes, the first byte is the LSB of the Int64+-- and last byte is the MSB (little endian representation).+--+-- /Pre-release/+--+{-# INLINE int64le #-}+int64le :: Monad m => Int64 -> Stream m Word8+int64le i = word64le (fromIntegral i :: Word64)++-- | Big endian (MSB first) Float+{-# INLINE float32be #-}+float32be :: Monad m => Float -> Stream m Word8+float32be = word32beD . castFloatToWord32++-- | Little endian (LSB first) Float+{-# INLINE float32le #-}+float32le :: Monad m => Float -> Stream m Word8+float32le = word32leD . castFloatToWord32++-- | Big endian (MSB first) Double+{-# INLINE double64be #-}+double64be :: Monad m => Double -> Stream m Word8+double64be = word64beD . castDoubleToWord64++-- | Little endian (LSB first) Double+{-# INLINE double64le #-}+double64le :: Monad m => Double -> Stream m Word8+double64le = word64leD . castDoubleToWord64++-- | Encode a Unicode character to stream of bytes in 0-255 range.+--+{-# INLINE charLatin1 #-}+charLatin1 :: Applicative m => Char -> Stream m Word8+charLatin1 = Stream.fromPure . fromIntegral . ord++{-# INLINE charUtf8 #-}+charUtf8 :: Monad m => Char -> Stream m Word8+charUtf8 = Stream.unfold readCharUtf8++-------------------------------------------------------------------------------+-- Host byte order+-------------------------------------------------------------------------------++-- | Stream a 'Word64' as eight bytes in the host byte order.+--+-- /Pre-release/+--+{-# INLINE word64host #-}+word64host :: Monad m => Word64 -> Stream m Word8+word64host =+#ifdef WORDS_BIGENDIAN+    word64be+#else+    word64le+#endif++-------------------------------------------------------------------------------+-- Type class+-------------------------------------------------------------------------------++class ToBytes a where+    -- | Convert a Haskell type to a byte stream.+    toBytes :: a -> Stream m Word8
src/Streamly/Internal/Data/Builder.hs view
@@ -16,7 +16,9 @@     ) where +#if !MIN_VERSION_base(4,18,0) import Control.Applicative (liftA2)+#endif  ------------------------------------------------------------------------------ -- The Builder type@@ -27,16 +29,16 @@ -- or even a Fold. Unlike fold the step function is one-shot and not called in -- a loop. newtype Builder s m a =-  Builder (s -> m (s, a))+  Builder (s -> m (a, s))  -- | Maps a function on the output of the fold (the type @b@). instance Functor m => Functor (Builder s m) where     {-# INLINE fmap #-}-    fmap f (Builder step1) = Builder (fmap (fmap f) . step1)+    fmap f (Builder step1) = Builder (fmap (\ (a, s) -> (f a, s)) . step1)  {-# INLINE fromPure #-} fromPure :: Applicative m => b -> Builder s m b-fromPure b = Builder (\s -> pure (s, b))+fromPure b = Builder (\s -> pure (b, s))  -- | Chain the actions and zip the outputs. {-# INLINE sequenceWith #-}@@ -47,9 +49,9 @@     where      step s = do-        (s1, x) <- stepL s-        (s2, y) <- stepR s1-        pure (s2, func x y)+        (x, s1) <- stepL s+        (y, s2) <- stepR s1+        pure (func x y, s2)  instance Monad m => Applicative (Builder a m) where     {-# INLINE pure #-}@@ -74,7 +76,7 @@         where          step s = do-            (s1, x) <- stepL s+            (x, s1) <- stepL s             let Builder stepR = f x-            (s2, y) <- stepR s1-            pure (s2, y)+            (y, s2) <- stepR s1+            pure (y, s2)
src/Streamly/Internal/Data/Fold.hs view
@@ -16,2583 +16,18 @@     -- * Imports     -- $setup -    -- * Fold Type-      Step (..)-    , Fold (..)-    , Tee (..)--    -- * Constructors-    -- | Which constructor to use?-    ---    -- * @foldl*@: If the fold never terminates i.e. does not use the 'Done'-    -- constructor otherwise use the @foldt*@ variants.-    -- * @*M@: Use the @M@ suffix variants if any of the step, initial, or-    -- extract function is monadic, otherwise use the pure variants.-    ---    , foldl'-    , foldlM'-    , foldl1'-    , foldlM1'-    , foldt'-    , foldtM'-    , foldr'-    , foldrM'--    -- * Mappers-    -- | Monadic functions useful with mapM/lmapM on folds or streams.-    , tracing-    , trace--    -- * Folds--    -- ** Accumulators-    -- *** Semigroups and Monoids-    , sconcat-    , mconcat-    , foldMap-    , foldMapM--    -- *** Reducers-    , drain-    , drainMapM-    , the-    , length-    , lengthGeneric-    , mean-    , rollingHash-    , defaultSalt-    , rollingHashWithSalt-    , rollingHashFirstN-    -- , rollingHashLastN--    -- *** Saturating Reducers-    -- | 'product' terminates if it becomes 0. Other folds can theoretically-    -- saturate on bounded types, and therefore terminate, however, they will-    -- run forever on unbounded types like Integer/Double.-    , sum-    , product-    , maximumBy-    , maximum-    , minimumBy-    , minimum--    -- *** Collectors-    -- | Avoid using these folds in scalable or performance critical-    -- applications, they buffer all the input in GC memory which can be-    -- detrimental to performance if the input is large.-    , toList-    , toListRev-    -- $toListRev-    , toStream-    , toStreamRev-    , toStreamK-    , toStreamKRev-    , topBy-    , top-    , bottomBy-    , bottom--    -- *** Scanners-    -- | Stateful transformation of the elements. Useful in combination with-    -- the 'scanMaybe' combinator. For scanners the result of the fold is-    -- usually a transformation of the current element rather than an-    -- aggregation of all elements till now.-    , latest- -- , nthLast -- using Ring array-    , indexingWith-    , indexing-    , indexingRev-    , rollingMapM--    -- *** Filters-    -- | Useful in combination with the 'scanMaybe' combinator.-    , filtering-    , deleteBy-    , uniqBy-    , uniq-    , repeated-    , findIndices-    , elemIndices--    -- ** Terminating Folds-    -- *** Empty folds-    -- | Folds that return a result without consuming any input.-    , fromPure-    , fromEffect-    , fromRefold--    -- *** Singleton folds-    -- | Folds that terminate after consuming exactly one input element. All-    -- these can be implemented in terms of the 'maybe' fold.-    , one-    , null -- XXX not very useful and could be problematic, remove it?-    , satisfy-    , maybe--    -- *** Multi folds-    -- | Terminate after consuming one or more elements.-    , drainN-    -- , lastN-    -- , (!!)-    , indexGeneric-    , index-    , findM-    , find-    , lookup-    , findIndex-    , elemIndex-    , elem-    , notElem-    , all-    , any-    , and-    , or--    -- ** Trimmers-    -- | Useful in combination with the 'scanMaybe' combinator.-    , taking-    , dropping-    , takingEndByM-    , takingEndBy-    , takingEndByM_-    , takingEndBy_-    , droppingWhileM-    , droppingWhile-    , prune--    -- * Running A Fold-    , drive-    -- , breakStream--    -- * Building Incrementally-    , extractM-    , reduce-    , close-    , isClosed-    , snoc-    , snocl-    , snocM-    , snoclM--    , addOne-    , addStream--    -- * Combinators-    -- ** Utilities-    , with--    -- ** Transforming the Monad-    , morphInner-    , generalizeInner--    -- ** Mapping on output-    , rmapM--    -- ** Mapping on Input-    , transform-    , lmap-    --, lsequence-    , lmapM--    -- ** Sliding Window-    , slide2--    -- ** Scanning Input-    , scan-    , scanMany-    , postscan-    , indexed--    -- ** Zipping Input-    , zipStreamWithM-    , zipStream--    -- ** Filtering Input-    , catMaybes-    , mapMaybeM-    , mapMaybe-    , scanMaybe-    , filter-    , filterM-    , sampleFromthen--    -- Either streams-    , catLefts-    , catRights-    , catEithers--    {--    -- ** Insertion-    -- | Insertion adds more elements to the stream.--    , insertBy-    , intersperseM--    -- ** Reordering-    , reverse-    -}--    -- ** Trimming-    , take--    -- By elements-    , takeEndBy-    , takeEndBy_-    , takeEndBySeq-    , takeEndBySeq_-    {--    , drop-    , dropWhile-    , dropWhileM-    -}--    -- ** Serial Append-    , splitWith-    , split_-    -- , tail-    -- , init-    , splitAt -- spanN-    -- , splitIn -- sessionN--    -- ** Parallel Distribution-    , teeWith-    , tee-    , teeWithFst-    , teeWithMin-    , distribute-    -- , distributeFst-    -- , distributeMin--    -- ** Unzipping-    , unzip-    -- These two can be expressed using lmap/lmapM and unzip-    , unzipWith-    , unzipWithM-    , unzipWithFstM-    , unzipWithMinM--    -- ** Parallel Alternative-    , shortest-    , longest--    -- ** Partitioning-    , partitionByM-    , partitionByFstM-    , partitionByMinM-    , partitionBy-    , partition--    -- ** Splitting-    , many-    , manyPost-    , groupsOf-    , chunksBetween-    , refoldMany-    , refoldMany1-    , intersperseWithQuotes--    -- ** Nesting-    , unfoldMany-    , concatSequence-    , concatMap-    , duplicate-    , refold--    -- * Deprecated-    , foldr-    , drainBy-    , last-    , head-    , sequence-    , mapM-    , variance-    , stdDev-    , serialWith-    )-where--#include "inline.hs"-#include "ArrayMacros.h"--import Control.Monad (void)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Bifunctor (first)-import Data.Bits (shiftL, shiftR, (.|.), (.&.))-import Data.Either (isLeft, isRight, fromLeft, fromRight)-import Data.Int (Int64)-import Data.Proxy (Proxy(..))-import Data.Word (Word32)-import Foreign.Storable (Storable, peek)-import Streamly.Internal.Data.Array.Mut.Type (MutArray(..))-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)-import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))-import Streamly.Internal.Data.Unboxed (Unbox, sizeOf)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)--import qualified Prelude-import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Array.Type as Array-import qualified Streamly.Internal.Data.Fold.Window as FoldW-import qualified Streamly.Internal.Data.Pipe.Type as Pipe-import qualified Streamly.Internal.Data.Ring.Unboxed as Ring-import qualified Streamly.Internal.Data.Stream.StreamD.Type as StreamD--import Prelude hiding-       ( filter, foldl1, drop, dropWhile, take, takeWhile, zipWith-       , foldl, foldr, map, mapM_, sequence, all, any, sum, product, elem-       , notElem, maximum, minimum, head, last, tail, length, null-       , reverse, iterate, init, and, or, lookup, (!!)-       , scanl, scanl1, replicate, concatMap, mconcat, foldMap, unzip-       , span, splitAt, break, mapM, zip, maybe)-import Streamly.Internal.Data.Fold.Type-import Streamly.Internal.Data.Fold.Tee--#include "DocTestDataFold.hs"----------------------------------------------------------------------------------- Running----------------------------------------------------------------------------------- | Drive a fold using the supplied 'Stream', reducing the resulting--- expression strictly at each step.------ Definition:------ >>> drive = flip Stream.fold------ Example:------ >>> Fold.drive (Stream.enumerateFromTo 1 100) Fold.sum--- 5050----{-# INLINE drive #-}-drive :: Monad m => Stream m a -> Fold m a b -> m b-drive = flip StreamD.fold--{---- | Like 'drive' but also returns the remaining stream. The resulting stream--- would be 'Stream.nil' if the stream finished before the fold.------ Definition:------ >>> breakStream = flip Stream.foldBreak------ /CPS/----{-# INLINE breakStreamK #-}-breakStreamK :: Monad m => StreamK m a -> Fold m a b -> m (b, StreamK m a)-breakStreamK strm fl = fmap f $ K.foldBreak fl (Stream.toStreamK strm)--    where--    f (b, str) = (b, Stream.fromStreamK str)--}---- | Append a stream to a fold to build the fold accumulator incrementally. We--- can repeatedly call 'addStream' on the same fold to continue building the--- fold and finally use 'drive' to finish the fold and extract the result. Also--- see the 'Streamly.Data.Fold.addOne' operation which is a singleton version--- of 'addStream'.------ Definitions:------ >>> addStream stream = Fold.drive stream . Fold.duplicate------ Example, build a list incrementally:------ >>> :{--- pure (Fold.toList :: Fold IO Int [Int])---     >>= Fold.addOne 1---     >>= Fold.addStream (Stream.enumerateFromTo 2 4)---     >>= Fold.drive Stream.nil---     >>= print--- :}--- [1,2,3,4]------ This can be used as an O(n) list append compared to the O(n^2) @++@ when--- used for incrementally building a list.------ Example, build a stream incrementally:------ >>> :{--- pure (Fold.toStream :: Fold IO Int (Stream Identity Int))---     >>= Fold.addOne 1---     >>= Fold.addStream (Stream.enumerateFromTo 2 4)---     >>= Fold.drive Stream.nil---     >>= print--- :}--- fromList [1,2,3,4]------ This can be used as an O(n) stream append compared to the O(n^2) @<>@ when--- used for incrementally building a stream.------ Example, build an array incrementally:------ >>> :{--- pure (Array.write :: Fold IO Int (Array Int))---     >>= Fold.addOne 1---     >>= Fold.addStream (Stream.enumerateFromTo 2 4)---     >>= Fold.drive Stream.nil---     >>= print--- :}--- fromList [1,2,3,4]------ Example, build an array stream incrementally:------ >>> :{--- let f :: Fold IO Int (Stream Identity (Array Int))---     f = Fold.groupsOf 2 (Array.writeN 3) Fold.toStream--- in pure f---     >>= Fold.addOne 1---     >>= Fold.addStream (Stream.enumerateFromTo 2 4)---     >>= Fold.drive Stream.nil---     >>= print--- :}--- fromList [fromList [1,2],fromList [3,4]]----addStream :: Monad m => Stream m a -> Fold m a b -> m (Fold m a b)-addStream stream = drive stream . duplicate----------------------------------------------------------------------------------- Transformations on fold inputs----------------------------------------------------------------------------------- | Flatten the monadic output of a fold to pure output.----{-# DEPRECATED sequence "Use \"rmapM id\" instead" #-}-{-# INLINE sequence #-}-sequence :: Monad m => Fold m a (m b) -> Fold m a b-sequence = rmapM id---- | Map a monadic function on the output of a fold.----{-# DEPRECATED mapM "Use rmapM instead" #-}-{-# INLINE mapM #-}-mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c-mapM = rmapM---- |--- >>> mapMaybeM f = Fold.lmapM f . Fold.catMaybes----{-# INLINE mapMaybeM #-}-mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Fold m b r -> Fold m a r-mapMaybeM f = lmapM f . catMaybes---- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of--- the fold, filters out 'Nothing' elements, and return the values extracted--- from 'Just'.------ >>> mapMaybe f = Fold.lmap f . Fold.catMaybes--- >>> mapMaybe f = Fold.mapMaybeM (return . f)------ >>> f x = if even x then Just x else Nothing--- >>> fld = Fold.mapMaybe f Fold.toList--- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)--- [2,4,6,8,10]----{-# INLINE mapMaybe #-}-mapMaybe :: Monad m => (a -> Maybe b) -> Fold m b r -> Fold m a r-mapMaybe f = lmap f . catMaybes----------------------------------------------------------------------------------- Transformations on fold inputs----------------------------------------------------------------------------------- | Apply a monadic function on the input and return the input.------ >>> Stream.fold (Fold.lmapM (Fold.tracing print) Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)--- 1--- 2------ /Pre-release/----{-# INLINE tracing #-}-tracing :: Monad m => (a -> m b) -> (a -> m a)-tracing f x = void (f x) >> return x---- | Apply a monadic function to each element flowing through and discard the--- results.------ >>> Stream.fold (Fold.trace print Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)--- 1--- 2------ >>> trace f = Fold.lmapM (Fold.tracing f)------ /Pre-release/-{-# INLINE trace #-}-trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r-trace f = lmapM (tracing f)---- rename to lpipe?------ | Apply a transformation on a 'Fold' using a 'Pipe'.------ /Pre-release/-{-# INLINE transform #-}-transform :: Monad m => Pipe m a b -> Fold m b c -> Fold m a c-transform (Pipe pstep1 pstep2 pinitial) (Fold fstep finitial fextract) =-    Fold step initial extract--    where--    initial = first (Tuple' pinitial) <$> finitial--    step (Tuple' ps fs) x = do-        r <- pstep1 ps x-        go fs r--        where--        -- XXX use SPEC?-        go acc (Pipe.Yield b (Consume ps')) = do-            acc' <- fstep acc b-            return-                $ case acc' of-                      Partial s -> Partial $ Tuple' ps' s-                      Done b2 -> Done b2-        go acc (Pipe.Yield b (Produce ps')) = do-            acc' <- fstep acc b-            r <- pstep2 ps'-            case acc' of-                Partial s -> go s r-                Done b2 -> return $ Done b2-        go acc (Pipe.Continue (Consume ps')) =-            return $ Partial $ Tuple' ps' acc-        go acc (Pipe.Continue (Produce ps')) = do-            r <- pstep2 ps'-            go acc r--    extract (Tuple' _ fs) = fextract fs--{-# INLINE scanWith #-}-scanWith :: Monad m => Bool -> Fold m a b -> Fold m b c -> Fold m a c-scanWith isMany (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract--    where--    {-# INLINE runStep #-}-    runStep actionL sR = do-        rL <- actionL-        case rL of-            Done bL -> do-                rR <- stepR sR bL-                case rR of-                    Partial sR1 ->-                        if isMany-                        then runStep initialL sR1-                        else Done <$> extractR sR1-                    Done bR -> return $ Done bR-            Partial sL -> do-                !b <- extractL sL-                rR <- stepR sR b-                return-                    $ case rR of-                        Partial sR1 -> Partial (sL, sR1)-                        Done bR -> Done bR--    initial = do-        r <- initialR-        case r of-            Partial sR -> runStep initialL sR-            Done b -> return $ Done b--    step (sL, sR) x = runStep (stepL sL x) sR--    extract = extractR . snd---- | Scan the input of a 'Fold' to change it in a stateful manner using another--- 'Fold'. The scan stops as soon as the fold terminates.------ /Pre-release/-{-# INLINE scan #-}-scan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c-scan = scanWith False---- XXX This does not fuse beacuse of the recursive step. Need to investigate.------ | Scan the input of a 'Fold' to change it in a stateful manner using another--- 'Fold'. The scan restarts with a fresh state if the fold terminates.------ /Pre-release/-{-# INLINE scanMany #-}-scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c-scanMany = scanWith True----------------------------------------------------------------------------------- Filters----------------------------------------------------------------------------------- | Returns the latest element omitting the first occurrence that satisfies--- the given equality predicate.------ Example:------ >>> input = Stream.fromList [1,3,3,5]--- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.deleteBy (==) 3) input--- [1,3,5]----{-# INLINE_NORMAL deleteBy #-}-deleteBy :: Monad m => (a -> a -> Bool) -> a -> Fold m a (Maybe a)-deleteBy eq x0 = fmap extract $ foldl' step (Tuple' False Nothing)--    where--    step (Tuple' False _) x =-        if eq x x0-        then Tuple' True Nothing-        else Tuple' False (Just x)-    step (Tuple' True _) x = Tuple' True (Just x)--    extract (Tuple' _ x) = x---- | Provide a sliding window of length 2 elements.------ See "Streamly.Internal.Data.Fold.Window".----{-# INLINE slide2 #-}-slide2 :: Monad m => Fold m (a, Maybe a) b -> Fold m a b-slide2 (Fold step1 initial1 extract1) = Fold step initial extract--    where--    initial =-        first (Tuple' Nothing) <$> initial1--    step (Tuple' prev s) cur =-        first (Tuple' (Just cur)) <$> step1 s (cur, prev)--    extract (Tuple' _ s) = extract1 s---- | Return the latest unique element using the supplied comparison function.--- Returns 'Nothing' if the current element is same as the last element--- otherwise returns 'Just'.------ Example, strip duplicate path separators:------ >>> input = Stream.fromList "//a//b"--- >>> f x y = x == '/' && y == '/'--- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.uniqBy f) input--- "/a/b"------ Space: @O(1)@------ /Pre-release/----{-# INLINE uniqBy #-}-uniqBy :: Monad m => (a -> a -> Bool) -> Fold m a (Maybe a)-uniqBy eq = rollingMap f--    where--    f pre curr =-        case pre of-            Nothing -> Just curr-            Just x -> if x `eq` curr then Nothing else Just curr---- | See 'uniqBy'.------ Definition:------ >>> uniq = Fold.uniqBy (==)----{-# INLINE uniq #-}-uniq :: (Monad m, Eq a) => Fold m a (Maybe a)-uniq = uniqBy (==)---- | Strip all leading and trailing occurrences of an element passing a--- predicate and make all other consecutive occurrences uniq.------ >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)------ @--- > Stream.prune isSpace (Stream.fromList "  hello      world!   ")--- "hello world!"------ @------ Space: @O(1)@------ /Unimplemented/-{-# INLINE prune #-}-prune ::-    -- (Monad m, Eq a) =>-    (a -> Bool) -> Fold m a (Maybe a)-prune = error "Not implemented yet!"---- | Emit only repeated elements, once.------ /Unimplemented/-repeated :: -- (Monad m, Eq a) =>-    Fold m a (Maybe a)-repeated = error "Not implemented yet!"----------------------------------------------------------------------------------- Left folds------------------------------------------------------------------------------------------------------------------------------------------------------------------ Run Effects----------------------------------------------------------------------------------- |--- Definitions:------ >>> drainMapM f = Fold.lmapM f Fold.drain--- >>> drainMapM f = Fold.foldMapM (void . f)------ Drain all input after passing it through a monadic function. This is the--- dual of mapM_ on stream producers.----{-# INLINE drainMapM #-}-drainMapM ::  Monad m => (a -> m b) -> Fold m a ()-drainMapM f = lmapM f drain--{-# DEPRECATED drainBy "Please use 'drainMapM' instead." #-}-{-# INLINE drainBy #-}-drainBy ::  Monad m => (a -> m b) -> Fold m a ()-drainBy = drainMapM---- | Returns the latest element of the input stream, if any.------ >>> latest = Fold.foldl1' (\_ x -> x)--- >>> latest = fmap getLast $ Fold.foldMap (Last . Just)----{-# INLINE latest #-}-latest :: Monad m => Fold m a (Maybe a)-latest = foldl1' (\_ x -> x)--{-# DEPRECATED last "Please use 'latest' instead." #-}-{-# INLINE last #-}-last :: Monad m => Fold m a (Maybe a)-last = latest---- | Terminates with 'Nothing' as soon as it finds an element different than--- the previous one, returns 'the' element if the entire input consists of the--- same element.----{-# INLINE the #-}-the :: (Monad m, Eq a) => Fold m a (Maybe a)-the = foldt' step initial id--    where--    initial = Partial Nothing--    step Nothing x = Partial (Just x)-    step old@(Just x0) x =-            if x0 == x-            then Partial old-            else Done Nothing----------------------------------------------------------------------------------- To Summary----------------------------------------------------------------------------------- | Like 'length', except with a more general 'Num' return value------ Definition:------ >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const  1)--- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0------ /Pre-release/-{-# INLINE lengthGeneric #-}-lengthGeneric :: (Monad m, Num b) => Fold m a b-lengthGeneric = foldl' (\n _ -> n + 1) 0---- | Determine the length of the input stream.------ Definition:------ >>> length = Fold.lengthGeneric--- >>> length = fmap getSum $ Fold.foldMap (Sum . const  1)----{-# INLINE length #-}-length :: Monad m => Fold m a Int-length = lengthGeneric----- | Determine the sum of all elements of a stream of numbers. Returns additive--- identity (@0@) when the stream is empty. Note that this is not numerically--- stable for floating point numbers.------ >>> sum = FoldW.cumulative FoldW.sum------ Same as following but numerically stable:------ >>> sum = Fold.foldl' (+) 0--- >>> sum = fmap Data.Monoid.getSum $ Fold.foldMap Data.Monoid.Sum----{-# INLINE sum #-}-sum :: (Monad m, Num a) => Fold m a a-sum = FoldW.cumulative FoldW.sum---- | Determine the product of all elements of a stream of numbers. Returns--- multiplicative identity (@1@) when the stream is empty. The fold terminates--- when it encounters (@0@) in its input.------ Same as the following but terminates on multiplication by @0@:------ >>> product = fmap Data.Monoid.getProduct $ Fold.foldMap Data.Monoid.Product----{-# INLINE product #-}-product :: (Monad m, Num a, Eq a) => Fold m a a-product =  foldt' step (Partial 1) id--    where--    step x a =-        if a == 0-        then Done 0-        else Partial $ x * a----------------------------------------------------------------------------------- To Summary (Maybe)----------------------------------------------------------------------------------- | Determine the maximum element in a stream using the supplied comparison--- function.----{-# INLINE maximumBy #-}-maximumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)-maximumBy cmp = foldl1' max'--    where--    max' x y =-        case cmp x y of-            GT -> x-            _ -> y---- | Determine the maximum element in a stream.------ Definitions:------ >>> maximum = Fold.maximumBy compare--- >>> maximum = Fold.foldl1' max------ Same as the following but without a default maximum. The 'Max' Monoid uses--- the 'minBound' as the default maximum:------ >>> maximum = fmap Data.Semigroup.getMax $ Fold.foldMap Data.Semigroup.Max----{-# INLINE maximum #-}-maximum :: (Monad m, Ord a) => Fold m a (Maybe a)-maximum = foldl1' max---- | Computes the minimum element with respect to the given comparison function----{-# INLINE minimumBy #-}-minimumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)-minimumBy cmp = foldl1' min'--    where--    min' x y =-        case cmp x y of-            GT -> y-            _ -> x---- | Determine the minimum element in a stream using the supplied comparison--- function.------ Definitions:------ >>> minimum = Fold.minimumBy compare--- >>> minimum = Fold.foldl1' min------ Same as the following but without a default minimum. The 'Min' Monoid uses the--- 'maxBound' as the default maximum:------ >>> maximum = fmap Data.Semigroup.getMin $ Fold.foldMap Data.Semigroup.Min----{-# INLINE minimum #-}-minimum :: (Monad m, Ord a) => Fold m a (Maybe a)-minimum = foldl1' min----------------------------------------------------------------------------------- To Summary (Statistical)----------------------------------------------------------------------------------- | Compute a numerically stable arithmetic mean of all elements in the input--- stream.----{-# INLINE mean #-}-mean :: (Monad m, Fractional a) => Fold m a a-mean = fmap done $ foldl' step begin--    where--    begin = Tuple' 0 0--    step (Tuple' x n) y =-        let n1 = n + 1-         in Tuple' (x + (y - x) / n1) n1--    done (Tuple' x _) = x---- | Compute a numerically stable (population) variance over all elements in--- the input stream.----{-# DEPRECATED variance "Use the streamly-statistics package instead" #-}-{-# INLINE variance #-}-variance :: (Monad m, Fractional a) => Fold m a a-variance = fmap done $ foldl' step begin--    where--    begin = Tuple3' 0 0 0--    step (Tuple3' n mean_ m2) x = Tuple3' n' mean' m2'--        where--        n' = n + 1-        mean' = (n * mean_ + x) / (n + 1)-        delta = x - mean_-        m2' = m2 + delta * delta * n / (n + 1)--    done (Tuple3' n _ m2) = m2 / n---- | Compute a numerically stable (population) standard deviation over all--- elements in the input stream.----{-# DEPRECATED stdDev "Use the streamly-statistics package instead" #-}-{-# INLINE stdDev #-}-stdDev :: (Monad m, Floating a) => Fold m a a-stdDev = sqrt <$> variance---- | Compute an 'Int' sized polynomial rolling hash------ > H = salt * k ^ n + c1 * k ^ (n - 1) + c2 * k ^ (n - 2) + ... + cn * k ^ 0------ Where @c1@, @c2@, @cn@ are the elements in the input stream and @k@ is a--- constant.------ This hash is often used in Rabin-Karp string search algorithm.------ See https://en.wikipedia.org/wiki/Rolling_hash----{-# INLINE rollingHashWithSalt #-}-rollingHashWithSalt :: (Monad m, Enum a) => Int64 -> Fold m a Int64-rollingHashWithSalt = foldl' step--    where--    k = 2891336453 :: Int64--    step cksum a = cksum * k + fromIntegral (fromEnum a)---- | A default salt used in the implementation of 'rollingHash'.-{-# INLINE defaultSalt #-}-defaultSalt :: Int64-defaultSalt = -2578643520546668380---- | Compute an 'Int' sized polynomial rolling hash of a stream.------ >>> rollingHash = Fold.rollingHashWithSalt Fold.defaultSalt----{-# INLINE rollingHash #-}-rollingHash :: (Monad m, Enum a) => Fold m a Int64-rollingHash = rollingHashWithSalt defaultSalt---- | Compute an 'Int' sized polynomial rolling hash of the first n elements of--- a stream.------ >>> rollingHashFirstN n = Fold.take n Fold.rollingHash------ /Pre-release/-{-# INLINE rollingHashFirstN #-}-rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64-rollingHashFirstN n = take n rollingHash---- XXX Compare this with the implementation in Fold.Window, preferrably use the--- latter if performance is good.---- | Apply a function on every two successive elements of a stream. The first--- argument of the map function is the previous element and the second argument--- is the current element. When processing the very first element in the--- stream, the previous element is 'Nothing'.------ /Pre-release/----{-# INLINE rollingMapM #-}-rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Fold m a b-rollingMapM f = Fold step initial extract--    where--    -- XXX We need just a postscan. We do not need an initial result here.-    -- Or we can supply a default initial result as an argument to rollingMapM.-    initial = return $ Partial (Nothing, error "Empty stream")--    step (prev, _) cur = do-        x <- f prev cur-        return $ Partial (Just cur, x)--    extract = return . snd---- |--- >>> rollingMap f = Fold.rollingMapM (\x y -> return $ f x y)----{-# INLINE rollingMap #-}-rollingMap :: Monad m => (Maybe a -> a -> b) -> Fold m a b-rollingMap f = rollingMapM (\x y -> return $ f x y)----------------------------------------------------------------------------------- Monoidal left folds----------------------------------------------------------------------------------- | Semigroup concat. Append the elements of an input stream to a provided--- starting value.------ Definition:------ >>> sconcat = Fold.foldl' (<>)------ >>> semigroups = fmap Data.Monoid.Sum $ Stream.enumerateFromTo 1 10--- >>> Stream.fold (Fold.sconcat 10) semigroups--- Sum {getSum = 65}----{-# INLINE sconcat #-}-sconcat :: (Monad m, Semigroup a) => a -> Fold m a a-sconcat = foldl' (<>)---- | Monoid concat. Fold an input stream consisting of monoidal elements using--- 'mappend' and 'mempty'.------ Definition:------ >>> mconcat = Fold.sconcat mempty------ >>> monoids = fmap Data.Monoid.Sum $ Stream.enumerateFromTo 1 10--- >>> Stream.fold Fold.mconcat monoids--- Sum {getSum = 55}----{-# INLINE mconcat #-}-mconcat ::-    ( Monad m-    , Monoid a) => Fold m a a-mconcat = sconcat mempty---- |--- Definition:------ >>> foldMap f = Fold.lmap f Fold.mconcat------ Make a fold from a pure function that folds the output of the function--- using 'mappend' and 'mempty'.------ >>> sum = Fold.foldMap Data.Monoid.Sum--- >>> Stream.fold sum $ Stream.enumerateFromTo 1 10--- Sum {getSum = 55}----{-# INLINE foldMap #-}-foldMap :: (Monad m, Monoid b) => (a -> b) -> Fold m a b-foldMap f = lmap f mconcat---- |--- Definition:------ >>> foldMapM f = Fold.lmapM f Fold.mconcat------ Make a fold from a monadic function that folds the output of the function--- using 'mappend' and 'mempty'.------ >>> sum = Fold.foldMapM (return . Data.Monoid.Sum)--- >>> Stream.fold sum $ Stream.enumerateFromTo 1 10--- Sum {getSum = 55}----{-# INLINE foldMapM #-}-foldMapM ::  (Monad m, Monoid b) => (a -> m b) -> Fold m a b-foldMapM act = foldlM' step (pure mempty)--    where--    step m a = do-        m' <- act a-        return $! mappend m m'----------------------------------------------------------------------------------- To Containers----------------------------------------------------------------------------------- $toListRev--- This is more efficient than 'Streamly.Internal.Data.Fold.toList'. toList is--- exactly the same as reversing the list after 'toListRev'.---- | Buffers the input stream to a list in the reverse order of the input.------ Definition:------ >>> toListRev = Fold.foldl' (flip (:)) []------ /Warning!/ working on large lists accumulated as buffers in memory could be--- very inefficient, consider using "Streamly.Array" instead.-------  xn : ... : x2 : x1 : []-{-# INLINE toListRev #-}-toListRev :: Monad m => Fold m a [a]-toListRev = foldl' (flip (:)) []----------------------------------------------------------------------------------- Partial Folds----------------------------------------------------------------------------------- | A fold that drains the first n elements of its input, running the effects--- and discarding the results.------ Definition:------ >>> drainN n = Fold.take n Fold.drain------ /Pre-release/-{-# INLINE drainN #-}-drainN :: Monad m => Int -> Fold m a ()-drainN n = take n drain----------------------------------------------------------------------------------- To Elements----------------------------------------------------------------------------------- | Like 'index', except with a more general 'Integral' argument------ /Pre-release/-{-# INLINE indexGeneric #-}-indexGeneric :: (Integral i, Monad m) => i -> Fold m a (Maybe a)-indexGeneric i = foldt' step (Partial 0) (const Nothing)--    where--    step j a =-        if i == j-        then Done $ Just a-        else Partial (j + 1)---- | Return the element at the given index.------ Definition:------ >>> index = Fold.indexGeneric----{-# INLINE index #-}-index :: Monad m => Int -> Fold m a (Maybe a)-index = indexGeneric---- | Consume a single input and transform it using the supplied 'Maybe'--- returning function.------ /Pre-release/----{-# INLINE maybe #-}-maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)-maybe f = foldt' (const (Done . f)) (Partial Nothing) id---- | Consume a single element and return it if it passes the predicate else--- return 'Nothing'.------ Definition:------ >>> satisfy f = Fold.maybe (\a -> if f a then Just a else Nothing)------ /Pre-release/-{-# INLINE satisfy #-}-satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)-satisfy f = maybe (\a -> if f a then Just a else Nothing)-{--satisfy f = Fold step (return $ Partial ()) (const (return Nothing))--    where--    step () a = return $ Done $ if f a then Just a else Nothing--}---- Naming notes:------ "head" and "next" are two alternative names for the same API. head sounds--- apt in the context of lists but next sounds more apt in the context of--- streams where we think in terms of generating and consuming the next element--- rather than taking the head of some static/persistent structure.------ We also want to keep the nomenclature consistent across folds and parsers,--- "head" becomes even more unintuitive for parsers because there are two--- possible variants viz. peek and next.------ Also, the "head" fold creates confusion in situations like--- https://github.com/composewell/streamly/issues/1404 where intuitive--- expectation from head is to consume the entire stream and just give us the--- head. There we want to convey the notion that we consume one element from--- the stream and stop. The name "one" already being used in parsers for this--- purpose sounds more apt from this perspective.------ The source of confusion is perhaps due to the fact that some folds consume--- the entire stream and others terminate early. It may have been clearer if we--- had separate abstractions for the two use cases.---- XXX We can possibly use "head" for the purposes of reducing the entire--- stream to the head element i.e. take the head and drain the rest.---- | Take one element from the stream and stop.------ Definition:------ >>> one = Fold.maybe Just------ This is similar to the stream 'Stream.uncons' operation.----{-# INLINE one #-}-one :: Monad m => Fold m a (Maybe a)-one = maybe Just---- | Extract the first element of the stream, if any.------ >>> head = Fold.one----{-# DEPRECATED head "Please use \"one\" instead" #-}-{-# INLINE head #-}-head :: Monad m => Fold m a (Maybe a)-head = one---- | Returns the first element that satisfies the given predicate.------ /Pre-release/-{-# INLINE findM #-}-findM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)-findM predicate = Fold step (return $ Partial ()) (const $ return Nothing)--    where--    step () a =-        let f r =-                if r-                then Done (Just a)-                else Partial ()-         in f <$> predicate a---- | Returns the first element that satisfies the given predicate.----{-# INLINE find #-}-find :: Monad m => (a -> Bool) -> Fold m a (Maybe a)-find p = findM (return . p)---- | In a stream of (key-value) pairs @(a, b)@, return the value @b@ of the--- first pair where the key equals the given value @a@.------ Definition:------ >>> lookup x = fmap snd <$> Fold.find ((== x) . fst)----{-# INLINE lookup #-}-lookup :: (Eq a, Monad m) => a -> Fold m (a,b) (Maybe b)-lookup a0 = foldt' step (Partial ()) (const Nothing)--    where--    step () (a, b) =-        if a == a0-        then Done $ Just b-        else Partial ()---- | Returns the first index that satisfies the given predicate.----{-# INLINE findIndex #-}-findIndex :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)-findIndex predicate = foldt' step (Partial 0) (const Nothing)--    where--    step i a =-        if predicate a-        then Done $ Just i-        else Partial (i + 1)---- | Returns the index of the latest element if the element satisfies the given--- predicate.----{-# INLINE findIndices #-}-findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)-findIndices predicate =-    -- XXX implement by combining indexing and filtering scans-    fmap (either (const Nothing) Just) $ foldl' step (Left (-1))--    where--    step i a =-        if predicate a-        then Right (either id id i + 1)-        else Left (either id id i + 1)---- | Returns the index of the latest element if the element matches the given--- value.------ Definition:------ >>> elemIndices a = Fold.findIndices (== a)----{-# INLINE elemIndices #-}-elemIndices :: (Monad m, Eq a) => a -> Fold m a (Maybe Int)-elemIndices a = findIndices (== a)---- | Returns the first index where a given value is found in the stream.------ Definition:------ >>> elemIndex a = Fold.findIndex (== a)----{-# INLINE elemIndex #-}-elemIndex :: (Eq a, Monad m) => a -> Fold m a (Maybe Int)-elemIndex a = findIndex (== a)----------------------------------------------------------------------------------- To Boolean----------------------------------------------------------------------------------- Similar to 'eof' parser, but the fold consumes and discards an input element--- when not at eof. XXX Remove or Rename to "eof"?---- | Consume one element, return 'True' if successful else return 'False'. In--- other words, test if the input is empty or not.------ WARNING! It consumes one element if the stream is not empty. If that is not--- what you want please use the eof parser instead.------ Definition:------ >>> null = fmap isJust Fold.one----{-# INLINE null #-}-null :: Monad m => Fold m a Bool-null = foldt' (\() _ -> Done False) (Partial ()) (const True)---- | Returns 'True' if any element of the input satisfies the predicate.------ Definition:------ >>> any p = Fold.lmap p Fold.or------ Example:------ >>> Stream.fold (Fold.any (== 0)) $ Stream.fromList [1,0,1]--- True----{-# INLINE any #-}-any :: Monad m => (a -> Bool) -> Fold m a Bool-any predicate = foldt' step initial id--    where--    initial = Partial False--    step _ a =-        if predicate a-        then Done True-        else Partial False---- | Return 'True' if the given element is present in the stream.------ Definition:------ >>> elem a = Fold.any (== a)----{-# INLINE elem #-}-elem :: (Eq a, Monad m) => a -> Fold m a Bool-elem a = any (== a)---- | Returns 'True' if all elements of the input satisfy the predicate.------ Definition:------ >>> all p = Fold.lmap p Fold.and------ Example:------ >>> Stream.fold (Fold.all (== 0)) $ Stream.fromList [1,0,1]--- False----{-# INLINE all #-}-all :: Monad m => (a -> Bool) -> Fold m a Bool-all predicate = foldt' step initial id--    where--    initial = Partial True--    step _ a =-        if predicate a-        then Partial True-        else Done False---- | Returns 'True' if the given element is not present in the stream.------ Definition:------ >>> notElem a = Fold.all (/= a)----{-# INLINE notElem #-}-notElem :: (Eq a, Monad m) => a -> Fold m a Bool-notElem a = all (/= a)---- | Returns 'True' if all elements are 'True', 'False' otherwise------ Definition:------ >>> and = Fold.all (== True)----{-# INLINE and #-}-and :: Monad m => Fold m Bool Bool-and = all (== True)---- | Returns 'True' if any element is 'True', 'False' otherwise------ Definition:------ >>> or = Fold.any (== True)----{-# INLINE or #-}-or :: Monad m => Fold m Bool Bool-or = any (== True)----------------------------------------------------------------------------------- Grouping/Splitting------------------------------------------------------------------------------------------------------------------------------------------------------------------ Grouping without looking at elements------------------------------------------------------------------------------------------------------------------------------------------------------------------ Binary APIs----------------------------------------------------------------------------------- | @splitAt n f1 f2@ composes folds @f1@ and @f2@ such that first @n@--- elements of its input are consumed by fold @f1@ and the rest of the stream--- is consumed by fold @f2@.------ >>> let splitAt_ n xs = Stream.fold (Fold.splitAt n Fold.toList Fold.toList) $ Stream.fromList xs------ >>> splitAt_ 6 "Hello World!"--- ("Hello ","World!")------ >>> splitAt_ (-1) [1,2,3]--- ([],[1,2,3])------ >>> splitAt_ 0 [1,2,3]--- ([],[1,2,3])------ >>> splitAt_ 1 [1,2,3]--- ([1],[2,3])------ >>> splitAt_ 3 [1,2,3]--- ([1,2,3],[])------ >>> splitAt_ 4 [1,2,3]--- ([1,2,3],[])------ > splitAt n f1 f2 = Fold.splitWith (,) (Fold.take n f1) f2------ /Internal/--{-# INLINE splitAt #-}-splitAt-    :: Monad m-    => Int-    -> Fold m a b-    -> Fold m a c-    -> Fold m a (b, c)-splitAt n fld = splitWith (,) (take n fld)----------------------------------------------------------------------------------- Element Aware APIs-------------------------------------------------------------------------------------------------------------------------------------------------------------------- Binary APIs---------------------------------------------------------------------------------{-# INLINE takingEndByM #-}-takingEndByM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)-takingEndByM p = Fold step initial (return . toMaybe)--    where--    initial = return $ Partial Nothing'--    step _ a = do-        r <- p a-        return-            $ if r-              then Done $ Just a-              else Partial $ Just' a---- |------ >>> takingEndBy p = Fold.takingEndByM (return . p)----{-# INLINE takingEndBy #-}-takingEndBy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)-takingEndBy p = takingEndByM (return . p)--{-# INLINE takingEndByM_ #-}-takingEndByM_ :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)-takingEndByM_ p = Fold step initial (return . toMaybe)--    where--    initial = return $ Partial Nothing'--    step _ a = do-        r <- p a-        return-            $ if r-              then Done Nothing-              else Partial $ Just' a---- |------ >>> takingEndBy_ p = Fold.takingEndByM_ (return . p)----{-# INLINE takingEndBy_ #-}-takingEndBy_ :: Monad m => (a -> Bool) -> Fold m a (Maybe a)-takingEndBy_ p = takingEndByM_ (return . p)--{-# INLINE droppingWhileM #-}-droppingWhileM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)-droppingWhileM p = Fold step initial (return . toMaybe)--    where--    initial = return $ Partial Nothing'--    step Nothing' a = do-        r <- p a-        return-            $ Partial-            $ if r-              then Nothing'-              else Just' a-    step _ a = return $ Partial $ Just' a---- |--- >>> droppingWhile p = Fold.droppingWhileM (return . p)----{-# INLINE droppingWhile #-}-droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)-droppingWhile p = droppingWhileM (return . p)---- Note: Keep this consistent with S.splitOn. In fact we should eliminate--- S.splitOn in favor of the fold.------ XXX Use Fold.many instead once it is fixed.--- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)---- | Like 'takeEndBy' but drops the element on which the predicate succeeds.------ Example:------ >>> input = Stream.fromList "hello\nthere\n"--- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList--- >>> Stream.fold line input--- "hello"------ >>> Stream.fold Fold.toList $ Stream.foldMany line input--- ["hello","there"]----{-# INLINE takeEndBy_ #-}-takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b--- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)-takeEndBy_ predicate (Fold fstep finitial fextract) =-    Fold step finitial fextract--    where--    step s a =-        if not (predicate a)-        then fstep s a-        else Done <$> fextract s---- Note:--- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)---- | Take the input, stop when the predicate succeeds taking the succeeding--- element as well.------ Example:------ >>> input = Stream.fromList "hello\nthere\n"--- >>> line = Fold.takeEndBy (== '\n') Fold.toList--- >>> Stream.fold line input--- "hello\n"------ >>> Stream.fold Fold.toList $ Stream.foldMany line input--- ["hello\n","there\n"]----{-# INLINE takeEndBy #-}-takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b--- takeEndBy predicate = scanMaybe (takingEndBy predicate)-takeEndBy predicate (Fold fstep finitial fextract) =-    Fold step finitial fextract--    where--    step s a = do-        res <- fstep s a-        if not (predicate a)-        then return res-        else do-            case res of-                Partial s1 -> Done <$> fextract s1-                Done b -> return $ Done b----------------------------------------------------------------------------------- Binary splitting on a separator---------------------------------------------------------------------------------data SplitOnSeqState acc a rb rh w ck =-      SplitOnSeqEmpty !acc-    | SplitOnSeqSingle !acc !a-    | SplitOnSeqWord !acc !Int !w-    | SplitOnSeqWordLoop !acc !w-    | SplitOnSeqKR !acc !Int !rb !rh-    | SplitOnSeqKRLoop !acc !ck !rb !rh---- XXX Need to add tests for takeEndBySeq, we have tests for takeEndBySeq_ .---- | Continue taking the input until the input sequence matches the supplied--- sequence, taking the supplied sequence as well. If the pattern is empty this--- acts as an identity fold.------ >>> s = Stream.fromList "hello there. How are you?"--- >>> f = Fold.takeEndBySeq (Array.fromList "re") Fold.toList--- >>> Stream.fold f s--- "hello there"------ >>> Stream.fold Fold.toList $ Stream.foldMany f s--- ["hello there",". How are"," you?"]------ /Pre-release/-{-# INLINE takeEndBySeq #-}-takeEndBySeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) =>-       Array.Array a-    -> Fold m a b-    -> Fold m a b-takeEndBySeq patArr (Fold fstep finitial fextract) =-    Fold step initial extract--    where--    patLen = Array.length patArr--    initial = do-        res <- finitial-        case res of-            Partial acc-                | patLen == 0 ->-                    -- XXX Should we match nothing or everything on empty-                    -- pattern?-                    -- Done <$> fextract acc-                    return $ Partial $ SplitOnSeqEmpty acc-                | patLen == 1 -> do-                    pat <- liftIO $ Array.unsafeIndexIO 0 patArr-                    return $ Partial $ SplitOnSeqSingle acc pat-                | SIZE_OF(a) * patLen <= sizeOf (Proxy :: Proxy Word) ->-                    return $ Partial $ SplitOnSeqWord acc 0 0-                | otherwise -> do-                    (rb, rhead) <- liftIO $ Ring.new patLen-                    return $ Partial $ SplitOnSeqKR acc 0 rb rhead-            Done b -> return $ Done b--    -- Word pattern related-    maxIndex = patLen - 1--    elemBits = SIZE_OF(a) * 8--    wordMask :: Word-    wordMask = (1 `shiftL` (elemBits * patLen)) - 1--    wordPat :: Word-    wordPat = wordMask .&. Array.foldl' addToWord 0 patArr--    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)--    -- For Rabin-Karp search-    k = 2891336453 :: Word32-    coeff = k ^ patLen--    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)--    deltaCksum cksum old new =-        addCksum cksum new - coeff * fromIntegral (fromEnum old)--    -- XXX shall we use a random starting hash or 1 instead of 0?-    -- XXX Need to keep this cached across fold calls in foldmany-    -- XXX We may need refold to inject the cached state instead of-    -- initializing the state every time.-    -- XXX Allocation of ring buffer should also be done once-    patHash = Array.foldl' addCksum 0 patArr--    step (SplitOnSeqEmpty s) x = do-        res <- fstep s x-        case res of-            Partial s1 -> return $ Partial $ SplitOnSeqEmpty s1-            Done b -> return $ Done b-    step (SplitOnSeqSingle s pat) x = do-        res <- fstep s x-        case res of-            Partial s1-                | pat /= x -> return $ Partial $ SplitOnSeqSingle s1 pat-                | otherwise -> Done <$> fextract s1-            Done b -> return $ Done b-    step (SplitOnSeqWord s idx wrd) x = do-        res <- fstep s x-        let wrd1 = addToWord wrd x-        case res of-            Partial s1-                | idx == maxIndex -> do-                    if wrd1 .&. wordMask == wordPat-                    then Done <$> fextract s1-                    else return $ Partial $ SplitOnSeqWordLoop s1 wrd1-                | otherwise ->-                    return $ Partial $ SplitOnSeqWord s1 (idx + 1) wrd1-            Done b -> return $ Done b-    step (SplitOnSeqWordLoop s wrd) x = do-        res <- fstep s x-        let wrd1 = addToWord wrd x-        case res of-            Partial s1-                | wrd1 .&. wordMask == wordPat ->-                    Done <$> fextract s1-                | otherwise ->-                    return $ Partial $ SplitOnSeqWordLoop s1 wrd1-            Done b -> return $ Done b-    step (SplitOnSeqKR s idx rb rh) x = do-        res <- fstep s x-        case res of-            Partial s1 -> do-                rh1 <- liftIO $ Ring.unsafeInsert rb rh x-                if idx == maxIndex-                then do-                    let fld = Ring.unsafeFoldRing (Ring.ringBound rb)-                    let !ringHash = fld addCksum 0 rb-                    if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr-                    then Done <$> fextract s1-                    else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1-                else-                    return $ Partial $ SplitOnSeqKR s1 (idx + 1) rb rh1-            Done b -> return $ Done b-    step (SplitOnSeqKRLoop s cksum rb rh) x = do-        res <- fstep s x-        case res of-            Partial s1 -> do-                old <- liftIO $ peek rh-                rh1 <- liftIO $ Ring.unsafeInsert rb rh x-                let ringHash = deltaCksum cksum old x-                if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr-                then Done <$> fextract s1-                else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1-            Done b -> return $ Done b--    extract state =-        let st =-                case state of-                    SplitOnSeqEmpty s -> s-                    SplitOnSeqSingle s _ -> s-                    SplitOnSeqWord s _ _ -> s-                    SplitOnSeqWordLoop s _ -> s-                    SplitOnSeqKR s _ _ _ -> s-                    SplitOnSeqKRLoop s _ _ _ -> s-         in fextract st---- | Like 'takeEndBySeq' but discards the matched sequence.------ /Pre-release/----{-# INLINE takeEndBySeq_ #-}-takeEndBySeq_ :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) =>-       Array.Array a-    -> Fold m a b-    -> Fold m a b-takeEndBySeq_ patArr (Fold fstep finitial fextract) =-    Fold step initial extract--    where--    patLen = Array.length patArr--    initial = do-        res <- finitial-        case res of-            Partial acc-                | patLen == 0 ->-                    -- XXX Should we match nothing or everything on empty-                    -- pattern?-                    -- Done <$> fextract acc-                    return $ Partial $ SplitOnSeqEmpty acc-                | patLen == 1 -> do-                    pat <- liftIO $ Array.unsafeIndexIO 0 patArr-                    return $ Partial $ SplitOnSeqSingle acc pat-                -- XXX Need to add tests for this case-                | SIZE_OF(a) * patLen <= sizeOf (Proxy :: Proxy Word) ->-                    return $ Partial $ SplitOnSeqWord acc 0 0-                | otherwise -> do-                    (rb, rhead) <- liftIO $ Ring.new patLen-                    return $ Partial $ SplitOnSeqKR acc 0 rb rhead-            Done b -> return $ Done b--    -- Word pattern related-    maxIndex = patLen - 1--    elemBits = SIZE_OF(a) * 8--    wordMask :: Word-    wordMask = (1 `shiftL` (elemBits * patLen)) - 1--    elemMask :: Word-    elemMask = (1 `shiftL` elemBits) - 1--    wordPat :: Word-    wordPat = wordMask .&. Array.foldl' addToWord 0 patArr--    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)--    -- For Rabin-Karp search-    k = 2891336453 :: Word32-    coeff = k ^ patLen--    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)--    deltaCksum cksum old new =-        addCksum cksum new - coeff * fromIntegral (fromEnum old)--    -- XXX shall we use a random starting hash or 1 instead of 0?-    -- XXX Need to keep this cached across fold calls in foldMany-    -- XXX We may need refold to inject the cached state instead of-    -- initializing the state every time.-    -- XXX Allocation of ring buffer should also be done once-    patHash = Array.foldl' addCksum 0 patArr--    step (SplitOnSeqEmpty s) x = do-        res <- fstep s x-        case res of-            Partial s1 -> return $ Partial $ SplitOnSeqEmpty s1-            Done b -> return $ Done b-    step (SplitOnSeqSingle s pat) x = do-        if pat /= x-        then do-            res <- fstep s x-            case res of-                Partial s1 -> return $ Partial $ SplitOnSeqSingle s1 pat-                Done b -> return $ Done b-        else Done <$> fextract s-    step (SplitOnSeqWord s idx wrd) x = do-        let wrd1 = addToWord wrd x-        if idx == maxIndex-        then do-            if wrd1 .&. wordMask == wordPat-            then Done <$> fextract s-            else return $ Partial $ SplitOnSeqWordLoop s wrd1-        else return $ Partial $ SplitOnSeqWord s (idx + 1) wrd1-    step (SplitOnSeqWordLoop s wrd) x = do-        let wrd1 = addToWord wrd x-            old = (wordMask .&. wrd)-                    `shiftR` (elemBits * (patLen - 1))-        res <- fstep s (toEnum $ fromIntegral old)-        case res of-            Partial s1-                | wrd1 .&. wordMask == wordPat ->-                    Done <$> fextract s1-                | otherwise ->-                    return $ Partial $ SplitOnSeqWordLoop s1 wrd1-            Done b -> return $ Done b-    step (SplitOnSeqKR s idx rb rh) x = do-        rh1 <- liftIO $ Ring.unsafeInsert rb rh x-        if idx == maxIndex-        then do-            let fld = Ring.unsafeFoldRing (Ring.ringBound rb)-            let !ringHash = fld addCksum 0 rb-            if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr-            then Done <$> fextract s-            else return $ Partial $ SplitOnSeqKRLoop s ringHash rb rh1-        else return $ Partial $ SplitOnSeqKR s (idx + 1) rb rh1-    step (SplitOnSeqKRLoop s cksum rb rh) x = do-        old <- liftIO $ peek rh-        res <- fstep s old-        case res of-            Partial s1 -> do-                rh1 <- liftIO $ Ring.unsafeInsert rb rh x-                let ringHash = deltaCksum cksum old x-                if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr-                then Done <$> fextract s1-                else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1-            Done b -> return $ Done b--    -- XXX extract should return backtrack count as well. If the fold-    -- terminates early inside extract, we may still have buffered data-    -- remaining which will be lost if we do not communicate that to the-    -- driver.-    extract state = do-        let consumeWord s n wrd = do-                if n == 0-                then fextract s-                else do-                    let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))-                    r <- fstep s (toEnum $ fromIntegral old)-                    case r of-                        Partial s1 -> consumeWord s1 (n - 1) wrd-                        Done b -> return b--        let consumeRing s n rb rh =-                if n == 0-                then fextract s-                else do-                    old <- liftIO $ peek rh-                    let rh1 = Ring.advance rb rh-                    r <- fstep s old-                    case r of-                        Partial s1 -> consumeRing s1 (n - 1) rb rh1-                        Done b -> return b--        case state of-            SplitOnSeqEmpty s -> fextract s-            SplitOnSeqSingle s _ -> fextract s-            SplitOnSeqWord s idx wrd -> consumeWord s idx wrd-            SplitOnSeqWordLoop s wrd -> consumeWord s patLen wrd-            SplitOnSeqKR s idx rb _ -> consumeRing s idx rb (Ring.startOf rb)-            SplitOnSeqKRLoop s _ rb rh -> consumeRing s patLen rb rh----------------------------------------------------------------------------------- Distributing------------------------------------------------------------------------------------- | Distribute one copy of the stream to each fold and zip the results.------ @---                 |-------Fold m a b--------|--- ---stream m a---|                         |---m (b,c)---                 |-------Fold m a c--------|--- @------  Definition:------ >>> tee = Fold.teeWith (,)------ Example:------ >>> t = Fold.tee Fold.sum Fold.length--- >>> Stream.fold t (Stream.enumerateFromTo 1.0 100.0)--- (5050.0,100)----{-# INLINE tee #-}-tee :: Monad m => Fold m a b -> Fold m a c -> Fold m a (b,c)-tee = teeWith (,)---- XXX use "List" instead of "[]"?, use Array for output to scale it to a large--- number of consumers? For polymorphic case a vector could be helpful. For--- Storables we can use arrays. Will need separate APIs for those.------ | Distribute one copy of the stream to each fold and collect the results in--- a container.------ @------                 |-------Fold m a b--------|--- ---stream m a---|                         |---m [b]---                 |-------Fold m a b--------|---                 |                         |---                            ...--- @------ >>> Stream.fold (Fold.distribute [Fold.sum, Fold.length]) (Stream.enumerateFromTo 1 5)--- [15,5]------ >>> distribute = Prelude.foldr (Fold.teeWith (:)) (Fold.fromPure [])------ This is the consumer side dual of the producer side 'sequence' operation.------ Stops when all the folds stop.----{-# INLINE distribute #-}-distribute :: Monad m => [Fold m a b] -> Fold m a [b]-distribute = Prelude.foldr (teeWith (:)) (fromPure [])----------------------------------------------------------------------------------- Partitioning---------------------------------------------------------------------------------{-# INLINE partitionByMUsing #-}-partitionByMUsing :: Monad m =>-       (  (x -> y -> (x, y))-       -> Fold m (Either b c) x-       -> Fold m (Either b c) y-       -> Fold m (Either b c) (x, y)-       )-    -> (a -> m (Either b c))-    -> Fold m b x-    -> Fold m c y-    -> Fold m a (x, y)-partitionByMUsing t f fld1 fld2 =-    let l = lmap (fromLeft undefined) fld1  -- :: Fold m (Either b c) x-        r = lmap (fromRight undefined) fld2 -- :: Fold m (Either b c) y-     in lmapM f (t (,) (filter isLeft l) (filter isRight r))---- | Partition the input over two folds using an 'Either' partitioning--- predicate.------ @------                                     |-------Fold b x--------|--- -----stream m a --> (Either b c)----|                       |----(x,y)---                                     |-------Fold c y--------|--- @------ Example, send input to either fold randomly:------ >>> :set -package random--- >>> import System.Random (randomIO)--- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a--- >>> f = Fold.partitionByM randomly Fold.length Fold.length--- >>> Stream.fold f (Stream.enumerateFromTo 1 100)--- ...------ Example, send input to the two folds in a proportion of 2:1:------ >>> :{--- proportionately m n = do---  ref <- newIORef $ cycle $ concat [replicate m Left, replicate n Right]---  return $ \a -> do---      r <- readIORef ref---      writeIORef ref $ tail r---      return $ Prelude.head r a--- :}------ >>> :{--- main = do---  g <- proportionately 2 1---  let f = Fold.partitionByM g Fold.length Fold.length---  r <- Stream.fold f (Stream.enumerateFromTo (1 :: Int) 100)---  print r--- :}------ >>> main--- (67,33)--------- This is the consumer side dual of the producer side 'mergeBy' operation.------ When one fold is done, any input meant for it is ignored until the other--- fold is also done.------ Stops when both the folds stop.------ /See also: 'partitionByFstM' and 'partitionByMinM'./------ /Pre-release/-{-# INLINE partitionByM #-}-partitionByM :: Monad m-    => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)-partitionByM = partitionByMUsing teeWith---- | Similar to 'partitionByM' but terminates when the first fold terminates.----{-# INLINE partitionByFstM #-}-partitionByFstM :: Monad m-    => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)-partitionByFstM = partitionByMUsing teeWithFst---- | Similar to 'partitionByM' but terminates when any fold terminates.----{-# INLINE partitionByMinM #-}-partitionByMinM :: Monad m =>-    (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)-partitionByMinM = partitionByMUsing teeWithMin---- Note: we could use (a -> Bool) instead of (a -> Either b c), but the latter--- makes the signature clearer as to which case belongs to which fold.--- XXX need to check the performance in both cases.---- | Same as 'partitionByM' but with a pure partition function.------ Example, count even and odd numbers in a stream:------ >>> :{---  let f = Fold.partitionBy (\n -> if even n then Left n else Right n)---                      (fmap (("Even " ++) . show) Fold.length)---                      (fmap (("Odd "  ++) . show) Fold.length)---   in Stream.fold f (Stream.enumerateFromTo 1 100)--- :}--- ("Even 50","Odd 50")------ /Pre-release/-{-# INLINE partitionBy #-}-partitionBy :: Monad m-    => (a -> Either b c) -> Fold m b x -> Fold m c y -> Fold m a (x, y)-partitionBy f = partitionByM (return . f)---- | Compose two folds such that the combined fold accepts a stream of 'Either'--- and routes the 'Left' values to the first fold and 'Right' values to the--- second fold.------ Definition:------ >>> partition = Fold.partitionBy id----{-# INLINE partition #-}-partition :: Monad m-    => Fold m b x -> Fold m c y -> Fold m (Either b c) (x, y)-partition = partitionBy id--{---- | Send one item to each fold in a round-robin fashion. This is the consumer--- side dual of producer side 'mergeN' operation.------ partitionN :: Monad m => [Fold m a b] -> Fold m a [b]--- partitionN fs = Fold step begin done--}----------------------------------------------------------------------------------- Unzipping---------------------------------------------------------------------------------{-# INLINE unzipWithMUsing #-}-unzipWithMUsing :: Monad m =>-       (  (x -> y -> (x, y))-       -> Fold m (b, c) x-       -> Fold m (b, c) y-       -> Fold m (b, c) (x, y)-       )-    -> (a -> m (b, c))-    -> Fold m b x-    -> Fold m c y-    -> Fold m a (x, y)-unzipWithMUsing t f fld1 fld2 =-    let f1 = lmap fst fld1  -- :: Fold m (b, c) b-        f2 = lmap snd fld2  -- :: Fold m (b, c) c-     in lmapM f (t (,) f1 f2)---- | Like 'unzipWith' but with a monadic splitter function.------ Definition:------ >>> unzipWithM k f1 f2 = Fold.lmapM k (Fold.unzip f1 f2)------ /Pre-release/-{-# INLINE unzipWithM #-}-unzipWithM :: Monad m-    => (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)-unzipWithM = unzipWithMUsing teeWith---- | Similar to 'unzipWithM' but terminates when the first fold terminates.----{-# INLINE unzipWithFstM #-}-unzipWithFstM :: Monad m =>-    (a -> m (b, c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)-unzipWithFstM = unzipWithMUsing teeWithFst---- | Similar to 'unzipWithM' but terminates when any fold terminates.----{-# INLINE unzipWithMinM #-}-unzipWithMinM :: Monad m =>-    (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)-unzipWithMinM = unzipWithMUsing teeWithMin---- | Split elements in the input stream into two parts using a pure splitter--- function, direct each part to a different fold and zip the results.------ Definitions:------ >>> unzipWith f = Fold.unzipWithM (return . f)--- >>> unzipWith f fld1 fld2 = Fold.lmap f (Fold.unzip fld1 fld2)------ This fold terminates when both the input folds terminate.------ /Pre-release/-{-# INLINE unzipWith #-}-unzipWith :: Monad m-    => (a -> (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)-unzipWith f = unzipWithM (return . f)---- | Send the elements of tuples in a stream of tuples through two different--- folds.------ @------                           |-------Fold m a x--------|--- ---------stream of (a,b)--|                         |----m (x,y)---                           |-------Fold m b y--------|------ @------ Definition:------ >>> unzip = Fold.unzipWith id------ This is the consumer side dual of the producer side 'zip' operation.----{-# INLINE unzip #-}-unzip :: Monad m => Fold m a x -> Fold m b y -> Fold m (a,b) (x,y)-unzip = unzipWith id----------------------------------------------------------------------------------- Combining streams and folds - Zipping----------------------------------------------------------------------------------- XXX These can be implemented using the fold scan, using the stream as a--- state.--- XXX Stream Skip state cannot be efficiently handled in folds but can be--- handled in parsers using the Continue facility. See zipWithM in the Parser--- module.------ cmpBy, eqBy, isPrefixOf, isSubsequenceOf etc can be implemented using--- zipStream.---- | Zip a stream with the input of a fold using the supplied function.------ /Unimplemented/----{-# INLINE zipStreamWithM #-}-zipStreamWithM :: -- Monad m =>-    (a -> b -> m c) -> Stream m a -> Fold m c x -> Fold m b x-zipStreamWithM = undefined---- | Zip a stream with the input of a fold.------ >>> zip = Fold.zipStreamWithM (curry return)------ /Unimplemented/----{-# INLINE zipStream #-}-zipStream :: Monad m => Stream m a -> Fold m (a, b) x -> Fold m b x-zipStream = zipStreamWithM (curry return)---- | Pair each element of a fold input with its index, starting from index 0.----{-# INLINE indexingWith #-}-indexingWith :: Monad m => Int -> (Int -> Int) -> Fold m a (Maybe (Int, a))-indexingWith i f = fmap toMaybe $ foldl' step initial--    where--    initial = Nothing'--    step Nothing' a = Just' (i, a)-    step (Just' (n, _)) a = Just' (f n, a)---- |--- >>> indexing = Fold.indexingWith 0 (+ 1)----{-# INLINE indexing #-}-indexing :: Monad m => Fold m a (Maybe (Int, a))-indexing = indexingWith 0 (+ 1)---- |--- >>> indexingRev n = Fold.indexingWith n (subtract 1)----{-# INLINE indexingRev #-}-indexingRev :: Monad m => Int -> Fold m a (Maybe (Int, a))-indexingRev n = indexingWith n (subtract 1)---- | Pair each element of a fold input with its index, starting from index 0.------ >>> indexed = Fold.scanMaybe Fold.indexing----{-# INLINE indexed #-}-indexed :: Monad m => Fold m (Int, a) b -> Fold m a b-indexed = scanMaybe indexing---- | Change the predicate function of a Fold from @a -> b@ to accept an--- additional state input @(s, a) -> b@. Convenient to filter with an--- addiitonal index or time input.------ >>> filterWithIndex = Fold.with Fold.indexed Fold.filter------ @--- filterWithAbsTime = with timestamped filter--- filterWithRelTime = with timeIndexed filter--- @------ /Pre-release/-{-# INLINE with #-}-with ::-       (Fold m (s, a) b -> Fold m a b)-    -> (((s, a) -> c) -> Fold m (s, a) b -> Fold m (s, a) b)-    -> (((s, a) -> c) -> Fold m a b -> Fold m a b)-with f comb g = f . comb g . lmap snd---- XXX Implement as a filter--- sampleFromthen :: Monad m => Int -> Int -> Fold m a (Maybe a)---- | @sampleFromthen offset stride@ samples the element at @offset@ index and--- then every element at strides of @stride@.----{-# INLINE sampleFromthen #-}-sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b-sampleFromthen offset size =-    with indexed filter (\(i, _) -> (i + offset) `mod` size == 0)----------------------------------------------------------------------------------- Nesting----------------------------------------------------------------------------------- | @concatSequence f t@ applies folds from stream @t@ sequentially and--- collects the results using the fold @f@.------ /Unimplemented/----{-# INLINE concatSequence #-}-concatSequence ::-    -- IsStream t =>-    Fold m b c -> t (Fold m a b) -> Fold m a c-concatSequence _f _p = undefined---- | Group the input stream into groups of elements between @low@ and @high@.--- Collection starts in chunks of @low@ and then keeps doubling until we reach--- @high@. Each chunk is folded using the provided fold function.------ This could be useful, for example, when we are folding a stream of unknown--- size to a stream of arrays and we want to minimize the number of--- allocations.------ NOTE: this would be an application of "many" using a terminating fold.------ /Unimplemented/----{-# INLINE chunksBetween #-}-chunksBetween :: -- Monad m =>-       Int -> Int -> Fold m a b -> Fold m b c -> Fold m a c-chunksBetween _low _high _f1 _f2 = undefined---- | A fold that buffers its input to a pure stream.------ /Warning!/ working on large streams accumulated as buffers in memory could--- be very inefficient, consider using "Streamly.Data.Array" instead.------ >>> toStream = fmap Stream.fromList Fold.toList------ /Pre-release/-{-# INLINE toStream #-}-toStream :: (Monad m, Monad n) => Fold m a (Stream n a)-toStream = fmap StreamD.fromList toList---- This is more efficient than 'toStream'. toStream is exactly the same as--- reversing the stream after toStreamRev.------ | Buffers the input stream to a pure stream in the reverse order of the--- input.------ >>> toStreamRev = fmap Stream.fromList Fold.toListRev------ /Warning!/ working on large streams accumulated as buffers in memory could--- be very inefficient, consider using "Streamly.Data.Array" instead.------ /Pre-release/----  xn : ... : x2 : x1 : []-{-# INLINE toStreamRev #-}-toStreamRev :: (Monad m, Monad n) => Fold m a (Stream n a)-toStreamRev = fmap StreamD.fromList toListRev---- XXX This does not fuse. It contains a recursive step function. We will need--- a Skip input constructor in the fold type to make it fuse.------ | Unfold and flatten the input stream of a fold.------ @--- Stream.fold (unfoldMany u f) = Stream.fold f . Stream.unfoldMany u--- @------ /Pre-release/-{-# INLINE unfoldMany #-}-unfoldMany :: Monad m => Unfold m a b -> Fold m b c -> Fold m a c-unfoldMany (Unfold ustep inject) (Fold fstep initial extract) =-    Fold consume initial extract--    where--    {-# INLINE produce #-}-    produce fs us = do-        ures <- ustep us-        case ures of-            StreamD.Yield b us1 -> do-                fres <- fstep fs b-                case fres of-                    Partial fs1 -> produce fs1 us1-                    -- XXX What to do with the remaining stream?-                    Done c -> return $ Done c-            StreamD.Skip us1 -> produce fs us1-            StreamD.Stop -> return $ Partial fs--    {-# INLINE_LATE consume #-}-    consume s a = inject a >>= produce s---- | Get the bottom most @n@ elements using the supplied comparison function.----{-# INLINE bottomBy #-}-bottomBy :: (MonadIO m, Unbox a) =>-       (a -> a -> Ordering)-    -> Int-    -> Fold m a (MutArray a)-bottomBy cmp n = Fold step initial extract--    where--    initial = do-        arr <- MA.newPinned n-        if n <= 0-        then return $ Done arr-        else return $ Partial (arr, 0)--    step (arr, i) x =-        if i < n-        then do-            arr' <- MA.snoc arr x-            MA.bubble cmp arr'-            return $ Partial (arr', i + 1)-        else do-            x1 <- MA.getIndexUnsafe (i - 1) arr-            case x `cmp` x1 of-                LT -> do-                    MA.putIndexUnsafe (i - 1) arr x-                    MA.bubble cmp arr-                    return $ Partial (arr, i)-                _ -> return $ Partial (arr, i)--    extract = return . fst---- | Get the top @n@ elements using the supplied comparison function.------ To get bottom n elements instead:------ >>> bottomBy cmp = Fold.topBy (flip cmp)------ Example:------ >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]--- >>> Stream.fold (Fold.topBy compare 3) stream >>= MutArray.toList--- [17,11,9]------ /Pre-release/----{-# INLINE topBy #-}-topBy :: (MonadIO m, Unbox a) =>-       (a -> a -> Ordering)-    -> Int-    -> Fold m a (MutArray a)-topBy cmp = bottomBy (flip cmp)---- | Fold the input stream to top n elements.------ Definition:------ >>> top = Fold.topBy compare------ >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]--- >>> Stream.fold (Fold.top 3) stream >>= MutArray.toList--- [17,11,9]------ /Pre-release/-{-# INLINE top #-}-top :: (MonadIO m, Unbox a, Ord a) => Int -> Fold m a (MutArray a)-top = bottomBy $ flip compare---- | Fold the input stream to bottom n elements.------ Definition:------ >>> bottom = Fold.bottomBy compare------ >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]--- >>> Stream.fold (Fold.bottom 3) stream >>= MutArray.toList--- [1,2,3]------ /Pre-release/-{-# INLINE bottom #-}-bottom :: (MonadIO m, Unbox a, Ord a) => Int -> Fold m a (MutArray a)-bottom = bottomBy compare----------------------------------------------------------------------------------- Interspersed parsing---------------------------------------------------------------------------------data IntersperseQState fs ps =-      IntersperseQUnquoted !fs !ps-    | IntersperseQQuoted !fs !ps-    | IntersperseQQuotedEsc !fs !ps---- Useful for parsing CSV with quoting and escaping-{-# INLINE intersperseWithQuotes #-}-intersperseWithQuotes :: (Monad m, Eq a) =>-    a -> a -> a -> Fold m a b -> Fold m b c -> Fold m a c-intersperseWithQuotes-    quote-    esc-    separator-    (Fold stepL initialL extractL)-    (Fold stepR initialR extractR) = Fold step initial extract--    where--    errMsg p status =-        error $ "intersperseWithQuotes: " ++ p ++ " parsing fold cannot "-                ++ status ++ " without input"--    {-# INLINE initL #-}-    initL mkState = do-        resL <- initialL-        case resL of-            Partial sL ->-                return $ Partial $ mkState sL-            Done _ ->-                errMsg "content" "succeed"--    initial = do-        res <- initialR-        case res of-            Partial sR -> initL (IntersperseQUnquoted sR)-            Done b -> return $ Done b--    {-# INLINE collect #-}-    collect nextS sR b = do-        res <- stepR sR b-        case res of-            Partial s ->-                initL (nextS s)-            Done c -> return (Done c)--    {-# INLINE process #-}-    process a sL sR nextState = do-        r <- stepL sL a-        case r of-            Partial s -> return $ Partial (nextState sR s)-            Done b -> collect nextState sR b--    {-# INLINE processQuoted #-}-    processQuoted a sL sR nextState = do-        r <- stepL sL a-        case r of-            Partial s -> return $ Partial (nextState sR s)-            Done _ -> error "Collecting fold finished inside quote"--    step (IntersperseQUnquoted sR sL) a-        | a == separator = do-            b <- extractL sL-            collect IntersperseQUnquoted sR b-        | a == quote = processQuoted a sL sR IntersperseQQuoted-        | otherwise = process a sL sR IntersperseQUnquoted--    step (IntersperseQQuoted sR sL) a-        | a == esc = processQuoted a sL sR IntersperseQQuotedEsc-        | a == quote = process a sL sR IntersperseQUnquoted-        | otherwise = processQuoted a sL sR IntersperseQQuoted--    step (IntersperseQQuotedEsc sR sL) a =-        processQuoted a sL sR IntersperseQQuoted--    extract (IntersperseQUnquoted sR _) = extractR sR-    extract (IntersperseQQuoted _ _) =-        error "intersperseWithQuotes: finished inside quote"-    extract (IntersperseQQuotedEsc _ _) =-        error "intersperseWithQuotes: finished inside quote, at escape char"+      module Streamly.Internal.Data.Fold.Type+    , module Streamly.Internal.Data.Fold.Tee+    , module Streamly.Internal.Data.Fold.Combinators+    , module Streamly.Internal.Data.Fold.Container+    , module Streamly.Internal.Data.Fold.Window+    )+where++import Streamly.Internal.Data.Fold.Combinators+import Streamly.Internal.Data.Fold.Container+import Streamly.Internal.Data.Fold.Tee+import Streamly.Internal.Data.Fold.Type+import Streamly.Internal.Data.Fold.Window++#include "DocTestDataFold.hs"
src/Streamly/Internal/Data/Fold/Chunked.hs view
@@ -6,7 +6,7 @@ -- Stability   : experimental -- Portability : GHC ----- Use "Streamly.Data.Parser.Chunked" instead.+-- Use "Streamly.Data.Parser" instead. -- -- Fold a stream of foreign arrays.  @Fold m a b@ in this module works -- on a stream of "Array a" and produces an output of type @b@.@@ -19,18 +19,19 @@ -- folds in Data.Fold to correctly work on an array stream as if it is an -- element stream. For example: ----- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Stream.Chunked as ArrayStream--- >>> import qualified Streamly.Internal.Data.Fold.Chunked as ChunkFold--- >>> import qualified Streamly.Data.Stream as Stream--- >>> import qualified Streamly.Data.StreamK as StreamK+-- >> import qualified Streamly.Data.Fold as Fold+-- >> import qualified Streamly.Internal.Data.Array.Stream as ArrayStream+-- >> import qualified Streamly.Internal.Data.Fold.Chunked as ChunkFold+-- >> import qualified Streamly.Data.Stream as Stream+-- >> import qualified Streamly.Data.StreamK as StreamK ----- >>> f = ChunkFold.fromFold (Fold.take 7 Fold.toList)--- >>> s = Stream.chunksOf 5 $ Stream.fromList "hello world"--- >>> ArrayStream.runArrayFold f (StreamK.fromStream s)+-- >> f = ChunkFold.fromFold (Fold.take 7 Fold.toList)+-- >> s = Stream.chunksOf 5 $ Stream.fromList "hello world"+-- >> ArrayStream.runArrayFold f (StreamK.fromStream s) -- Right "hello w" -- module Streamly.Internal.Data.Fold.Chunked+    {-# DEPRECATED "Please use Streamly.Data.Parser instead." #-}     (       ChunkFold (..) @@ -58,22 +59,22 @@  #include "ArrayMacros.h" +#if !MIN_VERSION_base(4,18,0) import Control.Applicative (liftA2)+#endif import Control.Exception (assert) import Control.Monad.IO.Class (MonadIO(..)) import Data.Bifunctor (first) import Data.Proxy (Proxy(..))-import Streamly.Internal.Data.Unboxed (peekWith, sizeOf, Unbox)+import Streamly.Internal.Data.Unbox (Unbox(..)) import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Array.Mut.Type (touch)-import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Parser.ParserD (Initial(..), Step(..))+import Streamly.Internal.Data.Array (Array(..))+import Streamly.Internal.Data.Parser (Initial(..), Step(..)) import Streamly.Internal.Data.Tuple.Strict (Tuple'(..)) -import qualified Streamly.Internal.Data.Array as Array+import qualified Streamly.Internal.Data.Array.Type as Array import qualified Streamly.Internal.Data.Fold as Fold-import qualified Streamly.Internal.Data.Parser.ParserD as ParserD-import qualified Streamly.Internal.Data.Parser.ParserD.Type as ParserD+import qualified Streamly.Internal.Data.Parser as ParserD import qualified Streamly.Internal.Data.Parser as Parser  import Prelude hiding (concatMap, take)@@ -102,8 +103,8 @@ {-# INLINE fromFold #-} fromFold :: forall m a b. (MonadIO m, Unbox a) =>     Fold.Fold m a b -> ChunkFold m a b-fromFold (Fold.Fold fstep finitial fextract) =-    ChunkFold (ParserD.Parser step initial (fmap (Done 0) . fextract))+fromFold (Fold.Fold fstep finitial _ ffinal) =+    ChunkFold (ParserD.Parser step initial extract)      where @@ -123,7 +124,7 @@             assert (cur == end) (return ())             return $ Partial 0 fs         goArray !_ !cur !fs = do-            x <- liftIO $ peekWith contents cur+            x <- liftIO $ peekAt cur contents             res <- fstep fs x             let elemSize = SIZE_OF(a)                 next = INDEX_NEXT(cur,a)@@ -133,6 +134,8 @@                 Fold.Partial fs1 ->                     goArray SPEC next fs1 +    extract = fmap (Done 0) . ffinal+ -- | Convert an element 'ParserD.Parser' into an array stream fold. If the -- parser fails the fold would throw an exception. --@@ -160,8 +163,7 @@             else return $ st (arrRem + n) fs1          goArray !_ !cur !fs = do-            x <- liftIO $ peekWith contents cur-            liftIO $ touch contents+            x <- liftIO $ peekAt cur contents             res <- step1 fs x             let elemSize = SIZE_OF(a)                 next = INDEX_NEXT(cur,a)
+ src/Streamly/Internal/Data/Fold/Combinators.hs view
@@ -0,0 +1,2544 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Fold.Combinators+-- Copyright   : (c) 2019 Composewell Technologies+--               (c) 2013 Gabriel Gonzalez+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- See "Streamly.Data.Fold" for an overview and+-- "Streamly.Internal.Data.Fold.Type" for design notes.++module Streamly.Internal.Data.Fold.Combinators+    (+    -- * Mappers+    -- | Monadic functions useful with mapM/lmapM on folds or streams.+      tracing+    , trace++    -- * Folds++    -- ** Accumulators+    -- *** Semigroups and Monoids+    , sconcat+    , mconcat+    , foldMap+    , foldMapM++    -- *** Reducers+    , drainMapM+    , the+    , length+    , lengthGeneric+    , mean+    , rollingHash+    , defaultSalt+    , rollingHashWithSalt+    , rollingHashFirstN+    -- , rollingHashLastN++    -- *** Saturating Reducers+    -- | 'product' terminates if it becomes 0. Other folds can theoretically+    -- saturate on bounded types, and therefore terminate, however, they will+    -- run forever on unbounded types like Integer/Double.+    , sum+    , product+    , maximumBy+    , maximum+    , minimumBy+    , minimum++    -- *** Collectors+    -- | Avoid using these folds in scalable or performance critical+    -- applications, they buffer all the input in GC memory which can be+    -- detrimental to performance if the input is large.+    , toListRev+    -- $toListRev+    , toStream+    , toStreamRev+    , topBy+    , top+    , bottomBy+    , bottom++    -- *** Scanners+    -- | Stateful transformation of the elements. Useful in combination with+    -- the 'scanMaybe' combinator. For scanners the result of the fold is+    -- usually a transformation of the current element rather than an+    -- aggregation of all elements till now.+    , latest+ -- , nthLast -- using Ring array+    , indexingWith+    , indexing+    , indexingRev+    , rollingMapM++    -- *** Filters+    -- | Useful in combination with the 'scanMaybe' combinator.+    , deleteBy+    , uniqBy+    , uniq+    , repeated+    , findIndices+    , elemIndices++    -- *** Singleton folds+    -- | Folds that terminate after consuming exactly one input element. All+    -- these can be implemented in terms of the 'maybe' fold.+    , one+    , null -- XXX not very useful and could be problematic, remove it?+    , satisfy+    , maybe++    -- *** Multi folds+    -- | Terminate after consuming one or more elements.+    , drainN+    -- , lastN+    -- , (!!)+    , indexGeneric+    , index+    , findM+    , find+    , lookup+    , findIndex+    , elemIndex+    , elem+    , notElem+    , all+    , any+    , and+    , or++    -- ** Trimmers+    -- | Useful in combination with the 'scanMaybe' combinator.+    , takingEndByM+    , takingEndBy+    , takingEndByM_+    , takingEndBy_+    , droppingWhileM+    , droppingWhile+    , prune++    -- * Running A Fold+    , drive+    -- , breakStream++    -- * Building Incrementally+    , addStream++    -- * Combinators+    -- ** Utilities+    , with++    -- ** Mapping on Input+    , transform++    -- ** Sliding Window+    , slide2++    -- ** Scanning Input+    , scan+    , scanMany+    , indexed++    -- ** Zipping Input+    , zipStreamWithM+    , zipStream++    -- ** Filtering Input+    , mapMaybeM+    , mapMaybe+    , sampleFromthen++    {-+    -- ** Insertion+    -- | Insertion adds more elements to the stream.++    , insertBy+    , intersperseM++    -- ** Reordering+    , reverse+    -}++    -- ** Trimming++    -- By elements+    , takeEndBy+    , takeEndBy_+    , takeEndBySeq+    , takeEndBySeq_+    {-+    , drop+    , dropWhile+    , dropWhileM+    -}++    -- ** Serial Append+    -- , tail+    -- , init+    , splitAt -- spanN+    -- , splitIn -- sessionN++    -- ** Parallel Distribution+    , tee+    , distribute+    -- , distributeFst+    -- , distributeMin++    -- ** Unzipping+    , unzip+    -- These two can be expressed using lmap/lmapM and unzip+    , unzipWith+    , unzipWithM+    , unzipWithFstM+    , unzipWithMinM++    -- ** Partitioning+    , partitionByM+    , partitionByFstM+    , partitionByMinM+    , partitionBy+    , partition++    -- ** Splitting+    , chunksBetween+    , intersperseWithQuotes++    -- ** Nesting+    , unfoldMany+    , concatSequence++    -- * Deprecated+    , drainBy+    , last+    , head+    , sequence+    , mapM+    , variance+    , stdDev+    )+where++#include "inline.hs"+#include "ArrayMacros.h"++import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Bifunctor (first)+import Data.Bits (shiftL, shiftR, (.|.), (.&.))+import Data.Either (isLeft, isRight, fromLeft, fromRight)+import Data.Int (Int64)+import Data.Proxy (Proxy(..))+import Data.Word (Word32)+import Foreign.Storable (Storable, peek)+import Streamly.Internal.Data.MutArray.Type (MutArray(..))+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)+import Streamly.Internal.Data.Pipe.Type (Pipe (..), PipeState(..))+import Streamly.Internal.Data.Unbox (Unbox, sizeOf)+import Streamly.Internal.Data.Unfold.Type (Unfold(..))+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..), Tuple3'(..))+import Streamly.Internal.Data.Stream.Type (Stream)++import qualified Prelude+import qualified Streamly.Internal.Data.MutArray.Type as MA+import qualified Streamly.Internal.Data.Array.Type as Array+import qualified Streamly.Internal.Data.Fold.Window as Fold+import qualified Streamly.Internal.Data.Pipe.Type as Pipe+import qualified Streamly.Internal.Data.Ring as Ring+import qualified Streamly.Internal.Data.Stream.Type as StreamD++import Prelude hiding+       ( Foldable(..), filter, drop, dropWhile, take, takeWhile, zipWith+       , map, mapM_, sequence, all, any+       , notElem, head, last, tail+       , reverse, iterate, init, and, or, lookup, (!!)+       , scanl, scanl1, replicate, concatMap, mconcat, unzip+       , span, splitAt, break, mapM, zip, maybe)+import Streamly.Internal.Data.Fold.Type++#include "DocTestDataFold.hs"++------------------------------------------------------------------------------+-- Running+------------------------------------------------------------------------------++-- | Drive a fold using the supplied 'Stream', reducing the resulting+-- expression strictly at each step.+--+-- Definition:+--+-- >>> drive = flip Stream.fold+--+-- Example:+--+-- >>> Fold.drive (Stream.enumerateFromTo 1 100) Fold.sum+-- 5050+--+{-# INLINE drive #-}+drive :: Monad m => Stream m a -> Fold m a b -> m b+drive = flip StreamD.fold++{-+-- | Like 'drive' but also returns the remaining stream. The resulting stream+-- would be 'Stream.nil' if the stream finished before the fold.+--+-- Definition:+--+-- >>> breakStream = flip Stream.foldBreak+--+-- /CPS/+--+{-# INLINE breakStreamK #-}+breakStreamK :: Monad m => StreamK m a -> Fold m a b -> m (b, StreamK m a)+breakStreamK strm fl = fmap f $ K.foldBreak fl (Stream.toStreamK strm)++    where++    f (b, str) = (b, Stream.fromStreamK str)+-}++-- | Append a stream to a fold to build the fold accumulator incrementally. We+-- can repeatedly call 'addStream' on the same fold to continue building the+-- fold and finally use 'drive' to finish the fold and extract the result. Also+-- see the 'Streamly.Data.Fold.addOne' operation which is a singleton version+-- of 'addStream'.+--+-- Definitions:+--+-- >>> addStream stream = Fold.drive stream . Fold.duplicate+--+-- Example, build a list incrementally:+--+-- >>> :{+-- pure (Fold.toList :: Fold IO Int [Int])+--     >>= Fold.addOne 1+--     >>= Fold.addStream (Stream.enumerateFromTo 2 4)+--     >>= Fold.drive Stream.nil+--     >>= print+-- :}+-- [1,2,3,4]+--+-- This can be used as an O(n) list append compared to the O(n^2) @++@ when+-- used for incrementally building a list.+--+-- Example, build a stream incrementally:+--+-- >>> :{+-- pure (Fold.toStream :: Fold IO Int (Stream Identity Int))+--     >>= Fold.addOne 1+--     >>= Fold.addStream (Stream.enumerateFromTo 2 4)+--     >>= Fold.drive Stream.nil+--     >>= print+-- :}+-- fromList [1,2,3,4]+--+-- This can be used as an O(n) stream append compared to the O(n^2) @<>@ when+-- used for incrementally building a stream.+--+-- Example, build an array incrementally:+--+-- >>> :{+-- pure (Array.write :: Fold IO Int (Array Int))+--     >>= Fold.addOne 1+--     >>= Fold.addStream (Stream.enumerateFromTo 2 4)+--     >>= Fold.drive Stream.nil+--     >>= print+-- :}+-- fromList [1,2,3,4]+--+-- Example, build an array stream incrementally:+--+-- >>> :{+-- let f :: Fold IO Int (Stream Identity (Array Int))+--     f = Fold.groupsOf 2 (Array.writeN 3) Fold.toStream+-- in pure f+--     >>= Fold.addOne 1+--     >>= Fold.addStream (Stream.enumerateFromTo 2 4)+--     >>= Fold.drive Stream.nil+--     >>= print+-- :}+-- fromList [fromList [1,2],fromList [3,4]]+--+addStream :: Monad m => Stream m a -> Fold m a b -> m (Fold m a b)+addStream stream = drive stream . duplicate++------------------------------------------------------------------------------+-- Transformations on fold inputs+------------------------------------------------------------------------------++-- | Flatten the monadic output of a fold to pure output.+--+{-# DEPRECATED sequence "Use \"rmapM id\" instead" #-}+{-# INLINE sequence #-}+sequence :: Monad m => Fold m a (m b) -> Fold m a b+sequence = rmapM id++-- | Map a monadic function on the output of a fold.+--+{-# DEPRECATED mapM "Use rmapM instead" #-}+{-# INLINE mapM #-}+mapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c+mapM = rmapM++-- |+-- >>> mapMaybeM f = Fold.lmapM f . Fold.catMaybes+--+{-# INLINE mapMaybeM #-}+mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Fold m b r -> Fold m a r+mapMaybeM f = lmapM f . catMaybes++-- | @mapMaybe f fold@ maps a 'Maybe' returning function @f@ on the input of+-- the fold, filters out 'Nothing' elements, and return the values extracted+-- from 'Just'.+--+-- >>> mapMaybe f = Fold.lmap f . Fold.catMaybes+-- >>> mapMaybe f = Fold.mapMaybeM (return . f)+--+-- >>> f x = if even x then Just x else Nothing+-- >>> fld = Fold.mapMaybe f Fold.toList+-- >>> Stream.fold fld (Stream.enumerateFromTo 1 10)+-- [2,4,6,8,10]+--+{-# INLINE mapMaybe #-}+mapMaybe :: Monad m => (a -> Maybe b) -> Fold m b r -> Fold m a r+mapMaybe f = lmap f . catMaybes++------------------------------------------------------------------------------+-- Transformations on fold inputs+------------------------------------------------------------------------------++-- | Apply a monadic function on the input and return the input.+--+-- >>> Stream.fold (Fold.lmapM (Fold.tracing print) Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)+-- 1+-- 2+--+-- /Pre-release/+--+{-# INLINE tracing #-}+tracing :: Monad m => (a -> m b) -> (a -> m a)+tracing f x = void (f x) >> return x++-- | Apply a monadic function to each element flowing through and discard the+-- results.+--+-- >>> Stream.fold (Fold.trace print Fold.drain) $ (Stream.enumerateFromTo (1 :: Int) 2)+-- 1+-- 2+--+-- >>> trace f = Fold.lmapM (Fold.tracing f)+--+-- /Pre-release/+{-# INLINE trace #-}+trace :: Monad m => (a -> m b) -> Fold m a r -> Fold m a r+trace f = lmapM (tracing f)++-- rename to lpipe?+--+-- | Apply a transformation on a 'Fold' using a 'Pipe'.+--+-- /Pre-release/+{-# INLINE transform #-}+transform :: Monad m => Pipe m a b -> Fold m b c -> Fold m a c+transform (Pipe pstep1 pstep2 pinitial) (Fold fstep finitial fextract ffinal) =+    Fold step initial extract final++    where++    initial = first (Tuple' pinitial) <$> finitial++    step (Tuple' ps fs) x = do+        r <- pstep1 ps x+        go fs r++        where++        -- XXX use SPEC?+        go acc (Pipe.Yield b (Consume ps')) = do+            acc' <- fstep acc b+            return+                $ case acc' of+                      Partial s -> Partial $ Tuple' ps' s+                      Done b2 -> Done b2+        go acc (Pipe.Yield b (Produce ps')) = do+            acc' <- fstep acc b+            r <- pstep2 ps'+            case acc' of+                Partial s -> go s r+                Done b2 -> return $ Done b2+        go acc (Pipe.Continue (Consume ps')) =+            return $ Partial $ Tuple' ps' acc+        go acc (Pipe.Continue (Produce ps')) = do+            r <- pstep2 ps'+            go acc r++    extract (Tuple' _ fs) = fextract fs++    final (Tuple' _ fs) = ffinal fs++{-# INLINE scanWith #-}+scanWith :: Monad m => Bool -> Fold m a b -> Fold m b c -> Fold m a c+scanWith isMany+    (Fold stepL initialL extractL finalL)+    (Fold stepR initialR extractR finalR) =+    Fold step initial extract final++    where++    {-# INLINE runStep #-}+    runStep actionL sR = do+        rL <- actionL+        case rL of+            Done bL -> do+                rR <- stepR sR bL+                case rR of+                    Partial sR1 ->+                        if isMany+                        then runStep initialL sR1+                        else Done <$> finalR sR1+                    Done bR -> return $ Done bR+            Partial sL -> do+                !b <- extractL sL+                rR <- stepR sR b+                case rR of+                    Partial sR1 -> return $ Partial (sL, sR1)+                    Done bR -> finalL sL >> return (Done bR)++    initial = do+        r <- initialR+        case r of+            Partial sR -> runStep initialL sR+            Done b -> return $ Done b++    step (sL, sR) x = runStep (stepL sL x) sR++    extract = extractR . snd++    final (sL, sR) = finalL sL *> finalR sR++-- | Scan the input of a 'Fold' to change it in a stateful manner using another+-- 'Fold'. The scan stops as soon as the fold terminates.+--+-- /Pre-release/+{-# INLINE scan #-}+scan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c+scan = scanWith False++-- XXX This does not fuse beacuse of the recursive step. Need to investigate.+--+-- | Scan the input of a 'Fold' to change it in a stateful manner using another+-- 'Fold'. The scan restarts with a fresh state if the fold terminates.+--+-- /Pre-release/+{-# INLINE scanMany #-}+scanMany :: Monad m => Fold m a b -> Fold m b c -> Fold m a c+scanMany = scanWith True++------------------------------------------------------------------------------+-- Filters+------------------------------------------------------------------------------++-- | Returns the latest element omitting the first occurrence that satisfies+-- the given equality predicate.+--+-- Example:+--+-- >>> input = Stream.fromList [1,3,3,5]+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.deleteBy (==) 3) input+-- [1,3,5]+--+{-# INLINE_NORMAL deleteBy #-}+deleteBy :: Monad m => (a -> a -> Bool) -> a -> Fold m a (Maybe a)+deleteBy eq x0 = fmap extract $ foldl' step (Tuple' False Nothing)++    where++    step (Tuple' False _) x =+        if eq x x0+        then Tuple' True Nothing+        else Tuple' False (Just x)+    step (Tuple' True _) x = Tuple' True (Just x)++    extract (Tuple' _ x) = x++-- | Provide a sliding window of length 2 elements.+--+-- See "Streamly.Internal.Data.Fold.Window".+--+{-# INLINE slide2 #-}+slide2 :: Monad m => Fold m (a, Maybe a) b -> Fold m a b+slide2 (Fold step1 initial1 extract1 final1) = Fold step initial extract final++    where++    initial =+        first (Tuple' Nothing) <$> initial1++    step (Tuple' prev s) cur =+        first (Tuple' (Just cur)) <$> step1 s (cur, prev)++    extract (Tuple' _ s) = extract1 s++    final (Tuple' _ s) = final1 s++-- | Return the latest unique element using the supplied comparison function.+-- Returns 'Nothing' if the current element is same as the last element+-- otherwise returns 'Just'.+--+-- Example, strip duplicate path separators:+--+-- >>> input = Stream.fromList "//a//b"+-- >>> f x y = x == '/' && y == '/'+-- >>> Stream.fold Fold.toList $ Stream.scanMaybe (Fold.uniqBy f) input+-- "/a/b"+--+-- Space: @O(1)@+--+-- /Pre-release/+--+{-# INLINE uniqBy #-}+uniqBy :: Monad m => (a -> a -> Bool) -> Fold m a (Maybe a)+uniqBy eq = rollingMap f++    where++    f pre curr =+        case pre of+            Nothing -> Just curr+            Just x -> if x `eq` curr then Nothing else Just curr++-- | See 'uniqBy'.+--+-- Definition:+--+-- >>> uniq = Fold.uniqBy (==)+--+{-# INLINE uniq #-}+uniq :: (Monad m, Eq a) => Fold m a (Maybe a)+uniq = uniqBy (==)++-- | Strip all leading and trailing occurrences of an element passing a+-- predicate and make all other consecutive occurrences uniq.+--+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)+--+-- @+-- > Stream.prune isSpace (Stream.fromList "  hello      world!   ")+-- "hello world!"+--+-- @+--+-- Space: @O(1)@+--+-- /Unimplemented/+{-# INLINE prune #-}+prune ::+    -- (Monad m, Eq a) =>+    (a -> Bool) -> Fold m a (Maybe a)+prune = error "Not implemented yet!"++-- | Emit only repeated elements, once.+--+-- /Unimplemented/+repeated :: -- (Monad m, Eq a) =>+    Fold m a (Maybe a)+repeated = error "Not implemented yet!"++------------------------------------------------------------------------------+-- Left folds+------------------------------------------------------------------------------++------------------------------------------------------------------------------+-- Run Effects+------------------------------------------------------------------------------++-- |+-- Definitions:+--+-- >>> drainMapM f = Fold.lmapM f Fold.drain+-- >>> drainMapM f = Fold.foldMapM (void . f)+--+-- Drain all input after passing it through a monadic function. This is the+-- dual of mapM_ on stream producers.+--+{-# INLINE drainMapM #-}+drainMapM ::  Monad m => (a -> m b) -> Fold m a ()+drainMapM f = lmapM f drain++{-# DEPRECATED drainBy "Please use 'drainMapM' instead." #-}+{-# INLINE drainBy #-}+drainBy ::  Monad m => (a -> m b) -> Fold m a ()+drainBy = drainMapM++-- | Returns the latest element of the input stream, if any.+--+-- >>> latest = Fold.foldl1' (\_ x -> x)+-- >>> latest = fmap getLast $ Fold.foldMap (Last . Just)+--+{-# INLINE latest #-}+latest :: Monad m => Fold m a (Maybe a)+latest = foldl1' (\_ x -> x)++{-# DEPRECATED last "Please use 'latest' instead." #-}+{-# INLINE last #-}+last :: Monad m => Fold m a (Maybe a)+last = latest++-- | Terminates with 'Nothing' as soon as it finds an element different than+-- the previous one, returns 'the' element if the entire input consists of the+-- same element.+--+{-# INLINE the #-}+the :: (Monad m, Eq a) => Fold m a (Maybe a)+the = foldt' step initial id++    where++    initial = Partial Nothing++    step Nothing x = Partial (Just x)+    step old@(Just x0) x =+            if x0 == x+            then Partial old+            else Done Nothing++------------------------------------------------------------------------------+-- To Summary+------------------------------------------------------------------------------++-- | Like 'length', except with a more general 'Num' return value+--+-- Definition:+--+-- >>> lengthGeneric = fmap getSum $ Fold.foldMap (Sum . const  1)+-- >>> lengthGeneric = Fold.foldl' (\n _ -> n + 1) 0+--+-- /Pre-release/+{-# INLINE lengthGeneric #-}+lengthGeneric :: (Monad m, Num b) => Fold m a b+lengthGeneric = foldl' (\n _ -> n + 1) 0++-- | Determine the length of the input stream.+--+-- Definition:+--+-- >>> length = Fold.lengthGeneric+-- >>> length = fmap getSum $ Fold.foldMap (Sum . const  1)+--+{-# INLINE length #-}+length :: Monad m => Fold m a Int+length = lengthGeneric+++-- | Determine the sum of all elements of a stream of numbers. Returns additive+-- identity (@0@) when the stream is empty. Note that this is not numerically+-- stable for floating point numbers.+--+-- >>> sum = Fold.cumulative Fold.windowSum+--+-- Same as following but numerically stable:+--+-- >>> sum = Fold.foldl' (+) 0+-- >>> sum = fmap Data.Monoid.getSum $ Fold.foldMap Data.Monoid.Sum+--+{-# INLINE sum #-}+sum :: (Monad m, Num a) => Fold m a a+sum = Fold.cumulative Fold.windowSum++-- | Determine the product of all elements of a stream of numbers. Returns+-- multiplicative identity (@1@) when the stream is empty. The fold terminates+-- when it encounters (@0@) in its input.+--+-- Same as the following but terminates on multiplication by @0@:+--+-- >>> product = fmap Data.Monoid.getProduct $ Fold.foldMap Data.Monoid.Product+--+{-# INLINE product #-}+product :: (Monad m, Num a, Eq a) => Fold m a a+product =  foldt' step (Partial 1) id++    where++    step x a =+        if a == 0+        then Done 0+        else Partial $ x * a++------------------------------------------------------------------------------+-- To Summary (Maybe)+------------------------------------------------------------------------------++-- | Determine the maximum element in a stream using the supplied comparison+-- function.+--+{-# INLINE maximumBy #-}+maximumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)+maximumBy cmp = foldl1' max'++    where++    max' x y =+        case cmp x y of+            GT -> x+            _ -> y++-- | Determine the maximum element in a stream.+--+-- Definitions:+--+-- >>> maximum = Fold.maximumBy compare+-- >>> maximum = Fold.foldl1' max+--+-- Same as the following but without a default maximum. The 'Max' Monoid uses+-- the 'minBound' as the default maximum:+--+-- >>> maximum = fmap Data.Semigroup.getMax $ Fold.foldMap Data.Semigroup.Max+--+{-# INLINE maximum #-}+maximum :: (Monad m, Ord a) => Fold m a (Maybe a)+maximum = foldl1' max++-- | Computes the minimum element with respect to the given comparison function+--+{-# INLINE minimumBy #-}+minimumBy :: Monad m => (a -> a -> Ordering) -> Fold m a (Maybe a)+minimumBy cmp = foldl1' min'++    where++    min' x y =+        case cmp x y of+            GT -> y+            _ -> x++-- | Determine the minimum element in a stream using the supplied comparison+-- function.+--+-- Definitions:+--+-- >>> minimum = Fold.minimumBy compare+-- >>> minimum = Fold.foldl1' min+--+-- Same as the following but without a default minimum. The 'Min' Monoid uses the+-- 'maxBound' as the default maximum:+--+-- >>> maximum = fmap Data.Semigroup.getMin $ Fold.foldMap Data.Semigroup.Min+--+{-# INLINE minimum #-}+minimum :: (Monad m, Ord a) => Fold m a (Maybe a)+minimum = foldl1' min++------------------------------------------------------------------------------+-- To Summary (Statistical)+------------------------------------------------------------------------------++-- | Compute a numerically stable arithmetic mean of all elements in the input+-- stream.+--+{-# INLINE mean #-}+mean :: (Monad m, Fractional a) => Fold m a a+mean = fmap done $ foldl' step begin++    where++    begin = Tuple' 0 0++    step (Tuple' x n) y =+        let n1 = n + 1+         in Tuple' (x + (y - x) / n1) n1++    done (Tuple' x _) = x++-- | Compute a numerically stable (population) variance over all elements in+-- the input stream.+--+{-# DEPRECATED variance "Use the streamly-statistics package instead" #-}+{-# INLINE variance #-}+variance :: (Monad m, Fractional a) => Fold m a a+variance = fmap done $ foldl' step begin++    where++    begin = Tuple3' 0 0 0++    step (Tuple3' n mean_ m2) x = Tuple3' n' mean' m2'++        where++        n' = n + 1+        mean' = (n * mean_ + x) / (n + 1)+        delta = x - mean_+        m2' = m2 + delta * delta * n / (n + 1)++    done (Tuple3' n _ m2) = m2 / n++-- | Compute a numerically stable (population) standard deviation over all+-- elements in the input stream.+--+{-# DEPRECATED stdDev "Use the streamly-statistics package instead" #-}+{-# INLINE stdDev #-}+stdDev :: (Monad m, Floating a) => Fold m a a+stdDev = sqrt <$> variance++-- | Compute an 'Int' sized polynomial rolling hash+--+-- > H = salt * k ^ n + c1 * k ^ (n - 1) + c2 * k ^ (n - 2) + ... + cn * k ^ 0+--+-- Where @c1@, @c2@, @cn@ are the elements in the input stream and @k@ is a+-- constant.+--+-- This hash is often used in Rabin-Karp string search algorithm.+--+-- See https://en.wikipedia.org/wiki/Rolling_hash+--+{-# INLINE rollingHashWithSalt #-}+rollingHashWithSalt :: (Monad m, Enum a) => Int64 -> Fold m a Int64+rollingHashWithSalt = foldl' step++    where++    k = 2891336453 :: Int64++    step cksum a = cksum * k + fromIntegral (fromEnum a)++-- | A default salt used in the implementation of 'rollingHash'.+{-# INLINE defaultSalt #-}+defaultSalt :: Int64+defaultSalt = -2578643520546668380++-- | Compute an 'Int' sized polynomial rolling hash of a stream.+--+-- >>> rollingHash = Fold.rollingHashWithSalt Fold.defaultSalt+--+{-# INLINE rollingHash #-}+rollingHash :: (Monad m, Enum a) => Fold m a Int64+rollingHash = rollingHashWithSalt defaultSalt++-- | Compute an 'Int' sized polynomial rolling hash of the first n elements of+-- a stream.+--+-- >>> rollingHashFirstN n = Fold.take n Fold.rollingHash+--+-- /Pre-release/+{-# INLINE rollingHashFirstN #-}+rollingHashFirstN :: (Monad m, Enum a) => Int -> Fold m a Int64+rollingHashFirstN n = take n rollingHash++-- XXX Compare this with the implementation in Fold.Window, preferrably use the+-- latter if performance is good.++-- | Apply a function on every two successive elements of a stream. The first+-- argument of the map function is the previous element and the second argument+-- is the current element. When processing the very first element in the+-- stream, the previous element is 'Nothing'.+--+-- /Pre-release/+--+{-# INLINE rollingMapM #-}+rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Fold m a b+rollingMapM f = Fold step initial extract extract++    where++    -- XXX We need just a postscan. We do not need an initial result here.+    -- Or we can supply a default initial result as an argument to rollingMapM.+    initial = return $ Partial (Nothing, error "Empty stream")++    step (prev, _) cur = do+        x <- f prev cur+        return $ Partial (Just cur, x)++    extract = return . snd++-- |+-- >>> rollingMap f = Fold.rollingMapM (\x y -> return $ f x y)+--+{-# INLINE rollingMap #-}+rollingMap :: Monad m => (Maybe a -> a -> b) -> Fold m a b+rollingMap f = rollingMapM (\x y -> return $ f x y)++------------------------------------------------------------------------------+-- Monoidal left folds+------------------------------------------------------------------------------++-- | Semigroup concat. Append the elements of an input stream to a provided+-- starting value.+--+-- Definition:+--+-- >>> sconcat = Fold.foldl' (<>)+--+-- >>> semigroups = fmap Data.Monoid.Sum $ Stream.enumerateFromTo 1 10+-- >>> Stream.fold (Fold.sconcat 10) semigroups+-- Sum {getSum = 65}+--+{-# INLINE sconcat #-}+sconcat :: (Monad m, Semigroup a) => a -> Fold m a a+sconcat = foldl' (<>)++-- | Monoid concat. Fold an input stream consisting of monoidal elements using+-- 'mappend' and 'mempty'.+--+-- Definition:+--+-- >>> mconcat = Fold.sconcat mempty+--+-- >>> monoids = fmap Data.Monoid.Sum $ Stream.enumerateFromTo 1 10+-- >>> Stream.fold Fold.mconcat monoids+-- Sum {getSum = 55}+--+{-# INLINE mconcat #-}+mconcat ::+    ( Monad m+    , Monoid a) => Fold m a a+mconcat = sconcat mempty++-- |+-- Definition:+--+-- >>> foldMap f = Fold.lmap f Fold.mconcat+--+-- Make a fold from a pure function that folds the output of the function+-- using 'mappend' and 'mempty'.+--+-- >>> sum = Fold.foldMap Data.Monoid.Sum+-- >>> Stream.fold sum $ Stream.enumerateFromTo 1 10+-- Sum {getSum = 55}+--+{-# INLINE foldMap #-}+foldMap :: (Monad m, Monoid b) => (a -> b) -> Fold m a b+foldMap f = lmap f mconcat++-- |+-- Definition:+--+-- >>> foldMapM f = Fold.lmapM f Fold.mconcat+--+-- Make a fold from a monadic function that folds the output of the function+-- using 'mappend' and 'mempty'.+--+-- >>> sum = Fold.foldMapM (return . Data.Monoid.Sum)+-- >>> Stream.fold sum $ Stream.enumerateFromTo 1 10+-- Sum {getSum = 55}+--+{-# INLINE foldMapM #-}+foldMapM ::  (Monad m, Monoid b) => (a -> m b) -> Fold m a b+foldMapM act = foldlM' step (pure mempty)++    where++    step m a = do+        m' <- act a+        return $! mappend m m'++------------------------------------------------------------------------------+-- To Containers+------------------------------------------------------------------------------++-- $toListRev+-- This is more efficient than 'Streamly.Internal.Data.Fold.toList'. toList is+-- exactly the same as reversing the list after 'toListRev'.++-- | Buffers the input stream to a list in the reverse order of the input.+--+-- Definition:+--+-- >>> toListRev = Fold.foldl' (flip (:)) []+--+-- /Warning!/ working on large lists accumulated as buffers in memory could be+-- very inefficient, consider using "Streamly.Array" instead.+--++--  xn : ... : x2 : x1 : []+{-# INLINE toListRev #-}+toListRev :: Monad m => Fold m a [a]+toListRev = foldl' (flip (:)) []++------------------------------------------------------------------------------+-- Partial Folds+------------------------------------------------------------------------------++-- | A fold that drains the first n elements of its input, running the effects+-- and discarding the results.+--+-- Definition:+--+-- >>> drainN n = Fold.take n Fold.drain+--+-- /Pre-release/+{-# INLINE drainN #-}+drainN :: Monad m => Int -> Fold m a ()+drainN n = take n drain++------------------------------------------------------------------------------+-- To Elements+------------------------------------------------------------------------------++-- | Like 'index', except with a more general 'Integral' argument+--+-- /Pre-release/+{-# INLINE indexGeneric #-}+indexGeneric :: (Integral i, Monad m) => i -> Fold m a (Maybe a)+indexGeneric i = foldt' step (Partial 0) (const Nothing)++    where++    step j a =+        if i == j+        then Done $ Just a+        else Partial (j + 1)++-- | Return the element at the given index.+--+-- Definition:+--+-- >>> index = Fold.indexGeneric+--+{-# INLINE index #-}+index :: Monad m => Int -> Fold m a (Maybe a)+index = indexGeneric++-- | Consume a single input and transform it using the supplied 'Maybe'+-- returning function.+--+-- /Pre-release/+--+{-# INLINE maybe #-}+maybe :: Monad m => (a -> Maybe b) -> Fold m a (Maybe b)+maybe f = foldt' (const (Done . f)) (Partial Nothing) id++-- | Consume a single element and return it if it passes the predicate else+-- return 'Nothing'.+--+-- Definition:+--+-- >>> satisfy f = Fold.maybe (\a -> if f a then Just a else Nothing)+--+-- /Pre-release/+{-# INLINE satisfy #-}+satisfy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)+satisfy f = maybe (\a -> if f a then Just a else Nothing)+{-+satisfy f = Fold step (return $ Partial ()) (const (return Nothing))++    where++    step () a = return $ Done $ if f a then Just a else Nothing+-}++-- Naming notes:+--+-- "head" and "next" are two alternative names for the same API. head sounds+-- apt in the context of lists but next sounds more apt in the context of+-- streams where we think in terms of generating and consuming the next element+-- rather than taking the head of some static/persistent structure.+--+-- We also want to keep the nomenclature consistent across folds and parsers,+-- "head" becomes even more unintuitive for parsers because there are two+-- possible variants viz. peek and next.+--+-- Also, the "head" fold creates confusion in situations like+-- https://github.com/composewell/streamly/issues/1404 where intuitive+-- expectation from head is to consume the entire stream and just give us the+-- head. There we want to convey the notion that we consume one element from+-- the stream and stop. The name "one" already being used in parsers for this+-- purpose sounds more apt from this perspective.+--+-- The source of confusion is perhaps due to the fact that some folds consume+-- the entire stream and others terminate early. It may have been clearer if we+-- had separate abstractions for the two use cases.++-- XXX We can possibly use "head" for the purposes of reducing the entire+-- stream to the head element i.e. take the head and drain the rest.++-- | Take one element from the stream and stop.+--+-- Definition:+--+-- >>> one = Fold.maybe Just+--+-- This is similar to the stream 'Stream.uncons' operation.+--+{-# INLINE one #-}+one :: Monad m => Fold m a (Maybe a)+one = maybe Just++-- | Extract the first element of the stream, if any.+--+-- >>> head = Fold.one+--+{-# DEPRECATED head "Please use \"one\" instead" #-}+{-# INLINE head #-}+head :: Monad m => Fold m a (Maybe a)+head = one++-- | Returns the first element that satisfies the given predicate.+--+-- /Pre-release/+{-# INLINE findM #-}+findM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)+findM predicate =+    Fold step (return $ Partial ()) extract extract++    where++    step () a =+        let f r =+                if r+                then Done (Just a)+                else Partial ()+         in f <$> predicate a++    extract = const $ return Nothing++-- | Returns the first element that satisfies the given predicate.+--+{-# INLINE find #-}+find :: Monad m => (a -> Bool) -> Fold m a (Maybe a)+find p = findM (return . p)++-- | In a stream of (key-value) pairs @(a, b)@, return the value @b@ of the+-- first pair where the key equals the given value @a@.+--+-- Definition:+--+-- >>> lookup x = fmap snd <$> Fold.find ((== x) . fst)+--+{-# INLINE lookup #-}+lookup :: (Eq a, Monad m) => a -> Fold m (a,b) (Maybe b)+lookup a0 = foldt' step (Partial ()) (const Nothing)++    where++    step () (a, b) =+        if a == a0+        then Done $ Just b+        else Partial ()++-- | Returns the first index that satisfies the given predicate.+--+{-# INLINE findIndex #-}+findIndex :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)+findIndex predicate = foldt' step (Partial 0) (const Nothing)++    where++    step i a =+        if predicate a+        then Done $ Just i+        else Partial (i + 1)++-- | Returns the index of the latest element if the element satisfies the given+-- predicate.+--+{-# INLINE findIndices #-}+findIndices :: Monad m => (a -> Bool) -> Fold m a (Maybe Int)+findIndices predicate =+    -- XXX implement by combining indexing and filtering scans+    fmap (either (const Nothing) Just) $ foldl' step (Left (-1))++    where++    step i a =+        if predicate a+        then Right (either id id i + 1)+        else Left (either id id i + 1)++-- | Returns the index of the latest element if the element matches the given+-- value.+--+-- Definition:+--+-- >>> elemIndices a = Fold.findIndices (== a)+--+{-# INLINE elemIndices #-}+elemIndices :: (Monad m, Eq a) => a -> Fold m a (Maybe Int)+elemIndices a = findIndices (== a)++-- | Returns the first index where a given value is found in the stream.+--+-- Definition:+--+-- >>> elemIndex a = Fold.findIndex (== a)+--+{-# INLINE elemIndex #-}+elemIndex :: (Eq a, Monad m) => a -> Fold m a (Maybe Int)+elemIndex a = findIndex (== a)++------------------------------------------------------------------------------+-- To Boolean+------------------------------------------------------------------------------++-- Similar to 'eof' parser, but the fold consumes and discards an input element+-- when not at eof. XXX Remove or Rename to "eof"?++-- | Consume one element, return 'True' if successful else return 'False'. In+-- other words, test if the input is empty or not.+--+-- WARNING! It consumes one element if the stream is not empty. If that is not+-- what you want please use the eof parser instead.+--+-- Definition:+--+-- >>> null = fmap isJust Fold.one+--+{-# INLINE null #-}+null :: Monad m => Fold m a Bool+null = foldt' (\() _ -> Done False) (Partial ()) (const True)++-- | Returns 'True' if any element of the input satisfies the predicate.+--+-- Definition:+--+-- >>> any p = Fold.lmap p Fold.or+--+-- Example:+--+-- >>> Stream.fold (Fold.any (== 0)) $ Stream.fromList [1,0,1]+-- True+--+{-# INLINE any #-}+any :: Monad m => (a -> Bool) -> Fold m a Bool+any predicate = foldt' step initial id++    where++    initial = Partial False++    step _ a =+        if predicate a+        then Done True+        else Partial False++-- | Return 'True' if the given element is present in the stream.+--+-- Definition:+--+-- >>> elem a = Fold.any (== a)+--+{-# INLINE elem #-}+elem :: (Eq a, Monad m) => a -> Fold m a Bool+elem a = any (== a)++-- | Returns 'True' if all elements of the input satisfy the predicate.+--+-- Definition:+--+-- >>> all p = Fold.lmap p Fold.and+--+-- Example:+--+-- >>> Stream.fold (Fold.all (== 0)) $ Stream.fromList [1,0,1]+-- False+--+{-# INLINE all #-}+all :: Monad m => (a -> Bool) -> Fold m a Bool+all predicate = foldt' step initial id++    where++    initial = Partial True++    step _ a =+        if predicate a+        then Partial True+        else Done False++-- | Returns 'True' if the given element is not present in the stream.+--+-- Definition:+--+-- >>> notElem a = Fold.all (/= a)+--+{-# INLINE notElem #-}+notElem :: (Eq a, Monad m) => a -> Fold m a Bool+notElem a = all (/= a)++-- | Returns 'True' if all elements are 'True', 'False' otherwise+--+-- Definition:+--+-- >>> and = Fold.all (== True)+--+{-# INLINE and #-}+and :: Monad m => Fold m Bool Bool+and = all (== True)++-- | Returns 'True' if any element is 'True', 'False' otherwise+--+-- Definition:+--+-- >>> or = Fold.any (== True)+--+{-# INLINE or #-}+or :: Monad m => Fold m Bool Bool+or = any (== True)++------------------------------------------------------------------------------+-- Grouping/Splitting+------------------------------------------------------------------------------++------------------------------------------------------------------------------+-- Grouping without looking at elements+------------------------------------------------------------------------------++------------------------------------------------------------------------------+-- Binary APIs+------------------------------------------------------------------------------++-- | @splitAt n f1 f2@ composes folds @f1@ and @f2@ such that first @n@+-- elements of its input are consumed by fold @f1@ and the rest of the stream+-- is consumed by fold @f2@.+--+-- >>> let splitAt_ n xs = Stream.fold (Fold.splitAt n Fold.toList Fold.toList) $ Stream.fromList xs+--+-- >>> splitAt_ 6 "Hello World!"+-- ("Hello ","World!")+--+-- >>> splitAt_ (-1) [1,2,3]+-- ([],[1,2,3])+--+-- >>> splitAt_ 0 [1,2,3]+-- ([],[1,2,3])+--+-- >>> splitAt_ 1 [1,2,3]+-- ([1],[2,3])+--+-- >>> splitAt_ 3 [1,2,3]+-- ([1,2,3],[])+--+-- >>> splitAt_ 4 [1,2,3]+-- ([1,2,3],[])+--+-- > splitAt n f1 f2 = Fold.splitWith (,) (Fold.take n f1) f2+--+-- /Internal/++{-# INLINE splitAt #-}+splitAt+    :: Monad m+    => Int+    -> Fold m a b+    -> Fold m a c+    -> Fold m a (b, c)+splitAt n fld = splitWith (,) (take n fld)++------------------------------------------------------------------------------+-- Element Aware APIs+------------------------------------------------------------------------------+--+------------------------------------------------------------------------------+-- Binary APIs+------------------------------------------------------------------------------++{-# INLINE takingEndByM #-}+takingEndByM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)+takingEndByM p = Fold step initial extract extract++    where++    initial = return $ Partial Nothing'++    step _ a = do+        r <- p a+        return+            $ if r+              then Done $ Just a+              else Partial $ Just' a++    extract = return . toMaybe++-- |+--+-- >>> takingEndBy p = Fold.takingEndByM (return . p)+--+{-# INLINE takingEndBy #-}+takingEndBy :: Monad m => (a -> Bool) -> Fold m a (Maybe a)+takingEndBy p = takingEndByM (return . p)++{-# INLINE takingEndByM_ #-}+takingEndByM_ :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)+takingEndByM_ p = Fold step initial extract extract++    where++    initial = return $ Partial Nothing'++    step _ a = do+        r <- p a+        return+            $ if r+              then Done Nothing+              else Partial $ Just' a++    extract = return . toMaybe++-- |+--+-- >>> takingEndBy_ p = Fold.takingEndByM_ (return . p)+--+{-# INLINE takingEndBy_ #-}+takingEndBy_ :: Monad m => (a -> Bool) -> Fold m a (Maybe a)+takingEndBy_ p = takingEndByM_ (return . p)++{-# INLINE droppingWhileM #-}+droppingWhileM :: Monad m => (a -> m Bool) -> Fold m a (Maybe a)+droppingWhileM p = Fold step initial extract extract++    where++    initial = return $ Partial Nothing'++    step Nothing' a = do+        r <- p a+        return+            $ Partial+            $ if r+              then Nothing'+              else Just' a+    step _ a = return $ Partial $ Just' a++    extract = return . toMaybe++-- |+-- >>> droppingWhile p = Fold.droppingWhileM (return . p)+--+{-# INLINE droppingWhile #-}+droppingWhile :: Monad m => (a -> Bool) -> Fold m a (Maybe a)+droppingWhile p = droppingWhileM (return . p)++-- Note: Keep this consistent with S.splitOn. In fact we should eliminate+-- S.splitOn in favor of the fold.+--+-- XXX Use Fold.many instead once it is fixed.+-- > Stream.splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)++-- | Like 'takeEndBy' but drops the element on which the predicate succeeds.+--+-- Example:+--+-- >>> input = Stream.fromList "hello\nthere\n"+-- >>> line = Fold.takeEndBy_ (== '\n') Fold.toList+-- >>> Stream.fold line input+-- "hello"+--+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input+-- ["hello","there"]+--+{-# INLINE takeEndBy_ #-}+takeEndBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b+-- takeEndBy_ predicate = scanMaybe (takingEndBy_ predicate)+takeEndBy_ predicate (Fold fstep finitial fextract ffinal) =+    Fold step finitial fextract ffinal++    where++    step s a =+        if not (predicate a)+        then fstep s a+        else Done <$> ffinal s++-- Note:+-- > Stream.splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)++-- | Take the input, stop when the predicate succeeds taking the succeeding+-- element as well.+--+-- Example:+--+-- >>> input = Stream.fromList "hello\nthere\n"+-- >>> line = Fold.takeEndBy (== '\n') Fold.toList+-- >>> Stream.fold line input+-- "hello\n"+--+-- >>> Stream.fold Fold.toList $ Stream.foldMany line input+-- ["hello\n","there\n"]+--+{-# INLINE takeEndBy #-}+takeEndBy :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a b+-- takeEndBy predicate = scanMaybe (takingEndBy predicate)+takeEndBy predicate (Fold fstep finitial fextract ffinal) =+    Fold step finitial fextract ffinal++    where++    step s a = do+        res <- fstep s a+        if not (predicate a)+        then return res+        else do+            case res of+                Partial s1 -> Done <$> ffinal s1+                Done b -> return $ Done b++------------------------------------------------------------------------------+-- Binary splitting on a separator+------------------------------------------------------------------------------++data SplitOnSeqState acc a rb rh w ck =+      SplitOnSeqEmpty !acc+    | SplitOnSeqSingle !acc !a+    | SplitOnSeqWord !acc !Int !w+    | SplitOnSeqWordLoop !acc !w+    | SplitOnSeqKR !acc !Int !rb !rh+    | SplitOnSeqKRLoop !acc !ck !rb !rh++-- XXX Need to add tests for takeEndBySeq, we have tests for takeEndBySeq_ .++-- | Continue taking the input until the input sequence matches the supplied+-- sequence, taking the supplied sequence as well. If the pattern is empty this+-- acts as an identity fold.+--+-- >>> s = Stream.fromList "hello there. How are you?"+-- >>> f = Fold.takeEndBySeq (Array.fromList "re") Fold.toList+-- >>> Stream.fold f s+-- "hello there"+--+-- >>> Stream.fold Fold.toList $ Stream.foldMany f s+-- ["hello there",". How are"," you?"]+--+-- /Pre-release/+{-# INLINE takeEndBySeq #-}+takeEndBySeq :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) =>+       Array.Array a+    -> Fold m a b+    -> Fold m a b+takeEndBySeq patArr (Fold fstep finitial fextract ffinal) =+    Fold step initial extract final++    where++    patLen = Array.length patArr++    initial = do+        res <- finitial+        case res of+            Partial acc+                | patLen == 0 ->+                    -- XXX Should we match nothing or everything on empty+                    -- pattern?+                    -- Done <$> ffinal acc+                    return $ Partial $ SplitOnSeqEmpty acc+                | patLen == 1 -> do+                    pat <- liftIO $ Array.unsafeIndexIO 0 patArr+                    return $ Partial $ SplitOnSeqSingle acc pat+                | SIZE_OF(a) * patLen <= sizeOf (Proxy :: Proxy Word) ->+                    return $ Partial $ SplitOnSeqWord acc 0 0+                | otherwise -> do+                    (rb, rhead) <- liftIO $ Ring.new patLen+                    return $ Partial $ SplitOnSeqKR acc 0 rb rhead+            Done b -> return $ Done b++    -- Word pattern related+    maxIndex = patLen - 1++    elemBits = SIZE_OF(a) * 8++    wordMask :: Word+    wordMask = (1 `shiftL` (elemBits * patLen)) - 1++    wordPat :: Word+    wordPat = wordMask .&. Array.foldl' addToWord 0 patArr++    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)++    -- For Rabin-Karp search+    k = 2891336453 :: Word32+    coeff = k ^ patLen++    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)++    deltaCksum cksum old new =+        addCksum cksum new - coeff * fromIntegral (fromEnum old)++    -- XXX shall we use a random starting hash or 1 instead of 0?+    -- XXX Need to keep this cached across fold calls in foldmany+    -- XXX We may need refold to inject the cached state instead of+    -- initializing the state every time.+    -- XXX Allocation of ring buffer should also be done once+    patHash = Array.foldl' addCksum 0 patArr++    step (SplitOnSeqEmpty s) x = do+        res <- fstep s x+        case res of+            Partial s1 -> return $ Partial $ SplitOnSeqEmpty s1+            Done b -> return $ Done b+    step (SplitOnSeqSingle s pat) x = do+        res <- fstep s x+        case res of+            Partial s1+                | pat /= x -> return $ Partial $ SplitOnSeqSingle s1 pat+                | otherwise -> Done <$> ffinal s1+            Done b -> return $ Done b+    step (SplitOnSeqWord s idx wrd) x = do+        res <- fstep s x+        let wrd1 = addToWord wrd x+        case res of+            Partial s1+                | idx == maxIndex -> do+                    if wrd1 .&. wordMask == wordPat+                    then Done <$> ffinal s1+                    else return $ Partial $ SplitOnSeqWordLoop s1 wrd1+                | otherwise ->+                    return $ Partial $ SplitOnSeqWord s1 (idx + 1) wrd1+            Done b -> return $ Done b+    step (SplitOnSeqWordLoop s wrd) x = do+        res <- fstep s x+        let wrd1 = addToWord wrd x+        case res of+            Partial s1+                | wrd1 .&. wordMask == wordPat ->+                    Done <$> ffinal s1+                | otherwise ->+                    return $ Partial $ SplitOnSeqWordLoop s1 wrd1+            Done b -> return $ Done b+    step (SplitOnSeqKR s idx rb rh) x = do+        res <- fstep s x+        case res of+            Partial s1 -> do+                rh1 <- liftIO $ Ring.unsafeInsert rb rh x+                if idx == maxIndex+                then do+                    let fld = Ring.unsafeFoldRing (Ring.ringBound rb)+                    let !ringHash = fld addCksum 0 rb+                    if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr+                    then Done <$> ffinal s1+                    else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1+                else+                    return $ Partial $ SplitOnSeqKR s1 (idx + 1) rb rh1+            Done b -> return $ Done b+    step (SplitOnSeqKRLoop s cksum rb rh) x = do+        res <- fstep s x+        case res of+            Partial s1 -> do+                old <- liftIO $ peek rh+                rh1 <- liftIO $ Ring.unsafeInsert rb rh x+                let ringHash = deltaCksum cksum old x+                if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr+                then Done <$> ffinal s1+                else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1+            Done b -> return $ Done b++    extractFunc fex state =+        let st =+                case state of+                    SplitOnSeqEmpty s -> s+                    SplitOnSeqSingle s _ -> s+                    SplitOnSeqWord s _ _ -> s+                    SplitOnSeqWordLoop s _ -> s+                    SplitOnSeqKR s _ _ _ -> s+                    SplitOnSeqKRLoop s _ _ _ -> s+        in fex st++    extract state = extractFunc fextract state++    final state = extractFunc ffinal state++-- | Like 'takeEndBySeq' but discards the matched sequence.+--+-- /Pre-release/+--+{-# INLINE takeEndBySeq_ #-}+takeEndBySeq_ :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a) =>+       Array.Array a+    -> Fold m a b+    -> Fold m a b+takeEndBySeq_ patArr (Fold fstep finitial fextract ffinal) =+    Fold step initial extract final++    where++    patLen = Array.length patArr++    initial = do+        res <- finitial+        case res of+            Partial acc+                | patLen == 0 ->+                    -- XXX Should we match nothing or everything on empty+                    -- pattern?+                    -- Done <$> ffinal acc+                    return $ Partial $ SplitOnSeqEmpty acc+                | patLen == 1 -> do+                    pat <- liftIO $ Array.unsafeIndexIO 0 patArr+                    return $ Partial $ SplitOnSeqSingle acc pat+                -- XXX Need to add tests for this case+                | SIZE_OF(a) * patLen <= sizeOf (Proxy :: Proxy Word) ->+                    return $ Partial $ SplitOnSeqWord acc 0 0+                | otherwise -> do+                    (rb, rhead) <- liftIO $ Ring.new patLen+                    return $ Partial $ SplitOnSeqKR acc 0 rb rhead+            Done b -> return $ Done b++    -- Word pattern related+    maxIndex = patLen - 1++    elemBits = SIZE_OF(a) * 8++    wordMask :: Word+    wordMask = (1 `shiftL` (elemBits * patLen)) - 1++    elemMask :: Word+    elemMask = (1 `shiftL` elemBits) - 1++    wordPat :: Word+    wordPat = wordMask .&. Array.foldl' addToWord 0 patArr++    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)++    -- For Rabin-Karp search+    k = 2891336453 :: Word32+    coeff = k ^ patLen++    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)++    deltaCksum cksum old new =+        addCksum cksum new - coeff * fromIntegral (fromEnum old)++    -- XXX shall we use a random starting hash or 1 instead of 0?+    -- XXX Need to keep this cached across fold calls in foldMany+    -- XXX We may need refold to inject the cached state instead of+    -- initializing the state every time.+    -- XXX Allocation of ring buffer should also be done once+    patHash = Array.foldl' addCksum 0 patArr++    step (SplitOnSeqEmpty s) x = do+        res <- fstep s x+        case res of+            Partial s1 -> return $ Partial $ SplitOnSeqEmpty s1+            Done b -> return $ Done b+    step (SplitOnSeqSingle s pat) x = do+        if pat /= x+        then do+            res <- fstep s x+            case res of+                Partial s1 -> return $ Partial $ SplitOnSeqSingle s1 pat+                Done b -> return $ Done b+        else Done <$> ffinal s+    step (SplitOnSeqWord s idx wrd) x = do+        let wrd1 = addToWord wrd x+        if idx == maxIndex+        then do+            if wrd1 .&. wordMask == wordPat+            then Done <$> ffinal s+            else return $ Partial $ SplitOnSeqWordLoop s wrd1+        else return $ Partial $ SplitOnSeqWord s (idx + 1) wrd1+    step (SplitOnSeqWordLoop s wrd) x = do+        let wrd1 = addToWord wrd x+            old = (wordMask .&. wrd)+                    `shiftR` (elemBits * (patLen - 1))+        res <- fstep s (toEnum $ fromIntegral old)+        case res of+            Partial s1+                | wrd1 .&. wordMask == wordPat ->+                    Done <$> ffinal s1+                | otherwise ->+                    return $ Partial $ SplitOnSeqWordLoop s1 wrd1+            Done b -> return $ Done b+    step (SplitOnSeqKR s idx rb rh) x = do+        rh1 <- liftIO $ Ring.unsafeInsert rb rh x+        if idx == maxIndex+        then do+            let fld = Ring.unsafeFoldRing (Ring.ringBound rb)+            let !ringHash = fld addCksum 0 rb+            if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr+            then Done <$> ffinal s+            else return $ Partial $ SplitOnSeqKRLoop s ringHash rb rh1+        else return $ Partial $ SplitOnSeqKR s (idx + 1) rb rh1+    step (SplitOnSeqKRLoop s cksum rb rh) x = do+        old <- liftIO $ peek rh+        res <- fstep s old+        case res of+            Partial s1 -> do+                rh1 <- liftIO $ Ring.unsafeInsert rb rh x+                let ringHash = deltaCksum cksum old x+                if ringHash == patHash && Ring.unsafeEqArray rb rh1 patArr+                then Done <$> ffinal s1+                else return $ Partial $ SplitOnSeqKRLoop s1 ringHash rb rh1+            Done b -> return $ Done b++    -- XXX extract should return backtrack count as well. If the fold+    -- terminates early inside extract, we may still have buffered data+    -- remaining which will be lost if we do not communicate that to the+    -- driver.+    extractFunc fex state = do+        let consumeWord s n wrd = do+                if n == 0+                then fex s+                else do+                    let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))+                    r <- fstep s (toEnum $ fromIntegral old)+                    case r of+                        Partial s1 -> consumeWord s1 (n - 1) wrd+                        Done b -> return b++        let consumeRing s n rb rh =+                if n == 0+                then fex s+                else do+                    old <- liftIO $ peek rh+                    let rh1 = Ring.advance rb rh+                    r <- fstep s old+                    case r of+                        Partial s1 -> consumeRing s1 (n - 1) rb rh1+                        Done b -> return b++        case state of+            SplitOnSeqEmpty s -> fex s+            SplitOnSeqSingle s _ -> fex s+            SplitOnSeqWord s idx wrd -> consumeWord s idx wrd+            SplitOnSeqWordLoop s wrd -> consumeWord s patLen wrd+            SplitOnSeqKR s idx rb _ -> consumeRing s idx rb (Ring.startOf rb)+            SplitOnSeqKRLoop s _ rb rh -> consumeRing s patLen rb rh++    extract state = extractFunc fextract state++    final state = extractFunc ffinal state++------------------------------------------------------------------------------+-- Distributing+------------------------------------------------------------------------------+--+-- | Distribute one copy of the stream to each fold and zip the results.+--+-- @+--                 |-------Fold m a b--------|+-- ---stream m a---|                         |---m (b,c)+--                 |-------Fold m a c--------|+-- @+--+--  Definition:+--+-- >>> tee = Fold.teeWith (,)+--+-- Example:+--+-- >>> t = Fold.tee Fold.sum Fold.length+-- >>> Stream.fold t (Stream.enumerateFromTo 1.0 100.0)+-- (5050.0,100)+--+{-# INLINE tee #-}+tee :: Monad m => Fold m a b -> Fold m a c -> Fold m a (b,c)+tee = teeWith (,)++-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large+-- number of consumers? For polymorphic case a vector could be helpful. For+-- Storables we can use arrays. Will need separate APIs for those.+--+-- | Distribute one copy of the stream to each fold and collect the results in+-- a container.+--+-- @+--+--                 |-------Fold m a b--------|+-- ---stream m a---|                         |---m [b]+--                 |-------Fold m a b--------|+--                 |                         |+--                            ...+-- @+--+-- >>> Stream.fold (Fold.distribute [Fold.sum, Fold.length]) (Stream.enumerateFromTo 1 5)+-- [15,5]+--+-- >>> distribute = Prelude.foldr (Fold.teeWith (:)) (Fold.fromPure [])+--+-- This is the consumer side dual of the producer side 'sequence' operation.+--+-- Stops when all the folds stop.+--+{-# INLINE distribute #-}+distribute :: Monad m => [Fold m a b] -> Fold m a [b]+distribute = Prelude.foldr (teeWith (:)) (fromPure [])++------------------------------------------------------------------------------+-- Partitioning+------------------------------------------------------------------------------++{-# INLINE partitionByMUsing #-}+partitionByMUsing :: Monad m =>+       (  (x -> y -> (x, y))+       -> Fold m (Either b c) x+       -> Fold m (Either b c) y+       -> Fold m (Either b c) (x, y)+       )+    -> (a -> m (Either b c))+    -> Fold m b x+    -> Fold m c y+    -> Fold m a (x, y)+partitionByMUsing t f fld1 fld2 =+    let l = lmap (fromLeft undefined) fld1  -- :: Fold m (Either b c) x+        r = lmap (fromRight undefined) fld2 -- :: Fold m (Either b c) y+     in lmapM f (t (,) (filter isLeft l) (filter isRight r))++-- | Partition the input over two folds using an 'Either' partitioning+-- predicate.+--+-- @+--+--                                     |-------Fold b x--------|+-- -----stream m a --> (Either b c)----|                       |----(x,y)+--                                     |-------Fold c y--------|+-- @+--+-- Example, send input to either fold randomly:+--+-- >>> :set -package random+-- >>> import System.Random (randomIO)+-- >>> randomly a = randomIO >>= \x -> return $ if x then Left a else Right a+-- >>> f = Fold.partitionByM randomly Fold.length Fold.length+-- >>> Stream.fold f (Stream.enumerateFromTo 1 100)+-- ...+--+-- Example, send input to the two folds in a proportion of 2:1:+--+-- >>> :{+-- proportionately m n = do+--  ref <- newIORef $ cycle $ concat [replicate m Left, replicate n Right]+--  return $ \a -> do+--      r <- readIORef ref+--      writeIORef ref $ tail r+--      return $ Prelude.head r a+-- :}+--+-- >>> :{+-- main = do+--  g <- proportionately 2 1+--  let f = Fold.partitionByM g Fold.length Fold.length+--  r <- Stream.fold f (Stream.enumerateFromTo (1 :: Int) 100)+--  print r+-- :}+--+-- >>> main+-- (67,33)+--+--+-- This is the consumer side dual of the producer side 'mergeBy' operation.+--+-- When one fold is done, any input meant for it is ignored until the other+-- fold is also done.+--+-- Stops when both the folds stop.+--+-- /See also: 'partitionByFstM' and 'partitionByMinM'./+--+-- /Pre-release/+{-# INLINE partitionByM #-}+partitionByM :: Monad m+    => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)+partitionByM = partitionByMUsing teeWith++-- | Similar to 'partitionByM' but terminates when the first fold terminates.+--+{-# INLINE partitionByFstM #-}+partitionByFstM :: Monad m+    => (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)+partitionByFstM = partitionByMUsing teeWithFst++-- | Similar to 'partitionByM' but terminates when any fold terminates.+--+{-# INLINE partitionByMinM #-}+partitionByMinM :: Monad m =>+    (a -> m (Either b c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)+partitionByMinM = partitionByMUsing teeWithMin++-- Note: we could use (a -> Bool) instead of (a -> Either b c), but the latter+-- makes the signature clearer as to which case belongs to which fold.+-- XXX need to check the performance in both cases.++-- | Same as 'partitionByM' but with a pure partition function.+--+-- Example, count even and odd numbers in a stream:+--+-- >>> :{+--  let f = Fold.partitionBy (\n -> if even n then Left n else Right n)+--                      (fmap (("Even " ++) . show) Fold.length)+--                      (fmap (("Odd "  ++) . show) Fold.length)+--   in Stream.fold f (Stream.enumerateFromTo 1 100)+-- :}+-- ("Even 50","Odd 50")+--+-- /Pre-release/+{-# INLINE partitionBy #-}+partitionBy :: Monad m+    => (a -> Either b c) -> Fold m b x -> Fold m c y -> Fold m a (x, y)+partitionBy f = partitionByM (return . f)++-- | Compose two folds such that the combined fold accepts a stream of 'Either'+-- and routes the 'Left' values to the first fold and 'Right' values to the+-- second fold.+--+-- Definition:+--+-- >>> partition = Fold.partitionBy id+--+{-# INLINE partition #-}+partition :: Monad m+    => Fold m b x -> Fold m c y -> Fold m (Either b c) (x, y)+partition = partitionBy id++{-+-- | Send one item to each fold in a round-robin fashion. This is the consumer+-- side dual of producer side 'mergeN' operation.+--+-- partitionN :: Monad m => [Fold m a b] -> Fold m a [b]+-- partitionN fs = Fold step begin done+-}++------------------------------------------------------------------------------+-- Unzipping+------------------------------------------------------------------------------++{-# INLINE unzipWithMUsing #-}+unzipWithMUsing :: Monad m =>+       (  (x -> y -> (x, y))+       -> Fold m (b, c) x+       -> Fold m (b, c) y+       -> Fold m (b, c) (x, y)+       )+    -> (a -> m (b, c))+    -> Fold m b x+    -> Fold m c y+    -> Fold m a (x, y)+unzipWithMUsing t f fld1 fld2 =+    let f1 = lmap fst fld1  -- :: Fold m (b, c) b+        f2 = lmap snd fld2  -- :: Fold m (b, c) c+     in lmapM f (t (,) f1 f2)++-- | Like 'unzipWith' but with a monadic splitter function.+--+-- Definition:+--+-- >>> unzipWithM k f1 f2 = Fold.lmapM k (Fold.unzip f1 f2)+--+-- /Pre-release/+{-# INLINE unzipWithM #-}+unzipWithM :: Monad m+    => (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)+unzipWithM = unzipWithMUsing teeWith++-- | Similar to 'unzipWithM' but terminates when the first fold terminates.+--+{-# INLINE unzipWithFstM #-}+unzipWithFstM :: Monad m =>+    (a -> m (b, c)) -> Fold m b x -> Fold m c y -> Fold m a (x, y)+unzipWithFstM = unzipWithMUsing teeWithFst++-- | Similar to 'unzipWithM' but terminates when any fold terminates.+--+{-# INLINE unzipWithMinM #-}+unzipWithMinM :: Monad m =>+    (a -> m (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)+unzipWithMinM = unzipWithMUsing teeWithMin++-- | Split elements in the input stream into two parts using a pure splitter+-- function, direct each part to a different fold and zip the results.+--+-- Definitions:+--+-- >>> unzipWith f = Fold.unzipWithM (return . f)+-- >>> unzipWith f fld1 fld2 = Fold.lmap f (Fold.unzip fld1 fld2)+--+-- This fold terminates when both the input folds terminate.+--+-- /Pre-release/+{-# INLINE unzipWith #-}+unzipWith :: Monad m+    => (a -> (b,c)) -> Fold m b x -> Fold m c y -> Fold m a (x,y)+unzipWith f = unzipWithM (return . f)++-- | Send the elements of tuples in a stream of tuples through two different+-- folds.+--+-- @+--+--                           |-------Fold m a x--------|+-- ---------stream of (a,b)--|                         |----m (x,y)+--                           |-------Fold m b y--------|+--+-- @+--+-- Definition:+--+-- >>> unzip = Fold.unzipWith id+--+-- This is the consumer side dual of the producer side 'zip' operation.+--+{-# INLINE unzip #-}+unzip :: Monad m => Fold m a x -> Fold m b y -> Fold m (a,b) (x,y)+unzip = unzipWith id++------------------------------------------------------------------------------+-- Combining streams and folds - Zipping+------------------------------------------------------------------------------++-- XXX These can be implemented using the fold scan, using the stream as a+-- state.+-- XXX Stream Skip state cannot be efficiently handled in folds but can be+-- handled in parsers using the Continue facility. See zipWithM in the Parser+-- module.+--+-- cmpBy, eqBy, isPrefixOf, isSubsequenceOf etc can be implemented using+-- zipStream.++-- | Zip a stream with the input of a fold using the supplied function.+--+-- /Unimplemented/+--+{-# INLINE zipStreamWithM #-}+zipStreamWithM :: -- Monad m =>+    (a -> b -> m c) -> Stream m a -> Fold m c x -> Fold m b x+zipStreamWithM = undefined++-- | Zip a stream with the input of a fold.+--+-- >>> zip = Fold.zipStreamWithM (curry return)+--+-- /Unimplemented/+--+{-# INLINE zipStream #-}+zipStream :: Monad m => Stream m a -> Fold m (a, b) x -> Fold m b x+zipStream = zipStreamWithM (curry return)++-- | Pair each element of a fold input with its index, starting from index 0.+--+{-# INLINE indexingWith #-}+indexingWith :: Monad m => Int -> (Int -> Int) -> Fold m a (Maybe (Int, a))+indexingWith i f = fmap toMaybe $ foldl' step initial++    where++    initial = Nothing'++    step Nothing' a = Just' (i, a)+    step (Just' (n, _)) a = Just' (f n, a)++-- |+-- >>> indexing = Fold.indexingWith 0 (+ 1)+--+{-# INLINE indexing #-}+indexing :: Monad m => Fold m a (Maybe (Int, a))+indexing = indexingWith 0 (+ 1)++-- |+-- >>> indexingRev n = Fold.indexingWith n (subtract 1)+--+{-# INLINE indexingRev #-}+indexingRev :: Monad m => Int -> Fold m a (Maybe (Int, a))+indexingRev n = indexingWith n (subtract 1)++-- | Pair each element of a fold input with its index, starting from index 0.+--+-- >>> indexed = Fold.scanMaybe Fold.indexing+--+{-# INLINE indexed #-}+indexed :: Monad m => Fold m (Int, a) b -> Fold m a b+indexed = scanMaybe indexing++-- | Change the predicate function of a Fold from @a -> b@ to accept an+-- additional state input @(s, a) -> b@. Convenient to filter with an+-- addiitonal index or time input.+--+-- >>> filterWithIndex = Fold.with Fold.indexed Fold.filter+--+-- @+-- filterWithAbsTime = with timestamped filter+-- filterWithRelTime = with timeIndexed filter+-- @+--+-- /Pre-release/+{-# INLINE with #-}+with ::+       (Fold m (s, a) b -> Fold m a b)+    -> (((s, a) -> c) -> Fold m (s, a) b -> Fold m (s, a) b)+    -> (((s, a) -> c) -> Fold m a b -> Fold m a b)+with f comb g = f . comb g . lmap snd++-- XXX Implement as a filter+-- sampleFromthen :: Monad m => Int -> Int -> Fold m a (Maybe a)++-- | @sampleFromthen offset stride@ samples the element at @offset@ index and+-- then every element at strides of @stride@.+--+{-# INLINE sampleFromthen #-}+sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Fold m a b+sampleFromthen offset size =+    with indexed filter (\(i, _) -> (i + offset) `mod` size == 0)++------------------------------------------------------------------------------+-- Nesting+------------------------------------------------------------------------------++-- | @concatSequence f t@ applies folds from stream @t@ sequentially and+-- collects the results using the fold @f@.+--+-- /Unimplemented/+--+{-# INLINE concatSequence #-}+concatSequence ::+    -- IsStream t =>+    Fold m b c -> t (Fold m a b) -> Fold m a c+concatSequence _f _p = undefined++-- | Group the input stream into groups of elements between @low@ and @high@.+-- Collection starts in chunks of @low@ and then keeps doubling until we reach+-- @high@. Each chunk is folded using the provided fold function.+--+-- This could be useful, for example, when we are folding a stream of unknown+-- size to a stream of arrays and we want to minimize the number of+-- allocations.+--+-- NOTE: this would be an application of "many" using a terminating fold.+--+-- /Unimplemented/+--+{-# INLINE chunksBetween #-}+chunksBetween :: -- Monad m =>+       Int -> Int -> Fold m a b -> Fold m b c -> Fold m a c+chunksBetween _low _high _f1 _f2 = undefined++-- | A fold that buffers its input to a pure stream.+--+-- /Warning!/ working on large streams accumulated as buffers in memory could+-- be very inefficient, consider using "Streamly.Data.Array" instead.+--+-- >>> toStream = fmap Stream.fromList Fold.toList+--+-- /Pre-release/+{-# INLINE toStream #-}+toStream :: (Monad m, Monad n) => Fold m a (Stream n a)+toStream = fmap StreamD.fromList toList++-- This is more efficient than 'toStream'. toStream is exactly the same as+-- reversing the stream after toStreamRev.+--+-- | Buffers the input stream to a pure stream in the reverse order of the+-- input.+--+-- >>> toStreamRev = fmap Stream.fromList Fold.toListRev+--+-- /Warning!/ working on large streams accumulated as buffers in memory could+-- be very inefficient, consider using "Streamly.Data.Array" instead.+--+-- /Pre-release/++--  xn : ... : x2 : x1 : []+{-# INLINE toStreamRev #-}+toStreamRev :: (Monad m, Monad n) => Fold m a (Stream n a)+toStreamRev = fmap StreamD.fromList toListRev++-- XXX This does not fuse. It contains a recursive step function. We will need+-- a Skip input constructor in the fold type to make it fuse.+--+-- | Unfold and flatten the input stream of a fold.+--+-- @+-- Stream.fold (unfoldMany u f) = Stream.fold f . Stream.unfoldMany u+-- @+--+-- /Pre-release/+{-# INLINE unfoldMany #-}+unfoldMany :: Monad m => Unfold m a b -> Fold m b c -> Fold m a c+unfoldMany (Unfold ustep inject) (Fold fstep initial extract final) =+    Fold consume initial extract final++    where++    {-# INLINE produce #-}+    produce fs us = do+        ures <- ustep us+        case ures of+            StreamD.Yield b us1 -> do+                fres <- fstep fs b+                case fres of+                    Partial fs1 -> produce fs1 us1+                    -- XXX What to do with the remaining stream?+                    Done c -> return $ Done c+            StreamD.Skip us1 -> produce fs us1+            StreamD.Stop -> return $ Partial fs++    {-# INLINE_LATE consume #-}+    consume s a = inject a >>= produce s++-- | Get the bottom most @n@ elements using the supplied comparison function.+--+{-# INLINE bottomBy #-}+bottomBy :: (MonadIO m, Unbox a) =>+       (a -> a -> Ordering)+    -> Int+    -> Fold m a (MutArray a)+bottomBy cmp n = Fold step initial extract extract++    where++    initial = do+        arr <- MA.pinnedNew n+        if n <= 0+        then return $ Done arr+        else return $ Partial (arr, 0)++    step (arr, i) x =+        if i < n+        then do+            arr' <- MA.snoc arr x+            MA.bubble cmp arr'+            return $ Partial (arr', i + 1)+        else do+            x1 <- MA.getIndexUnsafe (i - 1) arr+            case x `cmp` x1 of+                LT -> do+                    MA.putIndexUnsafe (i - 1) arr x+                    MA.bubble cmp arr+                    return $ Partial (arr, i)+                _ -> return $ Partial (arr, i)++    extract = return . fst++-- | Get the top @n@ elements using the supplied comparison function.+--+-- To get bottom n elements instead:+--+-- >>> bottomBy cmp = Fold.topBy (flip cmp)+--+-- Example:+--+-- >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]+-- >>> Stream.fold (Fold.topBy compare 3) stream >>= MutArray.toList+-- [17,11,9]+--+-- /Pre-release/+--+{-# INLINE topBy #-}+topBy :: (MonadIO m, Unbox a) =>+       (a -> a -> Ordering)+    -> Int+    -> Fold m a (MutArray a)+topBy cmp = bottomBy (flip cmp)++-- | Fold the input stream to top n elements.+--+-- Definition:+--+-- >>> top = Fold.topBy compare+--+-- >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]+-- >>> Stream.fold (Fold.top 3) stream >>= MutArray.toList+-- [17,11,9]+--+-- /Pre-release/+{-# INLINE top #-}+top :: (MonadIO m, Unbox a, Ord a) => Int -> Fold m a (MutArray a)+top = bottomBy $ flip compare++-- | Fold the input stream to bottom n elements.+--+-- Definition:+--+-- >>> bottom = Fold.bottomBy compare+--+-- >>> stream = Stream.fromList [2::Int,7,9,3,1,5,6,11,17]+-- >>> Stream.fold (Fold.bottom 3) stream >>= MutArray.toList+-- [1,2,3]+--+-- /Pre-release/+{-# INLINE bottom #-}+bottom :: (MonadIO m, Unbox a, Ord a) => Int -> Fold m a (MutArray a)+bottom = bottomBy compare++------------------------------------------------------------------------------+-- Interspersed parsing+------------------------------------------------------------------------------++data IntersperseQState fs ps =+      IntersperseQUnquoted !fs !ps+    | IntersperseQQuoted !fs !ps+    | IntersperseQQuotedEsc !fs !ps++-- Useful for parsing CSV with quoting and escaping+{-# INLINE intersperseWithQuotes #-}+intersperseWithQuotes :: (Monad m, Eq a) =>+    a -> a -> a -> Fold m a b -> Fold m b c -> Fold m a c+intersperseWithQuotes+    quote+    esc+    separator+    (Fold stepL initialL _ finalL)+    (Fold stepR initialR extractR finalR) = Fold step initial extract final++    where++    errMsg p status =+        error $ "intersperseWithQuotes: " ++ p ++ " parsing fold cannot "+                ++ status ++ " without input"++    {-# INLINE initL #-}+    initL mkState = do+        resL <- initialL+        case resL of+            Partial sL ->+                return $ Partial $ mkState sL+            Done _ ->+                errMsg "content" "succeed"++    initial = do+        res <- initialR+        case res of+            Partial sR -> initL (IntersperseQUnquoted sR)+            Done b -> return $ Done b++    {-# INLINE collect #-}+    collect nextS sR b = do+        res <- stepR sR b+        case res of+            Partial s ->+                initL (nextS s)+            Done c -> return (Done c)++    {-# INLINE process #-}+    process a sL sR nextState = do+        r <- stepL sL a+        case r of+            Partial s -> return $ Partial (nextState sR s)+            Done b -> collect nextState sR b++    {-# INLINE processQuoted #-}+    processQuoted a sL sR nextState = do+        r <- stepL sL a+        case r of+            Partial s -> return $ Partial (nextState sR s)+            Done _ -> do+                _ <- finalR sR+                error "Collecting fold finished inside quote"++    step (IntersperseQUnquoted sR sL) a+        | a == separator = do+            b <- finalL sL+            collect IntersperseQUnquoted sR b+        | a == quote = processQuoted a sL sR IntersperseQQuoted+        | otherwise = process a sL sR IntersperseQUnquoted++    step (IntersperseQQuoted sR sL) a+        | a == esc = processQuoted a sL sR IntersperseQQuotedEsc+        | a == quote = process a sL sR IntersperseQUnquoted+        | otherwise = processQuoted a sL sR IntersperseQQuoted++    step (IntersperseQQuotedEsc sR sL) a =+        processQuoted a sL sR IntersperseQQuoted++    extract (IntersperseQUnquoted sR _) = extractR sR+    extract (IntersperseQQuoted _ _) =+        error "intersperseWithQuotes: finished inside quote"+    extract (IntersperseQQuotedEsc _ _) =+        error "intersperseWithQuotes: finished inside quote, at escape char"++    final (IntersperseQUnquoted sR sL) = finalL sL *> finalR sR+    final (IntersperseQQuoted sR sL) = do+        _ <- finalR sR+        _ <- finalL sL+        error "intersperseWithQuotes: finished inside quote"+    final (IntersperseQQuotedEsc sR sL) = do+        _ <- finalR sR+        _ <- finalL sL+        error "intersperseWithQuotes: finished inside quote, at escape char"
src/Streamly/Internal/Data/Fold/Container.hs view
@@ -9,9 +9,6 @@  module Streamly.Internal.Data.Fold.Container     (-    -- * Imports-    -- $setup-     -- * Set operations       toSet     , toIntSet@@ -27,14 +24,27 @@     -- | Direct values in the input stream to different folds using an n-ary     -- fold selector. 'demux' is a generalization of 'classify' (and     -- 'partition') where each key of the classifier can use a different fold.-    , demuxKvToContainer-    , demuxKvToMap+    --+    -- You need to see only 'demux' if you are looking to find the capabilities+    -- of these combinators, all others are variants of that. +    -- *** Output is a container+    -- | The fold state snapshot returns the key-value container of in-progress+    -- folds.     , demuxToContainer     , demuxToContainerIO     , demuxToMap     , demuxToMapIO +    -- *** Input is explicit key-value tuple+    -- | Like above but inputs are in explicit key-value pair form.+    , demuxKvToContainer+    , demuxKvToMap++    -- *** Scan of finished fold results+    -- | Like above, but the resulting fold state snapshot contains the key+    -- value container as well as the finished key result if a fold in the+    -- container finished.     , demuxGeneric     , demux     , demuxGenericIO@@ -89,8 +99,9 @@ import qualified Data.Set as Set import qualified Streamly.Internal.Data.IsMap as IsMap -import Prelude hiding (length)-import Streamly.Internal.Data.Fold+import Prelude hiding (Foldable(..))+import Streamly.Internal.Data.Fold.Type+import Streamly.Internal.Data.Fold.Combinators  -- $setup -- >>> :m@@ -251,19 +262,23 @@ -- because key is to be determined on each input whereas fold is to be -- determined only once for a key. +-- | This is the most general of all demux, classify operations.+--+-- See 'demux' for documentation. {-# INLINE demuxGeneric #-} demuxGeneric :: (Monad m, IsMap f, Traversable f) =>        (a -> Key f)     -> (a -> m (Fold m a b))     -> Fold m a (m (f b), Maybe (Key f, b))-demuxGeneric getKey getFold = fmap extract $ foldlM' step initial+demuxGeneric getKey getFold =+    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) extract final      where      initial = return $ Tuple' IsMap.mapEmpty Nothing      {-# INLINE runFold #-}-    runFold kv (Fold step1 initial1 extract1) (k, a) = do+    runFold kv (Fold step1 initial1 extract1 final1) (k, a) = do          res <- initial1          case res of             Partial s -> do@@ -271,7 +286,7 @@                 return                     $ case res1 of                         Partial _ ->-                            let fld = Fold step1 (return res1) extract1+                            let fld = Fold step1 (return res1) extract1 final1                              in Tuple' (IsMap.mapInsert k fld kv) Nothing                         Done b -> Tuple' (IsMap.mapDelete k kv) (Just (k, b))             Done b -> return $ Tuple' kv (Just (k, b))@@ -284,25 +299,50 @@                 runFold kv fld (k, a)             Just f -> runFold kv f (k, a) -    extract (Tuple' kv x) = (Prelude.mapM f kv, x)+    extract (Tuple' kv x) = return (Prelude.mapM f kv, x)          where -        f (Fold _ i e) = do+        f (Fold _ i e _) = do             r <- i             case r of                 Partial s -> e s-                Done b -> return b+                _ -> error "demuxGeneric: unreachable code" --- | In a key value stream, fold values corresponding to each key with a key--- specific fold. The fold returns the fold result as the second component of--- the output tuple whenever a fold terminates. The first component of the--- tuple is a Map of in-progress folds. If a fold terminates, another--- instance of the fold is started upon receiving an input with that key.+    final (Tuple' kv x) = return (Prelude.mapM f kv, x)++        where++        f (Fold _ i _ fin) = do+            r <- i+            case r of+                Partial s -> fin s+                _ -> error "demuxGeneric: unreachable code"++-- | @demux getKey getFold@: In a key value stream, fold values corresponding+-- to each key using a key specific fold. @getFold@ is invoked to generate a+-- key specific fold when a key is encountered for the first time in the+-- stream. --+-- The first component of the output tuple is a key-value Map of in-progress+-- folds. The fold returns the fold result as the second component of the+-- output tuple whenever a fold terminates.+--+-- If a fold terminates, another instance of the fold is started upon receiving+-- an input with that key, @getFold@ is invoked again whenever the key is+-- encountered again.+-- -- This can be used to scan a stream and collect the results from the scan -- output. --+-- Since the fold generator function is monadic we can add folds dynamically.+-- For example, we can maintain a Map of keys to folds in an IORef and lookup+-- the fold from that corresponding to a key. This Map can be changed+-- dynamically, folds for new keys can be added or folds for old keys can be+-- deleted or modified.+--+-- Compare with 'classify', the fold in 'classify' is a static fold.+-- -- /Pre-release/ -- {-# INLINE demux #-}@@ -312,40 +352,43 @@     -> Fold m a (m (Map k b), Maybe (k, b)) demux = demuxGeneric +-- | This is specialized version of 'demuxGeneric' that uses mutable IO cells+-- as fold accumulators for better performance. {-# INLINE demuxGenericIO #-} demuxGenericIO :: (MonadIO m, IsMap f, Traversable f) =>        (a -> Key f)     -> (a -> m (Fold m a b))     -> Fold m a (m (f b), Maybe (Key f, b))-demuxGenericIO getKey getFold = fmap extract $ foldlM' step initial+demuxGenericIO getKey getFold =+    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) extract final      where      initial = return $ Tuple' IsMap.mapEmpty Nothing      {-# INLINE initFold #-}-    initFold kv (Fold step1 initial1 extract1) (k, a) = do+    initFold kv (Fold step1 initial1 extract1 final1) (k, a) = do          res <- initial1          case res of             Partial s -> do                 res1 <- step1 s a                 case res1 of                     Partial _ -> do-                        let fld = Fold step1 (return res1) extract1+                        let fld = Fold step1 (return res1) extract1 final1                         ref <- liftIO $ newIORef fld                         return $ Tuple' (IsMap.mapInsert k ref kv) Nothing                     Done b -> return $ Tuple' kv (Just (k, b))             Done b -> return $ Tuple' kv (Just (k, b))      {-# INLINE runFold #-}-    runFold kv ref (Fold step1 initial1 extract1) (k, a) = do+    runFold kv ref (Fold step1 initial1 extract1 final1) (k, a) = do          res <- initial1          case res of             Partial s -> do                 res1 <- step1 s a                 case res1 of                         Partial _ -> do-                            let fld = Fold step1 (return res1) extract1+                            let fld = Fold step1 (return res1) extract1 final1                             liftIO $ writeIORef ref fld                             return $ Tuple' kv Nothing                         Done b ->@@ -363,20 +406,34 @@                 f <- liftIO $ readIORef ref                 runFold kv ref f (k, a) -    extract (Tuple' kv x) = (Prelude.mapM f kv, x)+    extract (Tuple' kv x) = return (Prelude.mapM f kv, x)          where          f ref = do-            (Fold _ i e) <- liftIO $ readIORef ref+            Fold _ i e _ <- liftIO $ readIORef ref             r <- i             case r of                 Partial s -> e s-                Done b -> return b+                _ -> error "demuxGenericIO: unreachable code" +    final (Tuple' kv x) = return (Prelude.mapM f kv, x)++        where++        f ref = do+            Fold _ i _ fin <- liftIO $ readIORef ref+            r <- i+            case r of+                Partial s -> fin s+                _ -> error "demuxGenericIO: unreachable code"+ -- | This is specialized version of 'demux' that uses mutable IO cells as -- fold accumulators for better performance. --+-- Keep in mind that the values in the returned Map may be changed by the+-- ongoing fold if you are using those concurrently in another thread.+-- {-# INLINE demuxIO #-} demuxIO :: (MonadIO m, Ord k) =>        (a -> k)@@ -384,6 +441,13 @@     -> Fold m a (m (Map k b), Maybe (k, b)) demuxIO = demuxGenericIO +-- | Fold a key value stream to a key-value Map. If the same key appears+-- multiple times, only the last value is retained.+{-# INLINE kvToMapOverwriteGeneric #-}+kvToMapOverwriteGeneric :: (Monad m, IsMap f) => Fold m (Key f, a) (f a)+kvToMapOverwriteGeneric =+    foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty+ {-# INLINE demuxToContainer #-} demuxToContainer :: (Monad m, IsMap f, Traversable f) =>     (a -> Key f) -> (a -> m (Fold m a b)) -> Fold m a (f b)@@ -471,8 +535,8 @@     -- for that use case. We return an action because we want it to be lazy so     -- that the downstream consumers can choose to process or discard it.     (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))-classifyGeneric f (Fold step1 initial1 extract1) =-    fmap extract $ foldlM' step initial+classifyGeneric f (Fold step1 initial1 extract1 final1) =+    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) extract final      where @@ -509,8 +573,17 @@                             let kv1 = IsMap.mapDelete k kv                              in Tuple3' kv1 (Set.insert k set) (Just (k, b)) -    extract (Tuple3' kv _ x) = (Prelude.mapM extract1 kv, x)+    extract (Tuple3' kv _ x) = return (Prelude.mapM extract1 kv, x) +    final (Tuple3' kv set x) = return (IsMap.mapTraverseWithKey f1 kv, x)++        where++        f1 k s = do+            if Set.member k set+            then extract1 s+            else final1 s+ -- | Folds the values for each key using the supplied fold. When scanning, as -- soon as the fold is complete, its result is available in the second -- component of the tuple.  The first component of the tuple is a snapshot of@@ -534,8 +607,8 @@ {-# INLINE classifyGenericIO #-} classifyGenericIO :: (MonadIO m, IsMap f, Traversable f, Ord (Key f)) =>     (a -> Key f) -> Fold m a b -> Fold m a (m (f b), Maybe (Key f, b))-classifyGenericIO f (Fold step1 initial1 extract1) =-    fmap extract $ foldlM' step initial+classifyGenericIO f (Fold step1 initial1 extract1 final1) =+    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) extract final      where @@ -574,9 +647,22 @@                          in return                                 $ Tuple3' kv1 (Set.insert k set) (Just (k, b)) -    extract (Tuple3' kv _ x) =-        (Prelude.mapM (\ref -> liftIO (readIORef ref) >>= extract1) kv, x)+    extract (Tuple3' kv _ x) = return (Prelude.mapM g kv, x) +        where++        g ref = liftIO (readIORef ref) >>= extract1++    final (Tuple3' kv set x) = return (IsMap.mapTraverseWithKey g kv, x)++        where++        g k ref = do+            s <- liftIO $ readIORef ref+            if Set.member k set+            then extract1 s+            else final1 s+ -- | Same as classify except that it uses mutable IORef cells in the -- Map providing better performance. Be aware that if this is used as a scan, -- the values in the intermediate Maps would be mutable.@@ -589,13 +675,6 @@ classifyIO :: (MonadIO m, Ord k) =>     (a -> k) -> Fold m a b -> Fold m a (m (Map k b), Maybe (k, b)) classifyIO = classifyGenericIO---- | Fold a key value stream to a key-value Map. If the same key appears--- multiple times, only the last value is retained.-{-# INLINE kvToMapOverwriteGeneric #-}-kvToMapOverwriteGeneric :: (Monad m, IsMap f) => Fold m (Key f, a) (f a)-kvToMapOverwriteGeneric =-    foldl' (\kv (k, v) -> IsMap.mapInsert k v kv) IsMap.mapEmpty  {-# INLINE toContainer #-} toContainer :: (Monad m, IsMap f, Traversable f, Ord (Key f)) =>
src/Streamly/Internal/Data/Fold/Step.hs view
@@ -8,7 +8,7 @@ -- module Streamly.Internal.Data.Fold.Step     (-    -- * Types+    -- * Step Type       Step (..)      , mapMStep@@ -40,7 +40,7 @@     = Partial !s     | Done !b --- | 'first' maps over 'Partial' and 'second' maps over 'Done'.+-- | 'first' maps over the fold state and 'second' maps over the fold result. -- instance Bifunctor Step where     {-# INLINE bimap #-}
src/Streamly/Internal/Data/Fold/Tee.hs view
@@ -16,7 +16,9 @@     ) where +#if !MIN_VERSION_base(4,18,0) import Control.Applicative (liftA2)+#endif import Streamly.Internal.Data.Fold.Type (Fold)  import qualified Streamly.Internal.Data.Fold.Type as Fold@@ -77,7 +79,7 @@ -- | '<>' distributes the input to both the argument 'Tee's and combines their -- outputs using the 'Monoid' instance of the output type. ---instance (Semigroup b, Monoid b, Monad m) => Monoid (Tee m a b) where+instance (Monoid b, Monad m) => Monoid (Tee m a b) where     {-# INLINE mempty #-}     mempty = pure mempty 
src/Streamly/Internal/Data/Fold/Type.hs view
@@ -126,6 +126,25 @@ -- or to not be able to use `take 0` if we have it. Also, applicative and -- monadic composition of folds would not be possible. --+-- == Cleanup Action+--+-- Fold may use other folds in the downstream pipeline. When a fold is done and+-- it wants to terminate it needs to wait for the downstream folds before it+-- returns. For example, if the downstream fold is an async fold we need to+-- wait for the async fold to finish and return the final result.+--+-- To be able to support this use case we need a cleanup action in the fold.+-- The fold gets finalized once the cleanup is called and we can use extract to+-- get the final state/result of the fold.+--+-- Similar to folds we may have a cleanup action in streams as well. Currently,+-- we rely on GC to cleanup the streams, if we use a cleanup action then we can+-- perform cleanup quickly. Also, similar to folds we can also have an+-- "initial" action in streams as well to generate the initial state. It could+-- decouple the initialization of the stream from the first element being+-- pulled. For example, you may want to start a timer at initialization rather+-- than at the first element pull of the stream.+-- -- == Terminating Folds with backtracking -- -- Consider the example of @takeWhile@ operation, it needs to inspect an@@ -325,11 +344,9 @@ -- module Streamly.Internal.Data.Fold.Type     (-    -- * Imports-    -- $setup+      module Streamly.Internal.Data.Fold.Step -    -- * Types-      Step (..)+    -- * Fold Type     , Fold (..)      -- * Constructors@@ -425,21 +442,25 @@  #include "inline.hs" +#if !MIN_VERSION_base(4,18,0) import Control.Applicative (liftA2)-import Control.Monad ((>=>))+#endif+import Control.Monad ((>=>), void) import Data.Bifunctor (Bifunctor(..)) import Data.Either (fromLeft, fromRight, isLeft, isRight) import Data.Functor.Identity (Identity(..)) import Fusion.Plugin.Types (Fuse(..))-import Streamly.Internal.Data.Fold.Step (Step(..), mapMStep, chainStepM) import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe) import Streamly.Internal.Data.Tuple.Strict (Tuple'(..)) import Streamly.Internal.Data.Refold.Type (Refold(..)) -import qualified Streamly.Internal.Data.Stream.StreamK.Type as K+import qualified Streamly.Internal.Data.StreamK.Type as K -import Prelude hiding (concatMap, filter, foldr, map, take)+import Prelude hiding (Foldable(..), concatMap, filter, map, take) +-- Entire module is exported, do not import selectively+import Streamly.Internal.Data.Fold.Step+ #include "DocTestDataFold.hs"  ------------------------------------------------------------------------------@@ -450,23 +471,64 @@ -- The type @b@ is the accumulator of the writer. That's the reason the -- default folds in various modules are called "write". --- | The type @Fold m a b@ having constructor @Fold step initial extract@--- represents a fold over an input stream of values of type @a@ to a final--- value of type @b@ in 'Monad' @m@.+-- An alternative to using an "extract" function is to use "Partial s b" style+-- partial value so that we always emit the output value and there is no need+-- to extract. Then extract can be used for cleanup purposes. But in this case+-- in some cases we may need a "Continue" constructor where an output value is+-- not available, this was implicit earlier. Also, "b" should be lazy here so+-- that we do not always compute it even if we do not need it. ----- The fold uses an intermediate state @s@ as accumulator, the type @s@ is--- internal to the specific fold definition. The initial value of the fold--- state @s@ is returned by @initial@. The @step@ function consumes an input--- and either returns the final result @b@ if the fold is done or the next--- intermediate state (see 'Step'). At any point the fold driver can extract--- the result from the intermediate state using the @extract@ function.+-- Partial s b  --> extract :: s -> b+-- Continue     --> extract :: s -> Maybe b --+-- But keeping 'b' lazy does not let the fold optimize well. It leads to+-- significant regressions in the key-value folds.+--+-- The "final" function complicates combinators that take other folds as+-- argument because we need to call their finalizers at right places. An+-- alternative to reduce this complexity where it is not required is to use a+-- separate type for bracketed folds but then we need to manage the complexity+-- of two different fold types.++-- The "final" function could be (s -> m (Step s b)), like in parsers so that+-- it can be called in a loop to drain the fold.++-- | The type @Fold m a b@ represents a consumer of an input stream of values+-- of type @a@ and returning a final value of type @b@ in 'Monad' @m@. The+-- constructor of a fold is @Fold step initial extract final@.+--+-- The fold uses an internal state of type @s@. The initial value of the state+-- @s@ is created by @initial@. This function is called once and only once+-- before the fold starts consuming input. Any resource allocation can be done+-- in this function.+--+-- The @step@ function is called on each input, it consumes an input and+-- returns the next intermediate state (see 'Step') or the final result @b@ if+-- the fold terminates.+--+-- If the fold is used as a scan, the @extract@ function is used by the scan+-- driver to map the current state @s@ of the fold to the fold result. Thus+-- @extract@ can be called multiple times. In some folds, where scanning does+-- not make sense, this function is left unimplemented; such folds cannot be+-- used as scans.+--+-- Before a fold terminates, @final@ is called once and only once (unless the+-- fold terminated in @initial@ itself). Any resources allocated by @initial@+-- can be released in @final@. In folds that do not require any cleanup+-- @extract@ and @final@ are typically the same.+--+-- When implementing fold combinators, care should be taken to cleanup any+-- state of the argument folds held by the fold by calling the respective+-- @final@ at all exit points of the fold. Also, @final@ should not be called+-- more than once. Note that if a fold terminates by 'Done' constructor, there+-- is no state to cleanup.+-- -- NOTE: The constructor is not yet released, smart constructors are provided -- to create folds. -- data Fold m a b =-  -- | @Fold @ @ step @ @ initial @ @ extract@-  forall s. Fold (s -> a -> m (Step s b)) (m (Step s b)) (s -> m b)+  -- | @Fold@ @step@ @initial@ @extract@ @final@+  forall s. Fold (s -> a -> m (Step s b)) (m (Step s b)) (s -> m b) (s -> m b)  ------------------------------------------------------------------------------ -- Mapping on the output@@ -476,7 +538,8 @@ -- {-# INLINE rmapM #-} rmapM :: Monad m => (b -> m c) -> Fold m a b -> Fold m a c-rmapM f (Fold step initial extract) = Fold step1 initial1 (extract >=> f)+rmapM f (Fold step initial extract final) =+    Fold step1 initial1 (extract >=> f) (final >=> f)      where @@ -507,6 +570,7 @@         (\s a -> return $ Partial $ step s a)         (return (Partial initial))         return+        return  -- | Make a fold from a left fold style monadic step function and initial value -- of the accumulator.@@ -521,7 +585,7 @@ {-# INLINE foldlM' #-} foldlM' :: Monad m => (b -> a -> m b) -> m b -> Fold m a b foldlM' step initial =-    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) return+    Fold (\s a -> Partial <$> step s a) (Partial <$> initial) return return  -- | Make a strict left fold, for non-empty streams, using first element as the -- starting value. Returns Nothing if the stream is empty.@@ -619,7 +683,11 @@ {-# INLINE foldt' #-} foldt' :: Monad m => (s -> a -> Step s b) -> Step s b -> (s -> b) -> Fold m a b foldt' step initial extract =-    Fold (\s a -> return $ step s a) (return initial) (return . extract)+    Fold+        (\s a -> return $ step s a)+        (return initial)+        (return . extract)+        (return . extract)  -- | Make a terminating fold with an effectful step function and initial state, -- and a state extraction function.@@ -632,7 +700,7 @@ -- {-# INLINE foldtM' #-} foldtM' :: (s -> a -> m (Step s b)) -> m (Step s b) -> (s -> m b) -> Fold m a b-foldtM' = Fold+foldtM' step initial extract = Fold step initial extract extract  ------------------------------------------------------------------------------ -- Refold@@ -646,7 +714,7 @@ -- /Internal/ fromRefold :: Refold m c a b -> c -> Fold m a b fromRefold (Refold step inject extract) c =-    Fold step (inject c) extract+    Fold step (inject c) extract extract  ------------------------------------------------------------------------------ -- Basic Folds@@ -706,7 +774,8 @@ -- | Maps a function on the output of the fold (the type @b@). instance Functor m => Functor (Fold m a) where     {-# INLINE fmap #-}-    fmap f (Fold step1 initial1 extract) = Fold step initial (fmap2 f extract)+    fmap f (Fold step1 initial1 extract final) =+        Fold step initial (fmap2 f extract) (fmap2 f final)          where @@ -741,7 +810,7 @@ -- {-# INLINE fromPure #-} fromPure :: Applicative m => b -> Fold m a b-fromPure b = Fold undefined (pure $ Done b) pure+fromPure b = Fold undefined (pure $ Done b) pure pure  -- | Make a fold that yields the result of the supplied effectful action -- without consuming any further input.@@ -750,7 +819,7 @@ -- {-# INLINE fromEffect #-} fromEffect :: Applicative m => m b -> Fold m a b-fromEffect b = Fold undefined (Done <$> b) pure+fromEffect b = Fold undefined (Done <$> b) pure pure  {-# ANN type SeqFoldState Fuse #-} data SeqFoldState sl f sr = SeqFoldL !sl | SeqFoldR !f !sr@@ -775,16 +844,20 @@ -- complexity, because each composition adds a new branch that each subsequent -- fold's input element has to traverse, therefore, it cannot scale to a large -- number of compositions. After around 100 compositions the performance starts--- dipping rapidly compared to a CPS style implementation. When you need--- scaling use parser monad instead.+-- dipping rapidly compared to a CPS style implementation. --+-- For larger number of compositions you can convert the fold to a parser and+-- use ParserK.+-- -- /Time: O(n^2) where n is the number of compositions./ -- {-# INLINE splitWith #-} splitWith :: Monad m =>     (a -> b -> c) -> Fold m x a -> Fold m x b -> Fold m x c-splitWith func (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+splitWith func+    (Fold stepL initialL _ finalL)+    (Fold stepR initialR _ finalR) =+    Fold step initial extract final      where @@ -801,13 +874,18 @@     step (SeqFoldL st) a = runL (stepL st a)     step (SeqFoldR f st) a = runR (stepR st a) f -    extract (SeqFoldR f sR) = fmap f (extractR sR)-    extract (SeqFoldL sL) = do-        rL <- extractL sL+    -- XXX splitWith should not be used for scanning+    -- It would rarely make sense and resource tracking and cleanup would be+    -- expensive. especially when multiple splitWith are chained.+    extract _ = error "splitWith: cannot be used for scanning"++    final (SeqFoldR f sR) = fmap f (finalR sR)+    final (SeqFoldL sL) = do+        rL <- finalL sL         res <- initialR         fmap (func rL)             $ case res of-                Partial sR -> extractR sR+                Partial sR -> finalR sR                 Done rR -> return rR  {-# DEPRECATED serialWith "Please use \"splitWith\" instead" #-}@@ -827,8 +905,8 @@ -- {-# INLINE split_ #-} split_ :: Monad m => Fold m x a -> Fold m x b -> Fold m x b-split_ (Fold stepL initialL _) (Fold stepR initialR extractR) =-    Fold step initial extract+split_ (Fold stepL initialL _ finalL) (Fold stepR initialR _ finalR) =+    Fold step initial extract final      where @@ -851,11 +929,16 @@         resR <- stepR st a         return $ first SeqFoldR_ resR -    extract (SeqFoldR_ sR) = extractR sR-    extract (SeqFoldL_ _) = do+    -- XXX split_ should not be used for scanning+    -- See splitWith for more details.+    extract _ = error "split_: cannot be used for scanning"++    final (SeqFoldR_ sR) = finalR sR+    final (SeqFoldL_ sL) = do+        _ <- finalL sL         res <- initialR         case res of-            Partial sR -> extractR sR+            Partial sR -> finalR sR             Done rR -> return rR  -- | 'Applicative' form of 'splitWith'. Split the input serially over two@@ -903,8 +986,10 @@ -- {-# INLINE teeWith #-} teeWith :: Monad m => (a -> b -> c) -> Fold m x a -> Fold m x b -> Fold m x c-teeWith f (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+teeWith f+    (Fold stepL initialL extractL finalL)+    (Fold stepR initialR extractR finalR) =+    Fold step initial extract final      where @@ -931,6 +1016,10 @@     extract (TeeLeft bR sL) = (`f` bR) <$> extractL sL     extract (TeeRight bL sR) = f bL <$> extractR sR +    final (TeeBoth sL sR) = f <$> finalL sL <*> finalR sR+    final (TeeLeft bR sL) = (`f` bR) <$> finalL sL+    final (TeeRight bL sR) = f bL <$> finalR sR+ {-# ANN type TeeFstState Fuse #-} data TeeFstState sL sR b     = TeeFstBoth !sL !sR@@ -943,8 +1032,10 @@ {-# INLINE teeWithFst #-} teeWithFst :: Monad m =>     (b -> c -> d) -> Fold m a b -> Fold m a c -> Fold m a d-teeWithFst f (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+teeWithFst f+    (Fold stepL initialL extractL finalL)+    (Fold stepR initialR extractR finalR) =+    Fold step initial extract final      where @@ -963,7 +1054,7 @@             Done bl -> do                 Done . f bl <$>                     case resR of-                        Partial sr -> extractR sr+                        Partial sr -> finalR sr                         Done br -> return br      initial = runBoth initialL initialR@@ -974,6 +1065,9 @@     extract (TeeFstBoth sL sR) = f <$> extractL sL <*> extractR sR     extract (TeeFstLeft bR sL) = (`f` bR) <$> extractL sL +    final (TeeFstBoth sL sR) = f <$> finalL sL <*> finalR sR+    final (TeeFstLeft bR sL) = (`f` bR) <$> finalL sL+ -- | Like 'teeWith' but terminates as soon as any one of the two folds -- terminates. --@@ -982,8 +1076,10 @@ {-# INLINE teeWithMin #-} teeWithMin :: Monad m =>     (b -> c -> d) -> Fold m a b -> Fold m a c -> Fold m a d-teeWithMin f (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+teeWithMin f+    (Fold stepL initialL extractL finalL)+    (Fold stepR initialR extractR finalR) =+    Fold step initial extract final      where @@ -995,12 +1091,12 @@             Partial sl -> do                 case resR of                     Partial sr -> return $ Partial $ Tuple' sl sr-                    Done br -> Done . (`f` br) <$> extractL sl+                    Done br -> Done . (`f` br) <$> finalL sl              Done bl -> do                 Done . f bl <$>                     case resR of-                        Partial sr -> extractR sr+                        Partial sr -> finalR sr                         Done br -> return br      initial = runBoth initialL initialR@@ -1009,6 +1105,8 @@      extract (Tuple' sL sR) = f <$> extractL sL <*> extractR sR +    final (Tuple' sL sR) = f <$> finalL sL <*> finalR sR+ -- | Shortest alternative. Apply both folds in parallel but choose the result -- from the one which consumed least input i.e. take the shortest succeeding -- fold.@@ -1020,8 +1118,8 @@ -- {-# INLINE shortest #-} shortest :: Monad m => Fold m x a -> Fold m x b -> Fold m x (Either a b)-shortest (Fold stepL initialL extractL) (Fold stepR initialR _) =-    Fold step initial extract+shortest (Fold stepL initialL extractL finalL) (Fold stepR initialR _ finalR) =+    Fold step initial extract final      where @@ -1029,10 +1127,16 @@     runBoth actionL actionR = do         resL <- actionL         resR <- actionR-        return $-            case resL of-                Partial sL -> bimap (Tuple' sL) Right resR-                Done bL -> Done $ Left bL+        case resL of+            Partial sL ->+                case resR of+                    Partial sR -> return $ Partial $ Tuple' sL sR+                    Done bR -> finalL sL >> return (Done (Right bR))+            Done bL -> do+                case resR of+                    Partial sR -> void (finalR sR)+                    Done _ -> return ()+                return (Done (Left bL))      initial = runBoth initialL initialR @@ -1040,6 +1144,8 @@      extract (Tuple' sL _) = Left <$> extractL sL +    final (Tuple' sL sR) = Left <$> finalL sL <* finalR sR+ {-# ANN type LongestState Fuse #-} data LongestState sL sR     = LongestBoth !sL !sR@@ -1057,8 +1163,10 @@ -- {-# INLINE longest #-} longest :: Monad m => Fold m x a -> Fold m x b -> Fold m x (Either a b)-longest (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+longest+    (Fold stepL initialL _ finalL)+    (Fold stepR initialR _ finalR) =+    Fold step initial extract final      where @@ -1081,15 +1189,19 @@     step (LongestLeft sL) a = bimap LongestLeft Left <$> stepL sL a     step (LongestRight sR) a = bimap LongestRight Right <$> stepR sR a -    left sL = Left <$> extractL sL-    extract (LongestLeft sL) = left sL-    extract (LongestRight sR) = Right <$> extractR sR-    extract (LongestBoth sL _) = left sL+    -- XXX Scan with this may not make sense as we cannot determine the longest+    -- until one of them have exhausted.+    extract _ = error $ "longest: scan is not allowed as longest cannot be "+        ++ "determined until one fold has exhausted." -data ConcatMapState m sa a c-    = B !sa-    | forall s. C (s -> a -> m (Step s c)) !s (s -> m c)+    final (LongestLeft sL) = Left <$> finalL sL+    final (LongestRight sR) = Right <$> finalR sR+    final (LongestBoth sL sR) = Left <$> finalL sL <* finalR sR +data ConcatMapState m sa a b c+    = B !sa (sa -> m b)+    | forall s. C (s -> a -> m (Step s c)) !s (s -> m c) (s -> m c)+ -- | Map a 'Fold' returning function on the result of a 'Fold' and run the -- returned fold. This operation can be used to express data dependencies -- between fold operations.@@ -1111,43 +1223,47 @@ -- {-# INLINE concatMap #-} concatMap :: Monad m => (b -> Fold m a c) -> Fold m a b -> Fold m a c-concatMap f (Fold stepa initiala extracta) = Fold stepc initialc extractc+concatMap f (Fold stepa initiala _ finala) =+    Fold stepc initialc extractc finalc   where     initialc = do         r <- initiala         case r of-            Partial s -> return $ Partial (B s)+            Partial s -> return $ Partial (B s finala)             Done b -> initInnerFold (f b) -    stepc (B s) a = do+    stepc (B s fin) a = do         r <- stepa s a         case r of-            Partial s1 -> return $ Partial (B s1)+            Partial s1 -> return $ Partial (B s1 fin)             Done b -> initInnerFold (f b) -    stepc (C stepInner s extractInner) a = do+    stepc (C stepInner s extractInner fin) a = do         r <- stepInner s a         return $ case r of-            Partial sc -> Partial (C stepInner sc extractInner)+            Partial sc -> Partial (C stepInner sc extractInner fin)             Done c -> Done c -    extractc (B s) = do-        r <- extracta s-        initExtract (f r)-    extractc (C _ sInner extractInner) = extractInner sInner+    -- XXX Cannot use for scanning+    extractc _ = error "concatMap: cannot be used for scanning" -    initInnerFold (Fold step i e) = do+    initInnerFold (Fold step i e fin) = do         r <- i         return $ case r of-            Partial s -> Partial (C step s e)+            Partial s -> Partial (C step s e fin)             Done c -> Done c -    initExtract (Fold _ i e) = do+    initFinalize (Fold _ i _ fin) = do         r <- i         case r of-            Partial s -> e s+            Partial s -> fin s             Done c -> return c +    finalc (B s fin) = do+        r <- fin s+        initFinalize (f r)+    finalc (C _ sInner _ fin) = fin sInner+ ------------------------------------------------------------------------------ -- Mapping on input ------------------------------------------------------------------------------@@ -1166,7 +1282,7 @@ -- {-# INLINE lmap #-} lmap :: (a -> b) -> Fold m b r -> Fold m a r-lmap f (Fold step begin done) = Fold step' begin done+lmap f (Fold step begin done final) = Fold step' begin done final     where     step' x a = step x (f a) @@ -1174,7 +1290,7 @@ -- {-# INLINE lmapM #-} lmapM :: Monad m => (a -> m b) -> Fold m b r -> Fold m a r-lmapM f (Fold step begin done) = Fold step' begin done+lmapM f (Fold step begin done final) = Fold step' begin done final     where     step' x a = f a >>= step x @@ -1186,8 +1302,10 @@ -- /Pre-release/ {-# INLINE postscan #-} postscan :: Monad m => Fold m a b -> Fold m b c -> Fold m a c-postscan (Fold stepL initialL extractL) (Fold stepR initialR extractR) =-    Fold step initial extract+postscan+    (Fold stepL initialL extractL finalL)+    (Fold stepR initialR extractR finalR) =+    Fold step initial extract final      where @@ -1198,30 +1316,32 @@             Done bL -> do                 rR <- stepR sR bL                 case rR of-                    Partial sR1 -> Done <$> extractR sR1+                    Partial sR1 -> Done <$> finalR sR1                     Done bR -> return $ Done bR             Partial sL -> do                 !b <- extractL sL                 rR <- stepR sR b-                return-                    $ case rR of-                        Partial sR1 -> Partial (sL, sR1)-                        Done bR -> Done bR+                case rR of+                    Partial sR1 -> return $ Partial (sL, sR1)+                    Done bR -> finalL sL >> return (Done bR)      initial = do-        r <- initialR-        rL <- initialL-        case r of-            Partial sR ->+        rR <- initialR+        case rR of+            Partial sR -> do+                rL <- initialL                 case rL of-                    Done _ -> Done <$> extractR sR+                    Done _ -> Done <$> finalR sR                     Partial sL -> return $ Partial (sL, sR)             Done b -> return $ Done b +    -- XXX should use Tuple'     step (sL, sR) x = runStep (stepL sL x) sR      extract = extractR . snd +    final (sL, sR) = finalL sL *> finalR sR+ ------------------------------------------------------------------------------ -- Filtering ------------------------------------------------------------------------------@@ -1234,7 +1354,7 @@ -- {-# INLINE_NORMAL catMaybes #-} catMaybes :: Monad m => Fold m a b -> Fold m (Maybe a) b-catMaybes (Fold step initial extract) = Fold step1 initial extract+catMaybes (Fold step initial extract final) = Fold step1 initial extract final      where @@ -1274,7 +1394,7 @@ {-# INLINE filter #-} filter :: Monad m => (a -> Bool) -> Fold m a r -> Fold m a r -- filter p = scanMaybe (filtering p)-filter f (Fold step begin done) = Fold step' begin done+filter f (Fold step begin extract final) = Fold step' begin extract final     where     step' x a = if f a then step x a else return $ Partial x @@ -1285,7 +1405,7 @@ -- {-# INLINE filterM #-} filterM :: Monad m => (a -> m Bool) -> Fold m a r -> Fold m a r-filterM f (Fold step begin done) = Fold step' begin done+filterM f (Fold step begin extract final) = Fold step' begin extract final     where     step' x a = do       use <- f a@@ -1374,7 +1494,7 @@ {-# INLINE take #-} take :: Monad m => Int -> Fold m a b -> Fold m a b -- take n = scanMaybe (taking n)-take n (Fold fstep finitial fextract) = Fold step initial extract+take n (Fold fstep finitial fextract ffinal) = Fold step initial extract final      where @@ -1386,7 +1506,7 @@                     s1 = Tuple'Fused i1 s                 if i1 < n                 then return $ Partial s1-                else Done <$> fextract s+                else Done <$> ffinal s             Done b -> return $ Done b      initial = finitial >>= next (-1)@@ -1395,6 +1515,8 @@      extract (Tuple'Fused _ r) = fextract r +    final (Tuple'Fused _ r) = ffinal r+ ------------------------------------------------------------------------------ -- Nesting ------------------------------------------------------------------------------@@ -1412,8 +1534,8 @@ -- /Pre-release/ {-# INLINE duplicate #-} duplicate :: Monad m => Fold m a b -> Fold m a (Fold m a b)-duplicate (Fold step1 initial1 extract1) =-    Fold step initial (\s -> pure $ Fold step1 (pure $ Partial s) extract1)+duplicate (Fold step1 initial1 extract1 final1) =+    Fold step initial extract final      where @@ -1421,6 +1543,11 @@      step s a = second fromPure <$> step1 s a +    -- Scanning may be problematic due to multiple finalizations.+    extract = error "duplicate: scanning may be problematic"++    final s = pure $ Fold step1 (pure $ Partial s) extract1 final1+ -- If there were a finalize/flushing action in the stream type that would be -- equivalent to running initialize in Fold. But we do not have a flushing -- action in streams.@@ -1432,9 +1559,9 @@ -- /Pre-release/ {-# INLINE reduce #-} reduce :: Monad m => Fold m a b -> m (Fold m a b)-reduce (Fold step initial extract) = do+reduce (Fold step initial extract final) = do     i <- initial-    return $ Fold step (return i) extract+    return $ Fold step (return i) extract final  -- This is the dual of Stream @cons@. @@ -1444,7 +1571,8 @@ -- /Pre-release/ {-# INLINE snoclM #-} snoclM :: Monad m => Fold m a b -> m a -> Fold m a b-snoclM (Fold fstep finitial fextract) action = Fold fstep initial fextract+snoclM (Fold fstep finitial fextract ffinal) action =+    Fold fstep initial fextract ffinal      where @@ -1471,7 +1599,8 @@ {-# INLINE snocl #-} snocl :: Monad m => Fold m a b -> a -> Fold m a b -- snocl f = snoclM f . return-snocl (Fold fstep finitial fextract) a = Fold fstep initial fextract+snocl (Fold fstep finitial fextract ffinal) a =+    Fold fstep initial fextract ffinal      where @@ -1491,12 +1620,12 @@ -- /Pre-release/ {-# INLINE snocM #-} snocM :: Monad m => Fold m a b -> m a -> m (Fold m a b)-snocM (Fold step initial extract) action = do+snocM (Fold step initial extract final) action = do     res <- initial     r <- case res of           Partial fs -> action >>= step fs           Done _ -> return res-    return $ Fold step (return r) extract+    return $ Fold step (return r) extract final  -- Definitions: --@@ -1515,12 +1644,12 @@ -- /Pre-release/ {-# INLINE snoc #-} snoc :: Monad m => Fold m a b -> a -> m (Fold m a b)-snoc (Fold step initial extract) a = do+snoc (Fold step initial extract final) a = do     res <- initial     r <- case res of           Partial fs -> step fs a           Done _ -> return res-    return $ Fold step (return r) extract+    return $ Fold step (return r) extract final  -- | Append a singleton value to the fold. --@@ -1548,7 +1677,7 @@ -- /Pre-release/ {-# INLINE extractM #-} extractM :: Monad m => Fold m a b -> m b-extractM (Fold _ initial extract) = do+extractM (Fold _ initial extract _) = do     res <- initial     case res of           Partial fs -> extract fs@@ -1557,14 +1686,15 @@ -- | Close a fold so that it does not accept any more input. {-# INLINE close #-} close :: Monad m => Fold m a b -> Fold m a b-close (Fold _ initial1 extract1) = Fold undefined initial undefined+close (Fold _ initial1 _ final1) =+    Fold undefined initial undefined undefined      where      initial = do         res <- initial1         case res of-              Partial s -> Done <$> extract1 s+              Partial s -> Done <$> final1 s               Done b -> return $ Done b  -- Corresponds to the null check for streams.@@ -1574,7 +1704,7 @@ -- /Pre-release/ {-# INLINE isClosed #-} isClosed :: Monad m => Fold m a b -> m Bool-isClosed (Fold _ initial _) = do+isClosed (Fold _ initial _ _) = do     res <- initial     return $ case res of           Partial _ -> False@@ -1608,8 +1738,10 @@ -- {-# INLINE many #-} many :: Monad m => Fold m a b -> Fold m b c -> Fold m a c-many (Fold sstep sinitial sextract) (Fold cstep cinitial cextract) =-    Fold step initial extract+many+    (Fold sstep sinitial sextract sfinal)+    (Fold cstep cinitial cextract cfinal) =+    Fold step initial extract final      where @@ -1658,6 +1790,13 @@             Partial s -> cextract s             Done b -> return b +    final (ManyFirst ss cs) = sfinal ss *> cfinal cs+    final (ManyLoop ss cs) = do+        cres <- sfinal ss >>= cstep cs+        case cres of+            Partial s -> cfinal s+            Done b -> return b+ -- | Like many, but the "first" fold emits an output at the end even if no -- input is received. --@@ -1667,8 +1806,10 @@ -- {-# INLINE manyPost #-} manyPost :: Monad m => Fold m a b -> Fold m b c -> Fold m a c-manyPost (Fold sstep sinitial sextract) (Fold cstep cinitial cextract) =-    Fold step initial extract+manyPost+    (Fold sstep sinitial sextract sfinal)+    (Fold cstep cinitial cextract cfinal) =+    Fold step initial extract final      where @@ -1704,6 +1845,12 @@             Partial s -> cextract s             Done b -> return b +    final (Tuple' ss cs) = do+        cres <- sfinal ss >>= cstep cs+        case cres of+            Partial s -> cfinal s+            Done b -> return b+ -- | @groupsOf n split collect@ repeatedly applies the @split@ fold to chunks -- of @n@ items in the input stream and supplies the result to the @collect@ -- fold.@@ -1732,7 +1879,10 @@ -- {-# INLINE refoldMany #-} refoldMany :: Monad m => Fold m a b -> Refold m x b c -> Refold m x a c-refoldMany (Fold sstep sinitial sextract) (Refold cstep cinject cextract) =+refoldMany+    (Fold sstep sinitial sextract _sfinal)+    -- XXX We will need a "final" in refold as well+    (Refold cstep cinject cextract) =     Refold step inject extract      where@@ -1783,7 +1933,9 @@ -- /Internal/ {-# INLINE refoldMany1 #-} refoldMany1 :: Monad m => Refold m x a b -> Fold m b c -> Refold m x a c-refoldMany1 (Refold sstep sinject sextract) (Fold cstep cinitial cextract) =+refoldMany1+    (Refold sstep sinject sextract)+    (Fold cstep cinitial cextract _cfinal) =     Refold step inject extract      where@@ -1834,7 +1986,7 @@ {-# INLINE refold #-} refold :: Monad m => Refold m b a c -> Fold m a b -> Fold m a c refold (Refold step inject extract) f =-    Fold step (extractM f >>= inject) extract+    Fold step (extractM f >>= inject) extract extract  ------------------------------------------------------------------------------ -- morphInner@@ -1844,8 +1996,8 @@ -- -- /Pre-release/ morphInner :: (forall x. m x -> n x) -> Fold m a b -> Fold n a b-morphInner f (Fold step initial extract) =-    Fold (\x a -> f $ step x a) (f initial) (f . extract)+morphInner f (Fold step initial extract final) =+    Fold (\x a -> f $ step x a) (f initial) (f . extract) (f . final)  -- | Adapt a pure fold to any monad. --
src/Streamly/Internal/Data/Fold/Window.hs view
@@ -36,24 +36,24 @@     -- window folds by keeping the second element of the input tuple as     -- @Nothing@.     ---      lmap+      windowLmap     , cumulative -    , rollingMap-    , rollingMapM+    , windowRollingMap+    , windowRollingMapM      -- ** Sums-    , length-    , sum-    , sumInt-    , powerSum-    , powerSumFrac+    , windowLength+    , windowSum+    , windowSumInt+    , windowPowerSum+    , windowPowerSumFrac      -- ** Location-    , minimum-    , maximum-    , range-    , mean+    , windowMinimum+    , windowMaximum+    , windowRange+    , windowMean     ) where @@ -65,16 +65,18 @@ import Streamly.Internal.Data.Tuple.Strict     (Tuple'(..), Tuple3Fused' (Tuple3Fused')) +import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+ import qualified Streamly.Internal.Data.Fold.Type as Fold-import qualified Streamly.Internal.Data.Ring.Unboxed as Ring+import qualified Streamly.Internal.Data.Ring as Ring  import Prelude hiding (length, sum, minimum, maximum)  -- $setup -- >>> import Data.Bifunctor(bimap) -- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Fold.Window as FoldW--- >>> import qualified Streamly.Internal.Data.Ring.Unboxed as Ring+-- >>> import qualified Streamly.Internal.Data.Fold.Window as Fold+-- >>> import qualified Streamly.Internal.Data.Ring as Ring -- >>> import qualified Streamly.Data.Stream as Stream -- >>> import Prelude hiding (length, sum, minimum, maximum) @@ -87,9 +89,9 @@ -- -- >>> lmap f = Fold.lmap (bimap f (f <$>)) ---{-# INLINE lmap #-}-lmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b-lmap f = Fold.lmap (bimap f (f <$>))+{-# INLINE windowLmap #-}+windowLmap :: (c -> a) -> Fold m (a, Maybe a) b -> Fold m (c, Maybe c) b+windowLmap f = Fold.lmap (bimap f (f <$>))  -- | Convert an incremental fold to a cumulative fold using the entire input -- stream as a single window.@@ -105,10 +107,10 @@  -- | Apply an effectful function on the latest and the oldest element of the -- window.-{-# INLINE rollingMapM #-}-rollingMapM :: Monad m =>+{-# INLINE windowRollingMapM #-}+windowRollingMapM :: Monad m =>     (Maybe a -> a -> m (Maybe b)) -> Fold m (a, Maybe a) (Maybe b)-rollingMapM f = Fold.foldlM' f1 initial+windowRollingMapM f = Fold.foldlM' f1 initial      where @@ -118,12 +120,12 @@  -- | Apply a pure function on the latest and the oldest element of the window. ----- >>> rollingMap f = FoldW.rollingMapM (\x y -> return $ f x y)+-- >>> windowRollingMap f = Fold.windowRollingMapM (\x y -> return $ f x y) ---{-# INLINE rollingMap #-}-rollingMap :: Monad m =>+{-# INLINE windowRollingMap #-}+windowRollingMap :: Monad m =>     (Maybe a -> a -> Maybe b) -> Fold m (a, Maybe a) (Maybe b)-rollingMap f = Fold.foldl' f1 initial+windowRollingMap f = Fold.foldl' f1 initial      where @@ -146,9 +148,9 @@ -- -- /Internal/ ---{-# INLINE sumInt #-}-sumInt :: forall m a. (Monad m, Integral a) => Fold m (a, Maybe a) a-sumInt = Fold step initial extract+{-# INLINE windowSumInt #-}+windowSumInt :: forall m a. (Monad m, Integral a) => Fold m (a, Maybe a) a+windowSumInt = Fold step initial extract extract      where @@ -180,9 +182,9 @@ -- -- /Time/: \(\mathcal{O}(n)\) ---{-# INLINE sum #-}-sum :: forall m a. (Monad m, Num a) => Fold m (a, Maybe a) a-sum = Fold step initial extract+{-# INLINE windowSum #-}+windowSum :: forall m a. (Monad m, Num a) => Fold m (a, Maybe a) a+windowSum = Fold step initial extract extract      where @@ -219,9 +221,9 @@ -- -- >>> length = powerSum 0 ---{-# INLINE length #-}-length :: (Monad m, Num b) => Fold m (a, Maybe a) b-length = Fold.foldl' step 0+{-# INLINE windowLength #-}+windowLength :: (Monad m, Num b) => Fold m (a, Maybe a) b+windowLength = Fold.foldl' step 0      where @@ -237,18 +239,18 @@ -- /Space/: \(\mathcal{O}(1)\) -- -- /Time/: \(\mathcal{O}(n)\)-{-# INLINE powerSum #-}-powerSum :: (Monad m, Num a) => Int -> Fold m (a, Maybe a) a-powerSum k = lmap (^ k) sum+{-# INLINE windowPowerSum #-}+windowPowerSum :: (Monad m, Num a) => Int -> Fold m (a, Maybe a) a+windowPowerSum k = windowLmap (^ k) windowSum  -- | Like 'powerSum' but powers can be negative or fractional. This is slower -- than 'powerSum' for positive intergal powers. -- -- >>> powerSumFrac p = lmap (** p) sum ---{-# INLINE powerSumFrac #-}-powerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a-powerSumFrac p = lmap (** p) sum+{-# INLINE windowPowerSumFrac #-}+windowPowerSumFrac :: (Monad m, Floating a) => a -> Fold m (a, Maybe a) a+windowPowerSumFrac p = windowLmap (** p) windowSum  ------------------------------------------------------------------------------- -- Location@@ -265,9 +267,9 @@ -- -- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size. ---{-# INLINE range #-}-range :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe (a, a))-range n = Fold step initial extract+{-# INLINE windowRange #-}+windowRange :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe (a, a))+windowRange n = Fold step initial extract extract      where @@ -300,7 +302,11 @@         if i == 0         then return Nothing         else do-            x <- liftIO $ peek rh+            -- Here we use "ringStart" over "ringHead" as "ringHead" will be+            -- uninitialized if the ring is not full.+            -- Using "unsafeForeignPtrToPtr" here is safe as we touch the ring+            -- again in "foldFunc".+            x <- liftIO $ peek (unsafeForeignPtrToPtr (Ring.ringStart rb))             let accum (mn, mx) a = return (min mn a, max mx a)             fmap Just $ foldFunc i rh accum (x, x) rb @@ -316,9 +322,9 @@ -- -- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size. ---{-# INLINE minimum #-}-minimum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)-minimum n = fmap (fmap fst) $ range n+{-# INLINE windowMinimum #-}+windowMinimum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)+windowMinimum n = fmap (fmap fst) $ windowRange n  -- | The maximum element in a rolling window. --@@ -329,9 +335,9 @@ -- -- /Time/: \(\mathcal{O}(n*w)\) where \(w\) is the window size. ---{-# INLINE maximum #-}-maximum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)-maximum n = fmap (fmap snd) $ range n+{-# INLINE windowMaximum #-}+windowMaximum :: (MonadIO m, Storable a, Ord a) => Int -> Fold m a (Maybe a)+windowMaximum n = fmap (fmap snd) $ windowRange n  -- | Arithmetic mean of elements in a sliding window: --@@ -346,6 +352,6 @@ -- /Space/: \(\mathcal{O}(1)\) -- -- /Time/: \(\mathcal{O}(n)\)-{-# INLINE mean #-}-mean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a-mean = Fold.teeWith (/) sum length+{-# INLINE windowMean #-}+windowMean :: forall m a. (Monad m, Fractional a) => Fold m (a, Maybe a) a+windowMean = Fold.teeWith (/) windowSum windowLength
src/Streamly/Internal/Data/IOFinalizer.hs view
@@ -66,6 +66,9 @@ -- never runs again.  Note, the finalizing action runs with async exceptions -- masked. --+-- If this function is called multiple times, the action is guaranteed to run+-- once and only once.+-- -- /Pre-release/ runIOFinalizer :: MonadIO m => IOFinalizer -> m () runIOFinalizer (IOFinalizer ref) = liftIO $ do@@ -83,9 +86,16 @@ -- | Run an action clearing the finalizer atomically wrt async exceptions. The -- action is run with async exceptions masked. --+-- This function can be called at most once after setting the finalizer. If the+-- finalizer is not set it is considered a bug.+-- -- /Pre-release/ clearingIOFinalizer :: MonadIO m => IOFinalizer -> IO a -> m a clearingIOFinalizer (IOFinalizer ref) action = do     liftIO $ mask_ $ do-        writeIORef ref Nothing-        action+        res <- readIORef ref+        case res of+            Just _ -> do+                writeIORef ref Nothing+                action+            Nothing -> error "clearingIOFinalizer: finalizer not set"
src/Streamly/Internal/Data/IORef/Unboxed.hs view
@@ -23,36 +23,31 @@     (       IORef -    -- * Construction+    -- Construction     , newIORef -    -- * Write+    -- Write     , writeIORef     , modifyIORef' -    -- * Read+    -- Read     , readIORef-    , toStreamD+    , pollIntIORef     ) where  #include "inline.hs" -import Data.Proxy (Proxy(..)) import Control.Monad.IO.Class (MonadIO(..))-import Streamly.Internal.Data.Unboxed-    ( MutableByteArray(..)-    , Unbox-    , sizeOf-    , peekWith-    , pokeWith-    , newUnpinnedBytes-    )+import Data.Proxy (Proxy(..))+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)+import Streamly.Internal.Data.Unbox (Unbox(..), sizeOf) -import qualified Streamly.Internal.Data.Stream.StreamD.Type as D+import qualified Streamly.Internal.Data.MutByteArray.Type as MBA+import qualified Streamly.Internal.Data.Stream.Type as D  -- | An 'IORef' holds a single 'Unbox'-able value.-newtype IORef a = IORef MutableByteArray+newtype IORef a = IORef MutByteArray  -- | Create a new 'IORef'. --@@ -60,8 +55,8 @@ {-# INLINE newIORef #-} newIORef :: forall a. Unbox a => a -> IO (IORef a) newIORef x = do-    var <- newUnpinnedBytes (sizeOf (Proxy :: Proxy a))-    pokeWith var 0 x+    var <- MBA.new (sizeOf (Proxy :: Proxy a))+    pokeAt 0 var x     return $ IORef var  -- | Write a value to an 'IORef'.@@ -69,14 +64,14 @@ -- /Pre-release/ {-# INLINE writeIORef #-} writeIORef :: Unbox a => IORef a -> a -> IO ()-writeIORef (IORef var) = pokeWith var 0+writeIORef (IORef var) = pokeAt 0 var  -- | Read a value from an 'IORef'. -- -- /Pre-release/ {-# INLINE readIORef #-} readIORef :: Unbox a => IORef a -> IO a-readIORef (IORef var) = peekWith var 0+readIORef (IORef var) = peekAt 0 var  -- | Modify the value of an 'IORef' using a function with strict application. --@@ -89,10 +84,16 @@  -- | Generate a stream by continuously reading the IORef. --+-- This operation reads the IORef without any synchronization. It can be+-- assumed to be atomic because the IORef (MutableByteArray) is always aligned+-- to Int boundaries, we are assuming that compiler uses single instructions to+-- access the memory. It may read stale values though until caches are+-- synchronised in a multiprocessor architecture.+-- -- /Pre-release/-{-# INLINE_NORMAL toStreamD #-}-toStreamD :: (MonadIO m, Unbox a) => IORef a -> D.Stream m a-toStreamD var = D.Stream step ()+{-# INLINE_NORMAL pollIntIORef #-}+pollIntIORef :: (MonadIO m, Unbox a) => IORef a -> D.Stream m a+pollIntIORef var = D.Stream step ()      where 
src/Streamly/Internal/Data/IsMap.hs view
@@ -28,6 +28,8 @@     mapDelete :: Key f -> f a -> f a     mapUnion :: f a -> f a -> f a     mapNull :: f a -> Bool+    mapTraverseWithKey ::+        Applicative t => (Key f -> a -> t b) -> f a -> t (f b)  instance Ord k => IsMap (Map k) where     type Key (Map k) = k@@ -39,6 +41,7 @@     mapDelete = Map.delete     mapUnion = Map.union     mapNull = Map.null+    mapTraverseWithKey = Map.traverseWithKey  instance IsMap IntMap.IntMap where     type Key IntMap.IntMap = Int@@ -50,3 +53,4 @@     mapDelete = IntMap.delete     mapUnion = IntMap.union     mapNull = IntMap.null+    mapTraverseWithKey = IntMap.traverseWithKey
− src/Streamly/Internal/Data/List.hs
@@ -1,169 +0,0 @@-{-# LANGUAGE UndecidableInstances #-}---- |--- Module      : Streamly.Internal.Data.List--- Copyright   : (c) 2018 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : pre-release--- Portability : GHC------ Lists are just a special case of monadic streams. The stream type @Stream--- Identity a@ can be used as a replacement for @[a]@.  The 'List' type in this--- module is just a newtype wrapper around @Stream Identity@ for better type--- inference when using the 'OverloadedLists' GHC extension. @List a@ provides--- better performance compared to @[a]@. Standard list, string and list--- comprehension syntax can be used with the 'List' type by enabling--- 'OverloadedLists', 'OverloadedStrings' and 'MonadComprehensions' GHC--- extensions.  There would be a slight difference in the 'Show' and 'Read'--- strings of streamly list as compared to regular lists.------ Conversion to stream types is free, any stream combinator can be used on--- lists by converting them to streams.  However, for convenience, this module--- provides combinators that work directly on the 'List' type.--------- @--- List $ S.map (+ 1) $ toStream (1 \`Cons\` Nil)--- @------ To convert a 'List' to regular lists, you can use any of the following:------ * @toList . toStream@ and @toStream . fromList@--- * 'Data.Foldable.toList' from "Data.Foldable"--- * 'GHC.Exts.toList' and 'GHC.Exts.fromList' from 'IsList' in "GHC.Exts"------ If you have made use of 'Nil' and 'Cons' constructors in the code and you--- want to replace streamly lists with standard lists, all you need to do is--- import these definitions:------ @--- type List = []--- pattern Nil <- [] where Nil = []--- pattern Cons x xs = x : xs--- infixr 5 `Cons`--- {-\# COMPLETE Cons, Nil #-}--- @------ See <src/docs/streamly-vs-lists.md> for more details and--- <src/test/PureStreams.hs> for comprehensive usage examples.----module Streamly.Internal.Data.List-    (-    List (Nil, Cons)--    , toStream-    , fromStream--    -- XXX we may want to use rebindable syntax for variants instead of using-    -- different types (applicative do and apWith).-    , ZipList (..)-    , fromZipList-    , toZipList-    )-where--import Control.Arrow (second)-import Data.Functor.Identity (Identity, runIdentity)-import GHC.Exts (IsList(..), IsString(..))-import Streamly.Internal.Data.Stream.Cross (CrossStream(..))-import Streamly.Internal.Data.Stream.Type (Stream)-import Streamly.Internal.Data.Stream.Zip (ZipStream(..))-import Text.Read (readPrec)--import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Stream.Type as Stream---- XXX Rename to PureStream.---- | @List a@ is a replacement for @[a]@.------ /Pre-release/-newtype List a = List { toCrossStream :: CrossStream Identity a }-    deriving-    ( Eq, Ord-    , Semigroup, Monoid, Functor, Foldable-    , Applicative, Traversable, Monad, IsList)--toStream :: List a -> Stream Identity a-toStream = unCrossStream . toCrossStream--fromStream :: Stream Identity a -> List a-fromStream xs = List (CrossStream xs)--instance (a ~ Char) => IsString (List a) where-    {-# INLINE fromString #-}-    fromString = List . fromList--instance Show a => Show (List a) where-    show (List x) = show $ unCrossStream x--instance Read a => Read (List a) where-    readPrec = fromStream <$> readPrec----------------------------------------------------------------------------------- Patterns----------------------------------------------------------------------------------- Note: When using the OverloadedLists extension we should be able to pattern--- match using the regular list contructors. OverloadedLists uses 'toList' to--- perform the pattern match, it should not be too bad as it works lazily in--- the Identity monad. We need these patterns only when not using that--- extension.---- | An empty list constructor and pattern that matches an empty 'List'.--- Corresponds to '[]' for Haskell lists.----pattern Nil :: List a-pattern Nil <- (runIdentity . K.null . Stream.toStreamK . toStream -> True)--    where--    Nil = List $ CrossStream (Stream.fromStreamK K.nil)--infixr 5 `Cons`---- | A list constructor and pattern that deconstructs a 'List' into its head--- and tail. Corresponds to ':' for Haskell lists.----pattern Cons :: a -> List a -> List a-pattern Cons x xs <--    (fmap (second (List . CrossStream . Stream.fromStreamK))-        . runIdentity . K.uncons . Stream.toStreamK . toStream-            -> Just (x, xs)-    )--    where--    Cons x xs = List $ CrossStream $ Stream.cons x (toStream xs)--{-# COMPLETE Nil, Cons #-}----------------------------------------------------------------------------------- ZipList----------------------------------------------------------------------------------- | Just like 'List' except that it has a zipping 'Applicative' instance--- and no 'Monad' instance.----newtype ZipList a = ZipList { toZipStream :: ZipStream Identity a }-    deriving-    ( Show, Read, Eq, Ord-    , Semigroup, Monoid, Functor, Foldable-    , Applicative, Traversable, IsList-    )--instance (a ~ Char) => IsString (ZipList a) where-    {-# INLINE fromString #-}-    fromString = ZipList . fromList---- | Convert a 'ZipList' to a regular 'List'----fromZipList :: ZipList a -> List a-fromZipList (ZipList zs) = List $ CrossStream (unZipStream zs)---- | Convert a regular 'List' to a 'ZipList'----toZipList :: List a -> ZipList a-toZipList = ZipList . ZipStream . toStream
+ src/Streamly/Internal/Data/MutArray.hs view
@@ -0,0 +1,91 @@+-- |+-- Module      : Streamly.Internal.Data.MutArray+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.MutArray+    (+    -- * MutArray.Type module+      module Streamly.Internal.Data.MutArray.Type+    -- * MutArray module+    , splitOn+    , genSlicesFromLen+    , getSlicesFromLen+    , fromStream+    -- * Unboxed IORef+    , module Streamly.Internal.Data.IORef.Unboxed+    )+where++#include "inline.hs"++import Control.Monad.IO.Class (MonadIO(..))+import Streamly.Internal.Data.Unbox (Unbox)+import Streamly.Internal.Data.Stream (Stream)+import Streamly.Internal.Data.Unfold.Type (Unfold(..))++import qualified Streamly.Internal.Data.Stream as D+import qualified Streamly.Internal.Data.Unfold as Unfold++import Prelude hiding (foldr, length, read, splitAt)+import Streamly.Internal.Data.MutArray.Type+import Streamly.Internal.Data.IORef.Unboxed++-- | Split the array into a stream of slices using a predicate. The element+-- matching the predicate is dropped.+--+-- /Pre-release/+{-# INLINE splitOn #-}+splitOn :: (MonadIO m, Unbox a) =>+    (a -> Bool) -> MutArray a -> Stream m (MutArray a)+splitOn predicate arr =+    fmap (\(i, len) -> getSliceUnsafe i len arr)+        $ D.sliceOnSuffix predicate (read arr)++-- | Generate a stream of array slice descriptors ((index, len)) of specified+-- length from an array, starting from the supplied array index. The last slice+-- may be shorter than the requested length depending on the array length.+--+-- /Pre-release/+{-# INLINE genSlicesFromLen #-}+genSlicesFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (MutArray a) (Int, Int)+genSlicesFromLen from len =+    let fromThenTo n = (from, from + len, n - 1)+        mkSlice n i = return (i, min len (n - i))+     in Unfold.lmap length+        $ Unfold.mapM2 mkSlice+        $ Unfold.lmap fromThenTo Unfold.enumerateFromThenTo++-- | Generate a stream of slices of specified length from an array, starting+-- from the supplied array index. The last slice may be shorter than the+-- requested length depending on the array length.+--+-- /Pre-release/+{-# INLINE getSlicesFromLen #-}+getSlicesFromLen :: forall m a. (Monad m, Unbox a)+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> Unfold m (MutArray a) (MutArray a)+getSlicesFromLen from len =+    let mkSlice arr (i, n) = return $ getSliceUnsafe i n arr+     in Unfold.mapM2 mkSlice (genSlicesFromLen from len)++-- | Create an 'Array' from a stream. This is useful when we want to create a+-- single array from a stream of unknown size. 'writeN' is at least twice+-- as efficient when the size is already known.+--+-- Note that if the input stream is too large memory allocation for the array+-- may fail.  When the stream size is not known, `chunksOf` followed by+-- processing of indvidual arrays in the resulting stream should be preferred.+--+-- /Pre-release/+{-# INLINE fromStream #-}+fromStream :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)+fromStream = fromStreamD+-- fromStream (Stream m) = P.fold write m
+ src/Streamly/Internal/Data/MutArray/Generic.hs view
@@ -0,0 +1,912 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UnboxedTuples #-}+-- |+-- Module      : Streamly.Internal.Data.MutArray.Generic+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.MutArray.Generic+(+    -- * Type+    -- $arrayNotes+      MutArray (..)++    -- * Constructing and Writing+    -- ** Construction+    , nil++    -- *** Uninitialized Arrays+    , new+    -- , newArrayWith++    -- *** From streams+    , writeNUnsafe+    , writeN+    , writeWith+    , write+    , fromStreamN+    , fromStream+    , fromPureStream++    -- , writeRevN+    -- , writeRev++    -- ** From containers+    , fromListN+    , fromList++    -- * Random writes+    , putIndex+    , putIndexUnsafe+    , putIndices+    -- , putFromThenTo+    -- , putFrom -- start writing at the given position+    -- , putUpto -- write from beginning up to the given position+    -- , putFromTo+    -- , putFromRev+    -- , putUptoRev+    , modifyIndexUnsafe+    , modifyIndex+    -- , modifyIndices+    -- , modify+    -- , swapIndices++    -- * Growing and Shrinking+    -- Arrays grow only at the end, though it is possible to grow on both sides+    -- and therefore have a cons as well as snoc. But that will require two+    -- bounds in the array representation.++    -- ** Reallocation+    , realloc+    , uninit++    -- ** Appending elements+    , snocWith+    , snoc+    -- , snocLinear+    -- , snocMay+    , snocUnsafe++    -- ** Appending streams+    -- , writeAppendNUnsafe+    -- , writeAppendN+    -- , writeAppendWith+    -- , writeAppend++    -- ** Truncation+    -- These are not the same as slicing the array at the beginning, they may+    -- reduce the length as well as the capacity of the array.+    -- , truncateWith+    -- , truncate+    -- , truncateExp++    -- * Eliminating and Reading++    -- ** Unfolds+    , reader+    -- , readerRev+    , producerWith -- experimental+    , producer -- experimental++    -- ** To containers+    , read+    , readRev+    , toStreamK+    -- , toStreamKRev+    , toList++    -- ** Random reads+    , getIndex+    , getIndexUnsafe+    , getIndexUnsafeWith+    -- , getIndices+    -- , getFromThenTo+    -- , getIndexRev++    -- * Size+    , length++    -- * In-place Mutation Algorithms+    , strip+    -- , reverse+    -- , permute+    -- , partitionBy+    -- , shuffleBy+    -- , divideBy+    -- , mergeBy++    -- * Folding+    -- , foldl'+    -- , foldr+    , cmp+    , eq++    -- * Arrays of arrays+    --  We can add dimensionality parameter to the array type to get+    --  multidimensional arrays. Multidimensional arrays would just be a+    --  convenience wrapper on top of single dimensional arrays.++    -- | Operations dealing with multiple arrays, streams of arrays or+    -- multidimensional array representations.++    -- ** Construct from streams+    , chunksOf+    -- , arrayStreamKFromStreamD+    -- , writeChunks++    -- ** Eliminate to streams+    -- , flattenArrays+    -- , flattenArraysRev+    -- , fromArrayStreamK++    -- ** Construct from arrays+    -- get chunks without copying+    , getSliceUnsafe+    , getSlice+    -- , getSlicesFromLenN+    -- , splitAt -- XXX should be able to express using getSlice+    -- , breakOn++    -- ** Appending arrays+    -- , spliceCopy+    -- , spliceWith+    -- , splice+    -- , spliceExp+    , putSliceUnsafe+    -- , appendSlice+    -- , appendSliceFrom++    , clone+    )+where++#include "inline.hs"+#include "assert.hs"++import Control.Monad (when)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Functor.Identity (Identity(..))+import GHC.Base+    ( MutableArray#+    , RealWorld+    , copyMutableArray#+    , newArray#+    , readArray#+    , writeArray#+    )+import GHC.IO (IO(..))+import GHC.Int (Int(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Producer.Type (Producer (..))+import Streamly.Internal.Data.Unfold.Type (Unfold(..))+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.SVar.Type (adaptState)++import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.Producer as Producer+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Generate as D+import qualified Streamly.Internal.Data.Stream.Lift as D+import qualified Streamly.Internal.Data.StreamK.Type as K++import Prelude hiding (read, length)++#include "DocTestDataMutArrayGeneric.hs"++-------------------------------------------------------------------------------+-- MutArray Data Type+-------------------------------------------------------------------------------++data MutArray a =+    MutArray+        { arrContents# :: MutableArray# RealWorld a+          -- ^ The internal contents of the array representing the entire array.++        , arrStart :: {-# UNPACK #-}!Int+          -- ^ The starting index of this slice.++        , arrLen :: {-# UNPACK #-}!Int+          -- ^ The length of this slice.++        , arrTrueLen :: {-# UNPACK #-}!Int+          -- ^ This is the true length of the array. Coincidentally, this also+          -- represents the first index beyond the maximum acceptable index of+          -- the array. This is specific to the array contents itself and not+          -- dependent on the slice. This value should not change and is shared+          -- across all the slices.+        }++{-# INLINE bottomElement #-}+bottomElement :: a+bottomElement =+    error+        $ unwords+              [ funcName+              , "This is the bottom element of the array."+              , "This is a place holder and should never be reached!"+              ]++    where++    funcName = "Streamly.Internal.Data.MutArray.Generic.bottomElement:"++-- XXX Would be nice if GHC can provide something like newUninitializedArray# so+-- that we do not have to write undefined or error in the whole array.++-- | @new count@ allocates a zero length array that can be extended to hold+-- up to 'count' items without reallocating.+--+-- /Pre-release/+{-# INLINE new #-}+new :: MonadIO m => Int -> m (MutArray a)+new n@(I# n#) =+    liftIO+        $ IO+        $ \s# ->+              case newArray# n# bottomElement s# of+                  (# s1#, arr# #) ->+                      let ma = MutArray arr# 0 0 n+                       in (# s1#, ma #)++-- XXX This could be pure?++-- |+-- Definition:+--+-- >>> nil = MutArray.new 0+{-# INLINE nil #-}+nil :: MonadIO m => m (MutArray a)+nil = new 0++-------------------------------------------------------------------------------+-- Random writes+-------------------------------------------------------------------------------++-- | Write the given element to the given index of the 'MutableArray#'. Does not+-- check if the index is out of bounds of the array.+--+-- /Pre-release/+{-# INLINE putIndexUnsafeWith #-}+putIndexUnsafeWith :: MonadIO m => Int -> MutableArray# RealWorld a -> a -> m ()+putIndexUnsafeWith n _arrContents# x =+    liftIO+        $ IO+        $ \s# ->+              case n of+                  I# n# ->+                      let s1# = writeArray# _arrContents# n# x s#+                       in (# s1#, () #)++-- | Write the given element to the given index of the array. Does not check if+-- the index is out of bounds of the array.+--+-- /Pre-release/+{-# INLINE putIndexUnsafe #-}+putIndexUnsafe :: forall m a. MonadIO m => Int -> MutArray a -> a -> m ()+putIndexUnsafe i MutArray {..} x =+    assert (i >= 0 && i < arrLen)+    putIndexUnsafeWith (i + arrStart) arrContents# x++invalidIndex :: String -> Int -> a+invalidIndex label i =+    error $ label ++ ": invalid array index " ++ show i++-- | /O(1)/ Write the given element at the given index in the array.+-- Performs in-place mutation of the array.+--+-- >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))+--+-- /Pre-release/+{-# INLINE putIndex #-}+putIndex :: MonadIO m => Int -> MutArray a -> a -> m ()+putIndex i arr@MutArray {..} x =+    if i >= 0 && i < arrLen+    then putIndexUnsafe i arr x+    else invalidIndex "putIndex" i++-- | Write an input stream of (index, value) pairs to an array. Throws an+-- error if any index is out of bounds.+--+-- /Pre-release/+{-# INLINE putIndices #-}+putIndices :: MonadIO m+    => MutArray a -> Fold m (Int, a) ()+putIndices arr = FL.foldlM' step (return ())++    where++    step () (i, x) = liftIO (putIndex i arr x)++-- | Modify a given index of an array using a modifier function without checking+-- the bounds.+--+-- Unsafe because it does not check the bounds of the array.+--+-- /Pre-release/+modifyIndexUnsafe :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndexUnsafe i MutArray {..} f = do+    liftIO+        $ IO+        $ \s# ->+              case i + arrStart of+                  I# n# ->+                      case readArray# arrContents# n# s# of+                          (# s1#, a #) ->+                              let (a1, b) = f a+                                  s2# = writeArray# arrContents# n# a1 s1#+                               in (# s2#, b #)++-- | Modify a given index of an array using a modifier function.+--+-- /Pre-release/+modifyIndex :: MonadIO m => Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndex i arr@MutArray {..} f = do+    if i >= 0 && i < arrLen+    then modifyIndexUnsafe i arr f+    else invalidIndex "modifyIndex" i++-------------------------------------------------------------------------------+-- Resizing+-------------------------------------------------------------------------------++-- | Reallocates the array according to the new size. This is a safe function+-- that always creates a new array and copies the old array into the new one.+-- If the reallocated size is less than the original array it results in a+-- truncated version of the original array.+--+realloc :: MonadIO m => Int -> MutArray a -> m (MutArray a)+realloc n arr = do+    arr1 <- new n+    let !newLen@(I# newLen#) = min n (arrLen arr)+        !(I# arrS#) = arrStart arr+        !(I# arr1S#) = arrStart arr1+        arrC# = arrContents# arr+        arr1C# = arrContents# arr1+    liftIO+        $ IO+        $ \s# ->+              let s1# = copyMutableArray# arrC# arrS# arr1C# arr1S# newLen# s#+               in (# s1#, arr1 {arrLen = newLen, arrTrueLen = n} #)++reallocWith ::+       MonadIO m => String -> (Int -> Int) -> Int -> MutArray a -> m (MutArray a)+reallocWith label sizer reqSize arr = do+    let oldSize = arrLen arr+        newSize = sizer oldSize+        safeSize = max newSize (oldSize + reqSize)+    assert (newSize >= oldSize + reqSize || error badSize) (return ())+    realloc safeSize arr++    where++    badSize = concat+        [ label+        , ": new array size is less than required size "+        , show reqSize+        , ". Please check the sizing function passed."+        ]++-------------------------------------------------------------------------------+-- Snoc+-------------------------------------------------------------------------------++-- XXX Not sure of the behavior of writeArray# if we specify an index which is+-- out of bounds. This comment should be rewritten based on that.+-- | Really really unsafe, appends the element into the first array, may+-- cause silent data corruption or if you are lucky a segfault if the index+-- is out of bounds.+--+-- /Internal/+{-# INLINE snocUnsafe #-}+snocUnsafe :: MonadIO m => MutArray a -> a -> m (MutArray a)+snocUnsafe arr@MutArray {..} a = do+    assert (arrStart + arrLen < arrTrueLen) (return ())+    let arr1 = arr {arrLen = arrLen + 1}+    putIndexUnsafe arrLen arr1 a+    return arr1++-- NOINLINE to move it out of the way and not pollute the instruction cache.+{-# NOINLINE snocWithRealloc #-}+snocWithRealloc :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)+snocWithRealloc sizer arr x = do+    arr1 <- reallocWith "snocWithRealloc" sizer 1 arr+    snocUnsafe arr1 x++-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of+-- the array increases by 1.+--+-- If there is no reserved space available in @arr@ it is reallocated to a size+-- in bytes determined by the @sizer oldSize@ function, where @oldSize@ is the+-- original size of the array.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- /Pre-release/+{-# INLINE snocWith #-}+snocWith :: MonadIO m => (Int -> Int) -> MutArray a -> a -> m (MutArray a)+snocWith sizer arr@MutArray {..} x = do+    if arrStart + arrLen < arrTrueLen+    then snocUnsafe arr x+    else snocWithRealloc sizer arr x++-- XXX round it to next power of 2.++-- | The array is mutated to append an additional element to it. If there is no+-- reserved space available in the array then it is reallocated to double the+-- original size.+--+-- This is useful to reduce allocations when appending unknown number of+-- elements.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- >>> snoc = MutArray.snocWith (* 2)+--+-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.+--+-- /Pre-release/+{-# INLINE snoc #-}+snoc :: MonadIO m => MutArray a -> a -> m (MutArray a)+snoc = snocWith (* 2)++-- | Make the uninitialized memory in the array available for use extending it+-- by the supplied length beyond the current length of the array. The array may+-- be reallocated.+--+{-# INLINE uninit #-}+uninit :: MonadIO m => MutArray a -> Int -> m (MutArray a)+uninit arr@MutArray{..} len =+    if arrStart + arrLen + len <= arrTrueLen+    then return $ arr {arrLen = arrLen + len}+    else realloc (arrLen + len) arr++-------------------------------------------------------------------------------+-- Random reads+-------------------------------------------------------------------------------++-- | Return the element at the specified index without checking the bounds from+-- a @MutableArray# RealWorld@.+--+-- Unsafe because it does not check the bounds of the array.+{-# INLINE getIndexUnsafeWith #-}+getIndexUnsafeWith :: MonadIO m => MutableArray# RealWorld a -> Int -> m a+getIndexUnsafeWith _arrContents# n =+    liftIO+        $ IO+        $ \s# ->+              let !(I# i#) = n+               in readArray# _arrContents# i# s#++-- | Return the element at the specified index without checking the bounds.+--+-- Unsafe because it does not check the bounds of the array.+{-# INLINE_NORMAL getIndexUnsafe #-}+getIndexUnsafe :: MonadIO m => Int -> MutArray a -> m a+getIndexUnsafe n MutArray {..} = getIndexUnsafeWith arrContents# (n + arrStart)++-- | /O(1)/ Lookup the element at the given index. Index starts from 0.+--+{-# INLINE getIndex #-}+getIndex :: MonadIO m => Int -> MutArray a -> m (Maybe a)+getIndex i arr@MutArray {..} =+    if i >= 0 && i < arrLen+    then Just <$> getIndexUnsafe i arr+    else return Nothing++-------------------------------------------------------------------------------+-- Subarrays+-------------------------------------------------------------------------------++-- XXX We can also get immutable slices.++-- | /O(1)/ Slice an array in constant time.+--+-- Unsafe: The bounds of the slice are not checked.+--+-- /Unsafe/+--+-- /Pre-release/+{-# INLINE getSliceUnsafe #-}+getSliceUnsafe+    :: Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSliceUnsafe index len arr@MutArray {..} =+    assert (index >= 0 && len >= 0 && index + len <= arrLen)+        $ arr {arrStart = arrStart + index, arrLen = len}++-- | /O(1)/ Slice an array in constant time. Throws an error if the slice+-- extends out of the array bounds.+--+-- /Pre-release/+{-# INLINE getSlice #-}+getSlice+    :: Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSlice index len arr@MutArray{..} =+    if index >= 0 && len >= 0 && index + len <= arrLen+    then arr {arrStart = arrStart + index, arrLen = len}+    else error+             $ "getSlice: invalid slice, index "+             ++ show index ++ " length " ++ show len++-------------------------------------------------------------------------------+-- to Lists and streams+-------------------------------------------------------------------------------++-- XXX Maybe faster to create a list explicitly instead of mapM, if list fusion+-- does not work well.++-- | Convert an 'Array' into a list.+--+-- /Pre-release/+{-# INLINE toList #-}+toList :: MonadIO m => MutArray a -> m [a]+toList arr@MutArray{..} = mapM (`getIndexUnsafe` arr) [0 .. (arrLen - 1)]++-- | Generates a stream from the elements of a @MutArray@.+--+-- >>> read = Stream.unfold MutArray.reader+--+{-# INLINE_NORMAL read #-}+read :: MonadIO m => MutArray a -> D.Stream m a+read arr@MutArray{..} =+    D.mapM (`getIndexUnsafe` arr) $ D.enumerateFromToIntegral 0 (arrLen - 1)++-- Check equivalence with StreamK.fromStream . toStreamD and remove+{-# INLINE toStreamK #-}+toStreamK :: MonadIO m => MutArray a -> K.StreamK m a+toStreamK arr@MutArray{..} = K.unfoldrM step 0++    where++    step i+        | i == arrLen = return Nothing+        | otherwise = do+            x <- getIndexUnsafe i arr+            return $ Just (x, i + 1)++{-# INLINE_NORMAL readRev #-}+readRev :: MonadIO m => MutArray a -> D.Stream m a+readRev arr@MutArray{..} =+    D.mapM (`getIndexUnsafe` arr)+        $ D.enumerateFromThenToIntegral (arrLen - 1) (arrLen - 2) 0++-------------------------------------------------------------------------------+-- Folds+-------------------------------------------------------------------------------++-- XXX deduplicate this across unboxed array and this module?++-- | The default chunk size by which the array creation routines increase the+-- size of the array when the array is grown linearly.+arrayChunkSize :: Int+arrayChunkSize = 1024++-- | Like 'writeN' but does not check the array bounds when writing. The fold+-- driver must not call the step function more than 'n' times otherwise it will+-- corrupt the memory and crash. This function exists mainly because any+-- conditional in the step function blocks fusion causing 10x performance+-- slowdown.+--+-- /Pre-release/+{-# INLINE_NORMAL writeNUnsafe #-}+writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)+writeNUnsafe n = Fold step initial return return++    where++    initial = FL.Partial <$> new (max n 0)++    step arr x = FL.Partial <$> snocUnsafe arr x++-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an+-- 'Array'.+--+-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)+--+-- /Pre-release/+{-# INLINE_NORMAL writeN #-}+writeN :: MonadIO m => Int -> Fold m a (MutArray a)+writeN n = FL.take n $ writeNUnsafe n++-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.pinnedNew n)+-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)+-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)++-- | @writeWith minCount@ folds the whole input to a single array. The array+-- starts at a size big enough to hold minCount elements, the size is doubled+-- every time the array needs to be grown.+--+-- /Caution! Do not use this on infinite streams./+--+-- /Pre-release/+{-# INLINE_NORMAL writeWith #-}+writeWith :: MonadIO m => Int -> Fold m a (MutArray a)+-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (pinnedNew n)+writeWith elemCount = FL.rmapM extract $ FL.foldlM' step initial++    where++    initial = do+        when (elemCount < 0) $ error "writeWith: elemCount is negative"+        liftIO $ new elemCount++    step arr@(MutArray _ start end bound) x+        | end == bound = do+        let oldSize = end - start+            newSize = max (oldSize * 2) 1+        arr1 <- liftIO $ realloc newSize arr+        snocUnsafe arr1 x+    step arr x = snocUnsafe arr x++    -- extract = liftIO . rightSize+    extract = return++-- | Fold the whole input to a single array.+--+-- Same as 'writeWith' using an initial array size of 'arrayChunkSize' bytes+-- rounded up to the element size.+--+-- /Caution! Do not use this on infinite streams./+--+{-# INLINE write #-}+write :: MonadIO m => Fold m a (MutArray a)+write = writeWith arrayChunkSize++-- | Create a 'MutArray' from the first @n@ elements of a stream. The+-- array is allocated to size @n@, if the stream terminates before @n@+-- elements then the array may hold less than @n@ elements.+--+{-# INLINE fromStreamN #-}+fromStreamN :: MonadIO m => Int -> Stream m a -> m (MutArray a)+fromStreamN n = D.fold (writeN n)++{-# INLINE fromStream #-}+fromStream :: MonadIO m => Stream m a -> m (MutArray a)+fromStream = D.fold write++{-# INLINABLE fromListN #-}+fromListN :: MonadIO m => Int -> [a] -> m (MutArray a)+fromListN n xs = fromStreamN n $ D.fromList xs++{-# INLINABLE fromList #-}+fromList :: MonadIO m => [a] -> m (MutArray a)+fromList xs = fromStream $ D.fromList xs++{-# INLINABLE fromPureStream #-}+fromPureStream :: MonadIO m => Stream Identity a -> m (MutArray a)+fromPureStream xs =+    liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs++-------------------------------------------------------------------------------+-- Chunking+-------------------------------------------------------------------------------++data GroupState s a start end bound+    = GroupStart s+    | GroupBuffer s (MutableArray# RealWorld a) start end bound+    | GroupYield+          (MutableArray# RealWorld a)+          start+          end+          bound+          (GroupState s a start end bound)+    | GroupFinish++-- | @chunksOf n stream@ groups the input stream into a stream of+-- arrays of size n.+--+-- @chunksOf n = foldMany (MutArray.writeN n)@+--+-- /Pre-release/+{-# INLINE_NORMAL chunksOf #-}+chunksOf :: forall m a. MonadIO m+    => Int -> D.Stream m a -> D.Stream m (MutArray a)+-- XXX the idiomatic implementation leads to large regression in the D.reverse'+-- benchmark. It seems it has difficulty producing optimized code when+-- converting to StreamK. Investigate GHC optimizations.+-- chunksOf n = D.foldMany (writeN n)+chunksOf n (D.Stream step state) =+    D.Stream step' (GroupStart state)++    where++    -- start is always 0+    -- end and len are always equal++    {-# INLINE_LATE step' #-}+    step' _ (GroupStart st) = do+        when (n <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ "Streamly.Internal.Data.Array.Generic.Mut.Type.chunksOf: "+                    ++ "the size of arrays [" ++ show n+                    ++ "] must be a natural number"+        (MutArray contents start end bound :: MutArray a) <- new n+        return $ D.Skip (GroupBuffer st contents start end bound)++    step' gst (GroupBuffer st contents start end bound) = do+        r <- step (adaptState gst) st+        case r of+            D.Yield x s -> do+                putIndexUnsafeWith end contents x+                let end1 = end + 1+                return $+                    if end1 >= bound+                    then D.Skip+                            (GroupYield+                                contents start end1 bound (GroupStart s))+                    else D.Skip (GroupBuffer s contents start end1 bound)+            D.Skip s ->+                return $ D.Skip (GroupBuffer s contents start end bound)+            D.Stop ->+                return+                    $ D.Skip (GroupYield contents start end bound GroupFinish)++    step' _ (GroupYield contents start end bound next) =+         return $ D.Yield (MutArray contents start end bound) next++    step' _ GroupFinish = return D.Stop++-------------------------------------------------------------------------------+-- Unfolds+-------------------------------------------------------------------------------++-- | Resumable unfold of an array.+--+{-# INLINE_NORMAL producerWith #-}+producerWith :: Monad m => (forall b. IO b -> m b) -> Producer m (MutArray a) a+producerWith liftio = Producer step inject extract++    where++    {-# INLINE inject #-}+    inject arr = return (arr, 0)++    {-# INLINE extract #-}+    extract (arr, i) =+        return $ arr {arrStart = arrStart arr + i, arrLen = arrLen arr - i}++    {-# INLINE_LATE step #-}+    step (arr, i)+        | assert (arrLen arr >= 0) (i == arrLen arr) = return D.Stop+    step (arr, i) = do+        x <- liftio $ getIndexUnsafe i arr+        return $ D.Yield x (arr, i + 1)++-- | Resumable unfold of an array.+--+{-# INLINE_NORMAL producer #-}+producer :: MonadIO m => Producer m (MutArray a) a+producer = producerWith liftIO++-- | Unfold an array into a stream.+--+{-# INLINE_NORMAL reader #-}+reader :: MonadIO m => Unfold m (MutArray a) a+reader = Producer.simplify producer++--------------------------------------------------------------------------------+-- Appending arrays+--------------------------------------------------------------------------------++-- | Put a sub range of a source array into a subrange of a destination array.+-- This is not safe as it does not check the bounds.+{-# INLINE putSliceUnsafe #-}+putSliceUnsafe :: MonadIO m =>+    MutArray a -> Int -> MutArray a -> Int -> Int -> m ()+putSliceUnsafe src srcStart dst dstStart len = liftIO $ do+    assertM(len <= arrLen dst)+    assertM(len <= arrLen src)+    let !(I# srcStart#) = srcStart + arrStart src+        !(I# dstStart#) = dstStart + arrStart dst+        !(I# len#) = len+    let arrS# = arrContents# src+        arrD# = arrContents# dst+    IO $ \s# -> (# copyMutableArray#+                    arrS# srcStart# arrD# dstStart# len# s#+                , () #)++{-# INLINE clone #-}+clone :: MonadIO m => MutArray a -> m (MutArray a)+clone src = liftIO $ do+    let len = arrLen src+    dst <- new len+    putSliceUnsafe src 0 dst 0 len+    return dst++-------------------------------------------------------------------------------+-- Size+-------------------------------------------------------------------------------++{-# INLINE length #-}+length :: MutArray a -> Int+length = arrLen++-------------------------------------------------------------------------------+-- Equality+-------------------------------------------------------------------------------++-- | Compare the length of the arrays. If the length is equal, compare the+-- lexicographical ordering of two underlying byte arrays otherwise return the+-- result of length comparison.+--+-- /Pre-release/+{-# INLINE cmp #-}+cmp :: (MonadIO m, Ord a) => MutArray a -> MutArray a -> m Ordering+cmp a1 a2 =+    case compare lenA1 lenA2 of+        EQ -> loop (lenA1 - 1)+        x -> return x++    where++    lenA1 = length a1+    lenA2 = length a2++    loop i+        | i < 0 = return EQ+        | otherwise = do+            v1 <- getIndexUnsafe i a1+            v2 <- getIndexUnsafe i a2+            case compare v1 v2 of+                EQ -> loop (i - 1)+                x -> return x++{-# INLINE eq #-}+eq :: (MonadIO m, Eq a) => MutArray a -> MutArray a -> m Bool+eq a1 a2 =+    if lenA1 == lenA2+    then loop (lenA1 - 1)+    else return False++    where++    lenA1 = length a1+    lenA2 = length a2++    loop i+        | i < 0 = return True+        | otherwise = do+            v1 <- getIndexUnsafe i a1+            v2 <- getIndexUnsafe i a2+            if v1 == v2+            then loop (i - 1)+            else return False++{-# INLINE strip #-}+strip :: MonadIO m => (a -> Bool) -> MutArray a -> m (MutArray a)+strip p arr = liftIO $ do+    let lastIndex = length arr - 1+    indexR <- getIndexR lastIndex -- last predicate failing index+    if indexR < 0+    then nil+    else do+        indexL <- getIndexL 0 -- first predicate failing index+        if indexL == 0 && indexR == lastIndex+        then return arr+        else+           let newLen = indexR - indexL + 1+            in return $ getSliceUnsafe indexL newLen arr++    where++    getIndexR idx+        | idx < 0 = return idx+        | otherwise = do+            r <- getIndexUnsafe idx arr+            if p r+            then getIndexR (idx - 1)+            else return idx++    getIndexL idx = do+        r <- getIndexUnsafe idx arr+        if p r+        then getIndexL (idx + 1)+        else return idx
+ src/Streamly/Internal/Data/MutArray/Stream.hs view
@@ -0,0 +1,329 @@+-- |+-- Module      : Streamly.Internal.Data.MutArray.Stream+-- Copyright   : (c) 2019 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Combinators to efficiently manipulate streams of mutable arrays.+--+-- We can either push these in the MutArray module with a "chunks" prefix or+-- keep this as a separate module and release it.+--+module Streamly.Internal.Data.MutArray.Stream+    (+    -- * Generation+      MArray.chunksOf+    , MArray.pinnedChunksOf+    , MArray.writeChunks -- chunksWrite?+    , MArray.splitOn -- chunksSplitOn++    -- * Compaction+    , packArraysChunksOf+    , SpliceState (..)+    , lpackArraysChunksOf+    , compact -- chunksCompact+    , compactLE+    , compactEQ+    , compactGE++    -- * Elimination+    , MArray.flattenArrays -- chunksConcat+    , MArray.flattenArraysRev -- chunksConcatRev+    , MArray.fromArrayStreamK -- chunksCoalesce+    )+where++#include "inline.hs"+#include "ArrayMacros.h"++import Control.Monad.IO.Class (MonadIO(..))+import Control.Monad (when)+import Data.Bifunctor (first)+import Data.Proxy (Proxy(..))+import Streamly.Internal.Data.Unbox (Unbox, sizeOf)+import Streamly.Internal.Data.MutArray.Type (MutArray(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Parser (ParseError)+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))++import qualified Streamly.Internal.Data.MutArray as MArray+import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.Parser as ParserD+import qualified Streamly.Internal.Data.Stream as D++-------------------------------------------------------------------------------+-- Compact+-------------------------------------------------------------------------------++data SpliceState s arr+    = SpliceInitial s+    | SpliceBuffering s arr+    | SpliceYielding arr (SpliceState s arr)+    | SpliceFinish++-- XXX This can be removed once compactLEFold/compactLE are implemented.+--+-- | This mutates the first array (if it has space) to append values from the+-- second one. This would work for immutable arrays as well because an+-- immutable array never has space so a new array is allocated instead of+-- mutating it.+--+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- maximum specified size. Note that if a single array is bigger than the+-- specified size we do not split it to fit. When we coalesce multiple arrays+-- if the size would exceed the specified size we do not coalesce therefore the+-- actual array size may be less than the specified chunk size.+--+-- @since 0.7.0+{-# INLINE_NORMAL packArraysChunksOf #-}+packArraysChunksOf :: (MonadIO m, Unbox a)+    => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)+packArraysChunksOf n (D.Stream step state) =+    D.Stream step' (SpliceInitial state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (SpliceInitial st) = do+        when (n <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "+                 ++ "arrays [" ++ show n ++ "] must be a natural number"+        r <- step gst st+        case r of+            D.Yield arr s -> return $+                let len = MArray.byteLength arr+                 in if len >= n+                    then D.Skip (SpliceYielding arr (SpliceInitial s))+                    else D.Skip (SpliceBuffering s arr)+            D.Skip s -> return $ D.Skip (SpliceInitial s)+            D.Stop -> return D.Stop++    step' gst (SpliceBuffering st buf) = do+        r <- step gst st+        case r of+            D.Yield arr s -> do+                let len = MArray.byteLength buf + MArray.byteLength arr+                if len > n+                then return $+                    D.Skip (SpliceYielding buf (SpliceBuffering s arr))+                else do+                    buf' <- if MArray.byteCapacity buf < n+                            then liftIO $ MArray.realloc n buf+                            else return buf+                    buf'' <- MArray.splice buf' arr+                    return $ D.Skip (SpliceBuffering s buf'')+            D.Skip s -> return $ D.Skip (SpliceBuffering s buf)+            D.Stop -> return $ D.Skip (SpliceYielding buf SpliceFinish)++    step' _ SpliceFinish = return D.Stop++    step' _ (SpliceYielding arr next) = return $ D.Yield arr next++-- XXX Remove this once compactLEFold is implemented+-- lpackArraysChunksOf = Fold.many compactLEFold+--+{-# INLINE_NORMAL lpackArraysChunksOf #-}+lpackArraysChunksOf :: (MonadIO m, Unbox a)+    => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()+lpackArraysChunksOf n (Fold step1 initial1 _ final1) =+    Fold step initial extract final++    where++    initial = do+        when (n <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ "Streamly.Internal.Data.MutArray.Type.packArraysChunksOf: the size of "+                 ++ "arrays [" ++ show n ++ "] must be a natural number"++        r <- initial1+        return $ first (Tuple' Nothing) r++    step (Tuple' Nothing r1) arr =+            let len = MArray.byteLength arr+             in if len >= n+                then do+                    r <- step1 r1 arr+                    case r of+                        FL.Done _ -> return $ FL.Done ()+                        FL.Partial s -> do+                            _ <- final1 s+                            res <- initial1+                            return $ first (Tuple' Nothing) res+                else return $ FL.Partial $ Tuple' (Just arr) r1++    step (Tuple' (Just buf) r1) arr = do+            let len = MArray.byteLength buf + MArray.byteLength arr+            buf' <- if MArray.byteCapacity buf < len+                    then liftIO $ MArray.realloc (max n len) buf+                    else return buf+            buf'' <- MArray.splice buf' arr++            -- XXX this is common in both the equations of step+            if len >= n+            then do+                r <- step1 r1 buf''+                case r of+                    FL.Done _ -> return $ FL.Done ()+                    FL.Partial s -> do+                        _ <- final1 s+                        res <- initial1+                        return $ first (Tuple' Nothing) res+            else return $ FL.Partial $ Tuple' (Just buf'') r1++    -- XXX Several folds do extract >=> final, therefore, we need to make final+    -- return  "m b" rather than using extract post it if we want extract to be+    -- partial.+    --+    -- extract forces the pending buffer to be sent to the fold which is not+    -- what we want.+    extract _ = error "lpackArraysChunksOf: not designed for scanning"++    final (Tuple' Nothing r1) = final1 r1+    final (Tuple' (Just buf) r1) = do+        r <- step1 r1 buf+        case r of+            FL.Partial rr -> final1 rr+            FL.Done _ -> return ()++-- XXX Same as compactLE, to be removed once that is implemented.+--+-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- maximum specified size in bytes.+--+-- /Internal/+{-# INLINE compact #-}+compact :: (MonadIO m, Unbox a)+    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+compact = packArraysChunksOf++-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- maximum specified size. Note that if a single array is bigger than the+-- specified size we do not split it to fit. When we coalesce multiple arrays+-- if the size would exceed the specified size we do not coalesce therefore the+-- actual array size may be less than the specified chunk size.+--+-- /Internal/+{-# INLINE_NORMAL compactLEParserD #-}+compactLEParserD ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> ParserD.Parser (MutArray a) m (MutArray a)+compactLEParserD n = ParserD.Parser step initial extract++    where++    nBytes = n * SIZE_OF(a)++    initial =+        return+            $ if n <= 0+              then error+                       $ functionPath+                       ++ ": the size of arrays ["+                       ++ show n ++ "] must be a natural number"+              else ParserD.IPartial Nothing++    step Nothing arr =+        return+            $ let len = MArray.byteLength arr+               in if len >= nBytes+                  then ParserD.Done 0 arr+                  else ParserD.Partial 0 (Just arr)+    step (Just buf) arr =+        let len = MArray.byteLength buf + MArray.byteLength arr+         in if len > nBytes+            then return $ ParserD.Done 1 buf+            else do+                buf1 <-+                    if MArray.byteCapacity buf < nBytes+                    then liftIO $ MArray.realloc nBytes buf+                    else return buf+                buf2 <- MArray.splice buf1 arr+                return $ ParserD.Partial 0 (Just buf2)++    extract Nothing = return $ ParserD.Done 0 MArray.nil+    extract (Just buf) = return $ ParserD.Done 0 buf++    functionPath =+        "Streamly.Internal.Data.Stream.MutChunked.compactLEParserD"++-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- minimum specified size. Note that if all the arrays in the stream together+-- are smaller than the specified size the resulting array will be smaller than+-- the specified size. When we coalesce multiple arrays if the size would exceed+-- the specified size we stop coalescing further.+--+-- /Internal/+{-# INLINE_NORMAL compactGEFold #-}+compactGEFold ::+       forall m a. (MonadIO m, Unbox a)+    => Int -> FL.Fold m (MutArray a) (MutArray a)+compactGEFold n = Fold step initial extract extract++    where++    nBytes = n * SIZE_OF(a)++    initial =+        return+            $ if n < 0+              then error+                       $ functionPath+                       ++ ": the size of arrays ["+                       ++ show n ++ "] must be a natural number"+              else FL.Partial Nothing++    step Nothing arr =+        return+            $ let len = MArray.byteLength arr+               in if len >= nBytes+                  then FL.Done arr+                  else FL.Partial (Just arr)+    step (Just buf) arr = do+        let len = MArray.byteLength buf + MArray.byteLength arr+        buf1 <-+            if MArray.byteCapacity buf < len+            then liftIO $ MArray.realloc (max len nBytes) buf+            else return buf+        buf2 <- MArray.splice buf1 arr+        if len >= n+        then return $ FL.Done buf2+        else return $ FL.Partial (Just buf2)++    extract Nothing = return MArray.nil+    extract (Just buf) = return buf++    functionPath =+        "Streamly.Internal.Data.Stream.MutChunked.compactGEFold"++-- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a+-- maximum specified size in bytes.+--+-- /Internal/+compactLE :: (MonadIO m, Unbox a) =>+    Int -> Stream m (MutArray a) -> Stream m (Either ParseError (MutArray a))+compactLE n = D.parseManyD (compactLEParserD n)++-- | Like 'compactLE' but generates arrays of exactly equal to the size+-- specified except for the last array in the stream which could be shorter.+--+-- /Unimplemented/+{-# INLINE compactEQ #-}+compactEQ :: -- (MonadIO m, Unbox a) =>+    Int -> Stream m (MutArray a) -> Stream m (MutArray a)+compactEQ _n _xs = undefined+    -- IsStream.fromStreamD $ D.foldMany (compactEQFold n) (IsStream.toStreamD xs)++-- | Like 'compactLE' but generates arrays of size greater than or equal to the+-- specified except for the last array in the stream which could be shorter.+--+-- /Internal/+{-# INLINE compactGE #-}+compactGE ::+       (MonadIO m, Unbox a)+    => Int -> Stream m (MutArray a) -> Stream m (MutArray a)+compactGE n = D.foldMany (compactGEFold n)
+ src/Streamly/Internal/Data/MutArray/Type.hs view
@@ -0,0 +1,2479 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UnboxedTuples #-}+-- |+-- Module      : Streamly.Internal.Data.MutArray.Type+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Pinned and unpinned mutable array for 'Unboxed' types. Fulfils the following+-- goals:+--+-- * Random access (array)+-- * Efficient storage (unboxed)+-- * Performance (unboxed access)+-- * Performance - in-place operations (mutable)+-- * Performance - GC (pinned, mutable)+-- * interfacing with OS (pinned)+--+-- Stream and Fold APIs allow easy, efficient and convenient operations on+-- arrays.+--+-- Mutable arrays and file system files are quite similar, they can grow and+-- their content is mutable. Therefore, both have similar APIs as well. We+-- strive to keep the API consistent for both. Ideally, you should be able to+-- replace one with another with little changes to the code.++module Streamly.Internal.Data.MutArray.Type+    (+    -- ** Type+    -- $arrayNotes+      MutArray (..)+    , MutByteArray+    , MutableByteArray+    , pin+    , unpin+    , isPinned++    -- -- * Constructing and Writing+    -- ** Construction+    , nil++    -- *** Uninitialized Arrays+    , pinnedNew+    , pinnedNewBytes+    , pinnedNewAligned+    , new+    , newArrayWith++    -- *** From streams+    , ArrayUnsafe (..)+    , writeNWithUnsafe+    , writeNWith+    , writeNUnsafe+    , pinnedWriteNUnsafe+    , writeN+    , pinnedWriteN+    , pinnedWriteNAligned++    , writeWith+    , write+    , pinnedWrite++    , writeRevN+    -- , writeRev++    -- *** From containers+    , fromListN+    , pinnedFromListN+    , fromList+    , pinnedFromList+    , fromListRevN+    , fromListRev+    , fromStreamDN+    , fromStreamD+    , fromPureStream++    -- ** Random writes+    , putIndex+    , putIndexUnsafe+    , putIndices+    -- , putFromThenTo+    -- , putFrom -- start writing at the given position+    -- , putUpto -- write from beginning up to the given position+    -- , putFromTo+    -- , putFromRev+    -- , putUptoRev+    , modifyIndexUnsafe+    , modifyIndex+    , modifyIndices+    , modify+    , swapIndices+    , unsafeSwapIndices++    -- ** Growing and Shrinking+    -- | Arrays grow only at the end, though it is possible to grow on both sides+    -- and therefore have a cons as well as snoc. But that will require two+    -- bounds in the array representation.++    -- *** Appending elements+    , snocWith+    , snoc+    , snocLinear+    , snocMay+    , snocUnsafe++    -- *** Appending streams+    , writeAppendNUnsafe+    , writeAppendN+    , writeAppendWith+    , writeAppend++    -- ** Eliminating and Reading++    -- *** To streams+    , reader+    , readerRevWith+    , readerRev++    -- *** To containers+    , toStreamDWith+    , toStreamDRevWith+    , toStreamKWith+    , toStreamKRevWith+    , read+    , readRev+    , toStreamK+    , toStreamKRev+    , toList++    -- experimental+    , producerWith+    , producer++    -- *** Random reads+    , getIndex+    , getIndexUnsafe+    , getIndices+    , getIndicesD+    -- , getFromThenTo+    , getIndexRev++    -- ** Memory Management+    , blockSize+    , arrayChunkBytes+    , allocBytesToElemCount+    , realloc+    , resize+    , resizeExp+    , rightSize++    -- ** Size+    , length+    , byteLength+    -- , capacity+    , byteCapacity+    , bytesFree++    -- ** In-place Mutation Algorithms+    , strip+    , reverse+    , permute+    , partitionBy+    , shuffleBy+    , divideBy+    , mergeBy+    , bubble++    -- ** Casting+    , cast+    , castUnsafe+    , asBytes+    , asPtrUnsafe++    -- ** Folding+    , foldl'+    , foldr+    , cmp++    -- ** Arrays of arrays+    --  We can add dimensionality parameter to the array type to get+    --  multidimensional arrays. Multidimensional arrays would just be a+    --  convenience wrapper on top of single dimensional arrays.++    -- | Operations dealing with multiple arrays, streams of arrays or+    -- multidimensional array representations.++    -- *** Construct from streams+    , chunksOf+    , pinnedChunksOf+    , writeChunks++    -- *** Eliminate to streams+    , flattenArrays+    , flattenArraysRev+    , fromArrayStreamK++    -- *** Construct from arrays+    -- get chunks without copying+    , getSliceUnsafe+    , getSlice+    -- , getSlicesFromLenN+    , splitAt -- XXX should be able to express using getSlice+    , breakOn++    -- ** Cloning arrays+    , clone+    , pinnedClone++    -- ** Appending arrays+    , spliceCopy+    , spliceWith+    , splice+    , spliceExp+    , spliceUnsafe+    -- , putSlice+    -- , appendSlice+    -- , appendSliceFrom++    -- ** Utilities+    , roundUpToPower2+    , memcpy+    , memcmp+    , c_memchr+    )+where++#include "assert.hs"+#include "inline.hs"+#include "ArrayMacros.h"+#include "MachDeps.h"++import Control.Monad (when, void)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Bits (shiftR, (.|.), (.&.))+import Data.Functor.Identity (Identity(..))+import Data.Proxy (Proxy(..))+import Data.Word (Word8)+import Foreign.C.Types (CSize(..), CInt(..))+import Foreign.Ptr (plusPtr, minusPtr, nullPtr)+import Streamly.Internal.Data.MutByteArray.Type+    ( MutByteArray(..)+    , MutableByteArray+    , PinnedState(..)+    , getMutableByteArray#+    , putSliceUnsafe+    )+import Streamly.Internal.Data.Unbox (Unbox(..))+import GHC.Base+    ( IO(..)+    , Int(..)+    , compareByteArrays#+    , copyMutableByteArray#+    )+import GHC.Base (noinline)+import GHC.Exts (unsafeCoerce#)+import GHC.Ptr (Ptr(..))++import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Producer.Type (Producer (..))+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.StreamK.Type (StreamK)+import Streamly.Internal.Data.SVar.Type (adaptState, defState)+import Streamly.Internal.Data.Unfold.Type (Unfold(..))+import Streamly.Internal.System.IO (arrayPayloadSize, defaultChunkSize)++import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.MutByteArray.Type as Unboxed+import qualified Streamly.Internal.Data.Producer as Producer+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Lift as D+import qualified Streamly.Internal.Data.StreamK.Type as K+import qualified Prelude++import Prelude hiding+    (Foldable(..), read, unlines, splitAt, reverse, truncate)++#include "DocTestDataMutArray.hs"++-------------------------------------------------------------------------------+-- Foreign helpers+-------------------------------------------------------------------------------++foreign import ccall unsafe "string.h memcpy" c_memcpy+    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO (Ptr Word8)++foreign import ccall unsafe "string.h memchr" c_memchr+    :: Ptr Word8 -> Word8 -> CSize -> IO (Ptr Word8)++foreign import ccall unsafe "string.h memcmp" c_memcmp+    :: Ptr Word8 -> Ptr Word8 -> CSize -> IO CInt++-- | Given an 'Unboxed' type (unused first arg) and a number of bytes, return+-- how many elements of that type will completely fit in those bytes.+--+{-# INLINE bytesToElemCount #-}+bytesToElemCount :: forall a. Unbox a => a -> Int -> Int+bytesToElemCount _ n = n `div` SIZE_OF(a)++-- XXX we are converting Int to CSize+memcpy :: Ptr Word8 -> Ptr Word8 -> Int -> IO ()+memcpy dst src len = void (c_memcpy dst src (fromIntegral len))++-- XXX we are converting Int to CSize+-- return True if the memory locations have identical contents+{-# INLINE memcmp #-}+memcmp :: Ptr Word8 -> Ptr Word8 -> Int -> IO Bool+memcmp p1 p2 len = do+    r <- c_memcmp p1 p2 (fromIntegral len)+    return $ r == 0++-------------------------------------------------------------------------------+-- MutArray Data Type+-------------------------------------------------------------------------------++-- $arrayNotes+--+-- We can use an 'Unboxed' constraint in the MutArray type and the constraint+-- can be automatically provided to a function that pattern matches on the+-- MutArray type. However, it has huge performance cost, so we do not use it.+-- Investigate a GHC improvement possiblity.++-- | An unboxed mutable array. An array is created with a given length+-- and capacity. Length is the number of valid elements in the array.  Capacity+-- is the maximum number of elements that the array can be expanded to without+-- having to reallocate the memory.+--+-- The elements in the array can be mutated in-place without changing the+-- reference (constructor). However, the length of the array cannot be mutated+-- in-place.  A new array reference is generated when the length changes.  When+-- the length is increased (upto the maximum reserved capacity of the array),+-- the array is not reallocated and the new reference uses the same underlying+-- memory as the old one.+--+-- Several routines in this module allow the programmer to control the capacity+-- of the array. The programmer can control the trade-off between memory usage+-- and performance impact due to reallocations when growing or shrinking the+-- array.+--+data MutArray a =+#ifdef DEVBUILD+    Unbox a =>+#endif+    -- The array is a range into arrContents. arrContents may be a superset of+    -- the slice represented by the array. All offsets are in bytes.+    MutArray+    { arrContents :: {-# UNPACK #-} !MutByteArray+    , arrStart :: {-# UNPACK #-} !Int  -- ^ index into arrContents+    , arrEnd   :: {-# UNPACK #-} !Int    -- ^ index into arrContents+                                       -- Represents the first invalid index of+                                       -- the array.+    , arrBound :: {-# UNPACK #-} !Int    -- ^ first invalid index of arrContents.+    }++-------------------------------------------------------------------------------+-- Pinning & Unpinning+-------------------------------------------------------------------------------++-- | Return a copy of the array in pinned memory if unpinned, else return the+-- original array.+{-# INLINE pin #-}+pin :: MutArray a -> IO (MutArray a)+pin arr@MutArray{..} =+    if Unboxed.isPinned arrContents+    then pure arr+    else pinnedClone arr++-- | Return a copy of the array in unpinned memory if pinned, else return the+-- original array.+{-# INLINE unpin #-}+unpin :: MutArray a -> IO (MutArray a)+unpin arr@MutArray{..} =+    if Unboxed.isPinned arrContents+    then clone arr+    else pure arr++-- | Return 'True' if the array is allocated in pinned memory.+{-# INLINE isPinned #-}+isPinned :: MutArray a -> Bool+isPinned MutArray{..} = Unboxed.isPinned arrContents++-------------------------------------------------------------------------------+-- Construction+-------------------------------------------------------------------------------++-- XXX Change the names to use "new" instead of "newArray". That way we can use+-- the same names for managed file system objects as well. For unmanaged ones+-- we can use open/create etc as usual.+--+-- A new array is similar to "touch" creating a zero length file. An mmapped+-- array would be similar to a sparse file with holes. TBD: support mmapped+-- files and arrays.++-- GHC always guarantees word-aligned memory, alignment is important only when+-- we need more than that.  See stg_pinnedNewAlignedByteArrayzh and+-- allocatePinned in GHC source.++-- | @newArrayWith allocator alignment count@ allocates a new array of zero+-- length and with a capacity to hold @count@ elements, using @allocator+-- size alignment@ as the memory allocator function.+--+-- Alignment must be greater than or equal to machine word size and a power of+-- 2.+--+-- Alignment is ignored if the allocator allocates unpinned memory.+--+-- /Pre-release/+{-# INLINE newArrayWith #-}+newArrayWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> Int -> m MutByteArray) -> Int -> Int -> m (MutArray a)+newArrayWith alloc alignSize count = do+    let size = max (count * SIZE_OF(a)) 0+    contents <- alloc size alignSize+    return $ MutArray+        { arrContents = contents+        , arrStart = 0+        , arrEnd   = 0+        , arrBound = size+        }++nil ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    MutArray a+nil = MutArray Unboxed.nil 0 0 0++{-# INLINE newBytesAs #-}+newBytesAs :: MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    PinnedState -> Int -> m (MutArray a)+newBytesAs ps bytes = do+    contents <- liftIO $ Unboxed.newBytesAs ps bytes+    return $ MutArray+        { arrContents = contents+        , arrStart = 0+        , arrEnd   = 0+        , arrBound = bytes+        }++-- | Allocates a pinned empty array that can hold 'count' items.  The memory of+-- the array is uninitialized and the allocation is aligned as per the+-- 'Unboxed' instance of the type.+--+-- /Pre-release/+{-# INLINE pinnedNewBytes #-}+pinnedNewBytes :: MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    Int -> m (MutArray a)+pinnedNewBytes = newBytesAs Pinned++-- | Like 'newArrayWith' but using an allocator is a pinned memory allocator and+-- the alignment is dictated by the 'Unboxed' instance of the type.+--+-- /Internal/+{-# INLINE pinnedNewAligned #-}+pinnedNewAligned :: (MonadIO m, Unbox a) => Int -> Int -> m (MutArray a)+pinnedNewAligned =+    newArrayWith (\s a -> liftIO $ Unboxed.pinnedNewAlignedBytes s a)++{-# INLINE newAs #-}+newAs :: (MonadIO m, Unbox a) => PinnedState -> Int -> m (MutArray a)+newAs ps =+    newArrayWith+        (\s _ -> liftIO $ Unboxed.newBytesAs ps s)+        (error "new: alignment is not used in unpinned arrays.")++-- XXX can unaligned allocation be more efficient when alignment is not needed?+--+-- | Allocates an empty pinned array that can hold 'count' items.  The memory of+-- the array is uninitialized and the allocation is aligned as per the 'Unboxed'+-- instance of the type.+--+{-# INLINE pinnedNew #-}+pinnedNew :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)+pinnedNew = newAs Pinned++-- | Allocates an empty unpinned array that can hold 'count' items.  The memory+-- of the array is uninitialized.+--+{-# INLINE new #-}+new :: (MonadIO m, Unbox a) => Int -> m (MutArray a)+new = newAs Unpinned++-------------------------------------------------------------------------------+-- Random writes+-------------------------------------------------------------------------------++-- | Write the given element to the given index of the array. Does not check if+-- the index is out of bounds of the array.+--+-- /Pre-release/+{-# INLINE putIndexUnsafe #-}+putIndexUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> MutArray a -> a -> m ()+putIndexUnsafe i MutArray{..} x = do+    let index = INDEX_OF(arrStart, i, a)+    assert (i >= 0 && INDEX_VALID(index, arrEnd, a)) (return ())+    liftIO $ pokeAt index arrContents  x++invalidIndex :: String -> Int -> a+invalidIndex label i =+    error $ label ++ ": invalid array index " ++ show i++-- | /O(1)/ Write the given element at the given index in the array.+-- Performs in-place mutation of the array.+--+-- >>> putIndex ix arr val = MutArray.modifyIndex ix arr (const (val, ()))+-- >>> f = MutArray.putIndices+-- >>> putIndex ix arr val = Stream.fold (f arr) (Stream.fromPure (ix, val))+--+{-# INLINE putIndex #-}+putIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m ()+putIndex i MutArray{..} x = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ pokeAt index arrContents  x+    else invalidIndex "putIndex" i++-- | Write an input stream of (index, value) pairs to an array. Throws an+-- error if any index is out of bounds.+--+-- /Pre-release/+{-# INLINE putIndices #-}+putIndices :: forall m a. (MonadIO m, Unbox a)+    => MutArray a -> Fold m (Int, a) ()+putIndices arr = FL.foldlM' step (return ())++    where++    step () (i, x) = liftIO (putIndex i arr x)++-- | Modify a given index of an array using a modifier function.+--+-- Unsafe because it does not check the bounds of the array.+--+-- /Pre-release/+modifyIndexUnsafe :: forall m a b. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndexUnsafe i MutArray{..} f = liftIO $ do+        let index = INDEX_OF(arrStart,i,a)+        assert (i >= 0 && INDEX_NEXT(index,a) <= arrEnd) (return ())+        r <- peekAt index arrContents+        let (x, res) = f r+        pokeAt index arrContents  x+        return res++-- | Modify a given index of an array using a modifier function.+--+-- /Pre-release/+modifyIndex :: forall m a b. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> (a -> (a, b)) -> m b+modifyIndex i MutArray{..} f = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ do+        r <- peekAt index arrContents+        let (x, res) = f r+        pokeAt index arrContents  x+        return res+    else invalidIndex "modifyIndex" i++-- | Modify the array indices generated by the supplied stream.+--+-- /Pre-release/+{-# INLINE modifyIndices #-}+modifyIndices :: forall m a . (MonadIO m, Unbox a)+    => MutArray a -> (Int -> a -> a) -> Fold m Int ()+modifyIndices arr f = FL.foldlM' step initial++    where++    initial = return ()++    step () i =+        let f1 x = (f i x, ())+         in modifyIndex i arr f1++-- | Modify each element of an array using the supplied modifier function.+--+-- This is an in-place equivalent of an immutable map operation.+--+-- /Pre-release/+modify :: forall m a. (MonadIO m, Unbox a)+    => MutArray a -> (a -> a) -> m ()+modify MutArray{..} f = liftIO $+    go arrStart++    where++    go i =+        when (INDEX_VALID(i,arrEnd,a)) $ do+            r <- peekAt i arrContents+            pokeAt i arrContents (f r)+            go (INDEX_NEXT(i,a))++-- XXX We could specify the number of bytes to swap instead of Proxy. Need+-- to ensure that the memory does not overlap.+{-# INLINE swapArrayByteIndices #-}+swapArrayByteIndices ::+       forall a. Unbox a+    => Proxy a+    -> MutByteArray+    -> Int+    -> Int+    -> IO ()+swapArrayByteIndices _ arrContents i1 i2 = do+    r1 <- peekAt i1 arrContents+    r2 <- peekAt i2 arrContents+    pokeAt i1 arrContents (r2 :: a)+    pokeAt i2 arrContents (r1 :: a)++-- | Swap the elements at two indices without validating the indices.+--+-- /Unsafe/: This could result in memory corruption if indices are not valid.+--+-- /Pre-release/+{-# INLINE unsafeSwapIndices #-}+unsafeSwapIndices :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> MutArray a -> m ()+unsafeSwapIndices i1 i2 MutArray{..} = liftIO $ do+        let t1 = INDEX_OF(arrStart,i1,a)+            t2 = INDEX_OF(arrStart,i2,a)+        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2++-- | Swap the elements at two indices.+--+-- /Pre-release/+swapIndices :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> MutArray a -> m ()+swapIndices i1 i2 MutArray{..} = liftIO $ do+        let t1 = INDEX_OF(arrStart,i1,a)+            t2 = INDEX_OF(arrStart,i2,a)+        when (i1 < 0 || INDEX_INVALID(t1,arrEnd,a))+            $ invalidIndex "swapIndices" i1+        when (i2 < 0 || INDEX_INVALID(t2,arrEnd,a))+            $ invalidIndex "swapIndices" i2+        swapArrayByteIndices (Proxy :: Proxy a) arrContents t1 t2++-------------------------------------------------------------------------------+-- Rounding+-------------------------------------------------------------------------------++-- XXX Should we use bitshifts in calculations or it gets optimized by the+-- compiler/processor itself?+--+-- | The page or block size used by the GHC allocator. Allocator allocates at+-- least a block and then allocates smaller allocations from within a block.+blockSize :: Int+blockSize = 4 * 1024++-- | Allocations larger than 'largeObjectThreshold' are in multiples of block+-- size and are always pinned. The space beyond the end of a large object up to+-- the end of the block is unused.+largeObjectThreshold :: Int+largeObjectThreshold = (blockSize * 8) `div` 10++-- XXX Should be done only when we are using the GHC allocator.+-- | Round up an array larger than 'largeObjectThreshold' to use the whole+-- block.+{-# INLINE roundUpLargeArray #-}+roundUpLargeArray :: Int -> Int+roundUpLargeArray size =+    if size >= largeObjectThreshold+    then+        assert+            (blockSize /= 0 && ((blockSize .&. (blockSize - 1)) == 0))+            ((size + blockSize - 1) .&. negate blockSize)+    else size++{-# INLINE isPower2 #-}+isPower2 :: Int -> Bool+isPower2 n = n .&. (n - 1) == 0++{-# INLINE roundUpToPower2 #-}+roundUpToPower2 :: Int -> Int+roundUpToPower2 n =+#if WORD_SIZE_IN_BITS == 64+    1 + z6+#else+    1 + z5+#endif++    where++    z0 = n - 1+    z1 = z0 .|. z0 `shiftR` 1+    z2 = z1 .|. z1 `shiftR` 2+    z3 = z2 .|. z2 `shiftR` 4+    z4 = z3 .|. z3 `shiftR` 8+    z5 = z4 .|. z4 `shiftR` 16+    z6 = z5 .|. z5 `shiftR` 32++-- | @allocBytesToBytes elem allocatedBytes@ returns the array size in bytes+-- such that the real allocation is less than or equal to @allocatedBytes@,+-- unless @allocatedBytes@ is less than the size of one array element in which+-- case it returns one element's size.+--+{-# INLINE allocBytesToBytes #-}+allocBytesToBytes :: forall a. Unbox a => a -> Int -> Int+allocBytesToBytes _ n = max (arrayPayloadSize n) (SIZE_OF(a))++-- | Given an 'Unboxed' type (unused first arg) and real allocation size+-- (including overhead), return how many elements of that type will completely+-- fit in it, returns at least 1.+--+{-# INLINE allocBytesToElemCount #-}+allocBytesToElemCount :: Unbox a => a -> Int -> Int+allocBytesToElemCount x bytes =+    let n = bytesToElemCount x (allocBytesToBytes x bytes)+     in assert (n >= 1) n++-- | The default chunk size by which the array creation routines increase the+-- size of the array when the array is grown linearly.+arrayChunkBytes :: Int+arrayChunkBytes = 1024++-------------------------------------------------------------------------------+-- Resizing+-------------------------------------------------------------------------------++-- | Round the second argument down to multiples of the first argument.+{-# INLINE roundDownTo #-}+roundDownTo :: Int -> Int -> Int+roundDownTo elemSize size = size - (size `mod` elemSize)++-- XXX See if resizing can be implemented by reading the old array as a stream+-- and then using writeN to the new array.+--+-- NOTE: we are passing elemSize explicitly to avoid an Unboxed constraint.+-- Since this is not inlined Unboxed consrraint leads to dictionary passing+-- which complicates some inspection tests.+--+{-# NOINLINE reallocExplicit #-}+reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)+reallocExplicit elemSize newCapacityInBytes MutArray{..} = do+    assertM(arrEnd <= arrBound)++    -- Allocate new array+    let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes+    contents <-+        if Unboxed.isPinned arrContents+        then Unboxed.pinnedNew newCapMaxInBytes+        else Unboxed.new newCapMaxInBytes+    let !(MutByteArray mbarrFrom#) = arrContents+        !(MutByteArray mbarrTo#) = contents++    -- Copy old data+    let oldStart = arrStart+        !(I# oldStartInBytes#) = oldStart+        oldSizeInBytes = arrEnd - oldStart+        newCapInBytes = roundDownTo elemSize newCapMaxInBytes+        !newLenInBytes@(I# newLenInBytes#) = min oldSizeInBytes newCapInBytes+    assert (oldSizeInBytes `mod` elemSize == 0) (return ())+    assert (newLenInBytes >= 0) (return ())+    assert (newLenInBytes `mod` elemSize == 0) (return ())+    IO $ \s# -> (# copyMutableByteArray# mbarrFrom# oldStartInBytes#+                        mbarrTo# 0# newLenInBytes# s#, () #)++    return $ MutArray+        { arrStart = 0+        , arrContents = contents+        , arrEnd   = newLenInBytes+        , arrBound = newCapInBytes+        }++-- | @realloc newCapacity array@ reallocates the array to the specified+-- capacity in bytes.+--+-- If the new size is less than the original array the array gets truncated.+-- If the new size is not a multiple of array element size then it is rounded+-- down to multiples of array size.  If the new size is more than+-- 'largeObjectThreshold' then it is rounded up to the block size (4K).+--+-- If the original array is pinned, the newly allocated array is also pinned.+{-# INLINABLE realloc #-}+realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)+realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr++-- | @reallocWith label capSizer minIncrBytes array@. The label is used+-- in error messages and the capSizer is used to determine the capacity of the+-- new array in bytes given the current byte length of the array.+reallocWith :: forall m a. (MonadIO m , Unbox a) =>+       String+    -> (Int -> Int)+    -> Int+    -> MutArray a+    -> m (MutArray a)+reallocWith label capSizer minIncrBytes arr = do+    let oldSizeBytes = arrEnd arr - arrStart arr+        newCapBytes = capSizer oldSizeBytes+        newSizeBytes = oldSizeBytes + minIncrBytes+        safeCapBytes = max newCapBytes newSizeBytes+    assertM(safeCapBytes >= newSizeBytes || error (badSize newSizeBytes))++    realloc safeCapBytes arr++    where++    badSize newSize =+        concat+            [ label+            , ": new array size (in bytes) is less than required size "+            , show newSize+            , ". Please check the sizing function passed."+            ]++-- | @resize newCapacity array@ changes the total capacity of the array so that+-- it is enough to hold the specified number of elements.  Nothing is done if+-- the specified capacity is less than the length of the array.+--+-- If the capacity is more than 'largeObjectThreshold' then it is rounded up to+-- the block size (4K).+--+-- /Pre-release/+{-# INLINE resize #-}+resize :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+resize nElems arr@MutArray{..} = do+    let req = SIZE_OF(a) * nElems+        len = arrEnd - arrStart+    if req < len+    then return arr+    else realloc req arr++-- | Like 'resize' but if the byte capacity is more than 'largeObjectThreshold'+-- then it is rounded up to the closest power of 2.+--+-- /Pre-release/+{-# INLINE resizeExp #-}+resizeExp :: forall m a. (MonadIO m, Unbox a) =>+    Int -> MutArray a -> m (MutArray a)+resizeExp nElems arr@MutArray{..} = do+    let req = roundUpLargeArray (SIZE_OF(a) * nElems)+        req1 =+            if req > largeObjectThreshold+            then roundUpToPower2 req+            else req+        len = arrEnd - arrStart+    if req1 < len+    then return arr+    else realloc req1 arr++-- | Resize the allocated memory to drop any reserved free space at the end of+-- the array and reallocate it to reduce wastage.+--+-- Up to 25% wastage is allowed to avoid reallocations.  If the capacity is+-- more than 'largeObjectThreshold' then free space up to the 'blockSize' is+-- retained.+--+-- /Pre-release/+{-# INLINE rightSize #-}+rightSize :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m (MutArray a)+rightSize arr@MutArray{..} = do+    assert (arrEnd <= arrBound) (return ())+    let start = arrStart+        len = arrEnd - start+        capacity = arrBound - start+        target = roundUpLargeArray len+        waste = arrBound - arrEnd+    assert (target >= len) (return ())+    assert (len `mod` SIZE_OF(a) == 0) (return ())+    -- We trade off some wastage (25%) to avoid reallocations and copying.+    if target < capacity && len < 3 * waste+    then realloc target arr+    else return arr++-------------------------------------------------------------------------------+-- Snoc+-------------------------------------------------------------------------------++-- XXX We can possibly use a smallMutableByteArray to hold the start, end,+-- bound pointers.  Using fully mutable handle will ensure that we do not have+-- multiple references to the same array of different lengths lying around and+-- potentially misused. In that case "snoc" need not return a new array (snoc+-- :: MutArray a -> a -> m ()), it will just modify the old reference.  The array+-- length will be mutable.  This means the length function would also be+-- monadic.  Mutable arrays would behave more like files that grow in that+-- case.++-- | Snoc using a 'Ptr'. Low level reusable function.+--+-- /Internal/+{-# INLINE snocNewEnd #-}+snocNewEnd :: (MonadIO m, Unbox a) => Int -> MutArray a -> a -> m (MutArray a)+snocNewEnd newEnd arr@MutArray{..} x = liftIO $ do+    assert (newEnd <= arrBound) (return ())+    pokeAt arrEnd arrContents x+    return $ arr {arrEnd = newEnd}++-- | Really really unsafe, appends the element into the first array, may+-- cause silent data corruption or if you are lucky a segfault if the first+-- array does not have enough space to append the element.+--+-- /Internal/+{-# INLINE snocUnsafe #-}+snocUnsafe :: forall m a. (MonadIO m, Unbox a) =>+    MutArray a -> a -> m (MutArray a)+snocUnsafe arr@MutArray{..} = snocNewEnd (INDEX_NEXT(arrEnd,a)) arr++-- | Like 'snoc' but does not reallocate when pre-allocated array capacity+-- becomes full.+--+-- /Internal/+{-# INLINE snocMay #-}+snocMay :: forall m a. (MonadIO m, Unbox a) =>+    MutArray a -> a -> m (Maybe (MutArray a))+snocMay arr@MutArray{..} x = liftIO $ do+    let newEnd = INDEX_NEXT(arrEnd,a)+    if newEnd <= arrBound+    then Just <$> snocNewEnd newEnd arr x+    else return Nothing++-- NOINLINE to move it out of the way and not pollute the instruction cache.+{-# NOINLINE snocWithRealloc #-}+snocWithRealloc :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray a+    -> a+    -> m (MutArray a)+snocWithRealloc sizer arr x = do+    arr1 <- liftIO $ reallocWith "snocWith" sizer (SIZE_OF(a)) arr+    snocUnsafe arr1 x++-- | @snocWith sizer arr elem@ mutates @arr@ to append @elem@. The length of+-- the array increases by 1.+--+-- If there is no reserved space available in @arr@ it is reallocated to a size+-- in bytes determined by the @sizer oldSizeBytes@ function, where+-- @oldSizeBytes@ is the original size of the array in bytes.+--+-- If the new array size is more than 'largeObjectThreshold' we automatically+-- round it up to 'blockSize'.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- /Pre-release/+{-# INLINE snocWith #-}+snocWith :: forall m a. (MonadIO m, Unbox a) =>+       (Int -> Int)+    -> MutArray a+    -> a+    -> m (MutArray a)+snocWith allocSize arr x = liftIO $ do+    let newEnd = INDEX_NEXT(arrEnd arr,a)+    if newEnd <= arrBound arr+    then snocNewEnd newEnd arr x+    else snocWithRealloc allocSize arr x++-- | The array is mutated to append an additional element to it. If there+-- is no reserved space available in the array then it is reallocated to grow+-- it by 'arrayChunkBytes' rounded up to 'blockSize' when the size becomes more+-- than 'largeObjectThreshold'.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- Performs O(n^2) copies to grow but is thrifty on memory.+--+-- /Pre-release/+{-# INLINE snocLinear #-}+snocLinear :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+snocLinear = snocWith (+ allocBytesToBytes (undefined :: a) arrayChunkBytes)++-- | The array is mutated to append an additional element to it. If there is no+-- reserved space available in the array then it is reallocated to double the+-- original size.+--+-- This is useful to reduce allocations when appending unknown number of+-- elements.+--+-- Note that the returned array may be a mutated version of the original array.+--+-- >>> snoc = MutArray.snocWith (* 2)+--+-- Performs O(n * log n) copies to grow, but is liberal with memory allocation.+--+{-# INLINE snoc #-}+snoc :: forall m a. (MonadIO m, Unbox a) => MutArray a -> a -> m (MutArray a)+snoc = snocWith f++    where++    f oldSize =+        if isPower2 oldSize+        then oldSize * 2+        else roundUpToPower2 oldSize * 2++-------------------------------------------------------------------------------+-- Random reads+-------------------------------------------------------------------------------++-- XXX Can this be deduplicated with array/foreign++-- | Return the element at the specified index without checking the bounds.+--+-- Unsafe because it does not check the bounds of the array.+{-# INLINE_NORMAL getIndexUnsafe #-}+getIndexUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+getIndexUnsafe i MutArray{..} = do+    let index = INDEX_OF(arrStart,i,a)+    assert (i >= 0 && INDEX_VALID(index,arrEnd,a)) (return ())+    liftIO $ peekAt index arrContents++-- | /O(1)/ Lookup the element at the given index. Index starts from 0.+--+{-# INLINE getIndex #-}+getIndex :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (Maybe a)+getIndex i MutArray{..} = do+    let index = INDEX_OF(arrStart,i,a)+    if i >= 0 && INDEX_VALID(index,arrEnd,a)+    then liftIO $ Just <$> peekAt index arrContents+    else return Nothing++-- | /O(1)/ Lookup the element at the given index from the end of the array.+-- Index starts from 0.+--+-- Slightly faster than computing the forward index and using getIndex.+--+{-# INLINE getIndexRev #-}+getIndexRev :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m a+getIndexRev i MutArray{..} = do+    let index = RINDEX_OF(arrEnd,i,a)+    if i >= 0 && index >= arrStart+    then liftIO $ peekAt index arrContents+    else invalidIndex "getIndexRev" i++data GetIndicesState contents start end st =+    GetIndicesState contents start end st++-- | Given an unfold that generates array indices, read the elements on those+-- indices from the supplied MutArray. An error is thrown if an index is out of+-- bounds.+--+-- /Pre-release/+{-# INLINE getIndicesD #-}+getIndicesD :: (Monad m, Unbox a) =>+    (forall b. IO b -> m b) -> D.Stream m Int -> Unfold m (MutArray a) a+getIndicesD liftio (D.Stream stepi sti) = Unfold step inject++    where++    inject (MutArray contents start end _) =+        return $ GetIndicesState contents start end sti++    {-# INLINE_LATE step #-}+    step (GetIndicesState contents start end st) = do+        r <- stepi defState st+        case r of+            D.Yield i s -> do+                x <- liftio $ getIndex i (MutArray contents start end undefined)+                case x of+                    Just v -> return $ D.Yield v (GetIndicesState contents start end s)+                    Nothing -> error "Invalid Index"+            D.Skip s -> return $ D.Skip (GetIndicesState contents start end s)+            D.Stop -> return D.Stop++{-# INLINE getIndices #-}+getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a+getIndices = getIndicesD liftIO++-------------------------------------------------------------------------------+-- Subarrays+-------------------------------------------------------------------------------++-- XXX We can also get immutable slices.++-- | /O(1)/ Slice an array in constant time.+--+-- Unsafe: The bounds of the slice are not checked.+--+-- /Unsafe/+--+-- /Pre-release/+{-# INLINE getSliceUnsafe #-}+getSliceUnsafe :: forall a. Unbox a+    => Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSliceUnsafe index len (MutArray contents start e _) =+    let fp1 = INDEX_OF(start,index,a)+        end = fp1 + (len * SIZE_OF(a))+     in assert+            (index >= 0 && len >= 0 && end <= e)+            -- Note: In a slice we always use bound = end so that the slice+            -- user cannot overwrite elements beyond the end of the slice.+            (MutArray contents fp1 end end)++-- | /O(1)/ Slice an array in constant time. Throws an error if the slice+-- extends out of the array bounds.+--+-- /Pre-release/+{-# INLINE getSlice #-}+getSlice :: forall a. Unbox a =>+       Int -- ^ from index+    -> Int -- ^ length of the slice+    -> MutArray a+    -> MutArray a+getSlice index len (MutArray contents start e _) =+    let fp1 = INDEX_OF(start,index,a)+        end = fp1 + (len * SIZE_OF(a))+     in if index >= 0 && len >= 0 && end <= e+        -- Note: In a slice we always use bound = end so that the slice user+        -- cannot overwrite elements beyond the end of the slice.+        then MutArray contents fp1 end end+        else error+                $ "getSlice: invalid slice, index "+                ++ show index ++ " length " ++ show len++-------------------------------------------------------------------------------+-- In-place mutation algorithms+-------------------------------------------------------------------------------++-- XXX consider the bulk update/accumulation/permutation APIs from vector.++-- | You may not need to reverse an array because you can consume it in reverse+-- using 'readerRev'. To reverse large arrays you can read in reverse and write+-- to another array. However, in-place reverse can be useful to take adavantage+-- of cache locality and when you do not want to allocate additional memory.+--+{-# INLINE reverse #-}+reverse :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m ()+reverse MutArray{..} = liftIO $ do+    let l = arrStart+        h = INDEX_PREV(arrEnd,a)+     in swap l h++    where++    swap l h = do+        when (l < h) $ do+            swapArrayByteIndices (Proxy :: Proxy a) arrContents l h+            swap (INDEX_NEXT(l,a)) (INDEX_PREV(h,a))++-- | Generate the next permutation of the sequence, returns False if this is+-- the last permutation.+--+-- /Unimplemented/+{-# INLINE permute #-}+permute :: MutArray a -> m Bool+permute = undefined++-- | Partition an array into two halves using a partitioning predicate. The+-- first half retains values where the predicate is 'False' and the second half+-- retains values where the predicate is 'True'.+--+-- /Pre-release/+{-# INLINE partitionBy #-}+partitionBy :: forall m a. (MonadIO m, Unbox a)+    => (a -> Bool) -> MutArray a -> m (MutArray a, MutArray a)+partitionBy f arr@MutArray{..} = liftIO $ do+    if arrStart >= arrEnd+    then return (arr, arr)+    else do+        ptr <- go arrStart (INDEX_PREV(arrEnd,a))+        let pl = MutArray arrContents arrStart ptr ptr+            pr = MutArray arrContents ptr arrEnd arrEnd+        return (pl, pr)++    where++    -- Invariant low < high on entry, and on return as well+    moveHigh low high = do+        h <- peekAt high arrContents+        if f h+        then+            -- Correctly classified, continue the loop+            let high1 = INDEX_PREV(high,a)+             in if low == high1+                then return Nothing+                else moveHigh low high1+        else return (Just (high, h)) -- incorrectly classified++    -- Keep a low pointer starting at the start of the array (first partition)+    -- and a high pointer starting at the end of the array (second partition).+    -- Keep incrementing the low ptr and decrementing the high ptr until both+    -- are wrongly classified, at that point swap the two and continue until+    -- the two pointer cross each other.+    --+    -- Invariants when entering this loop:+    -- low <= high+    -- Both low and high are valid locations within the array+    go low high = do+        l <- peekAt low arrContents+        if f l+        then+            -- low is wrongly classified+            if low == high+            then return low+            else do -- low < high+                r <- moveHigh low high+                case r of+                    Nothing -> return low+                    Just (high1, h) -> do -- low < high1+                        pokeAt low arrContents h+                        pokeAt high1 arrContents l+                        let low1 = INDEX_NEXT(low,a)+                            high2 = INDEX_PREV(high1,a)+                        if low1 <= high2+                        then go low1 high2+                        else return low1 -- low1 > high2++        else do+            -- low is correctly classified+            let low1 = INDEX_NEXT(low,a)+            if low == high+            then return low1+            else go low1 high++-- | Shuffle corresponding elements from two arrays using a shuffle function.+-- If the shuffle function returns 'False' then do nothing otherwise swap the+-- elements. This can be used in a bottom up fold to shuffle or reorder the+-- elements.+--+-- /Unimplemented/+{-# INLINE shuffleBy #-}+shuffleBy :: (a -> a -> m Bool) -> MutArray a -> MutArray a -> m ()+shuffleBy = undefined++-- XXX we can also make the folds partial by stopping at a certain level.+--+-- | @divideBy level partition array@  performs a top down hierarchical+-- recursive partitioning fold of items in the container using the given+-- function as the partition function.  Level indicates the level in the tree+-- where the fold would stop.+--+-- This performs a quick sort if the partition function is+-- 'partitionBy (< pivot)'.+--+-- /Unimplemented/+{-# INLINABLE divideBy #-}+divideBy ::+    Int -> (MutArray a -> m (MutArray a, MutArray a)) -> MutArray a -> m ()+divideBy = undefined++-- | @mergeBy level merge array@ performs a pairwise bottom up fold recursively+-- merging the pairs using the supplied merge function. Level indicates the+-- level in the tree where the fold would stop.+--+-- This performs a random shuffle if the merge function is random.  If we+-- stop at level 0 and repeatedly apply the function then we can do a bubble+-- sort.+--+-- /Unimplemented/+mergeBy :: Int -> (MutArray a -> MutArray a -> m ()) -> MutArray a -> m ()+mergeBy = undefined++-------------------------------------------------------------------------------+-- Size+-------------------------------------------------------------------------------++-- | /O(1)/ Get the byte length of the array.+--+{-# INLINE byteLength #-}+byteLength :: MutArray a -> Int+byteLength MutArray{..} =+    let len = arrEnd - arrStart+    in assert (len >= 0) len++-- Note: try to avoid the use of length in performance sensitive internal+-- routines as it involves a costly 'div' operation. Instead use the end ptr+-- in the array to check the bounds etc.+--+-- | /O(1)/ Get the length of the array i.e. the number of elements in the+-- array.+--+-- Note that 'byteLength' is less expensive than this operation, as 'length'+-- involves a costly division operation.+--+{-# INLINE length #-}+length :: forall a. Unbox a => MutArray a -> Int+length arr =+    let elemSize = SIZE_OF(a)+        blen = byteLength arr+     in assert (blen `mod` elemSize == 0) (blen `div` elemSize)++-- | Get the total capacity of an array. An array may have space reserved+-- beyond the current used length of the array.+--+-- /Pre-release/+{-# INLINE byteCapacity #-}+byteCapacity :: MutArray a -> Int+byteCapacity MutArray{..} =+    let len = arrBound - arrStart+    in assert (len >= 0) len++-- | The remaining capacity in the array for appending more elements without+-- reallocation.+--+-- /Pre-release/+{-# INLINE bytesFree #-}+bytesFree :: MutArray a -> Int+bytesFree MutArray{..} =+    let n = arrBound - arrEnd+    in assert (n >= 0) n++-------------------------------------------------------------------------------+-- Streams of arrays - Creation+-------------------------------------------------------------------------------++data GroupState s contents start end bound+    = GroupStart s+    | GroupBuffer s contents start end bound+    | GroupYield+        contents start end bound (GroupState s contents start end bound)+    | GroupFinish++{-# INLINE_NORMAL chunksOfAs #-}+chunksOfAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> D.Stream m a -> D.Stream m (MutArray a)+chunksOfAs ps n (D.Stream step state) =+    D.Stream step' (GroupStart state)++    where++    {-# INLINE_LATE step' #-}+    step' _ (GroupStart st) = do+        when (n <= 0) $+            -- XXX we can pass the module string from the higher level API+            error $ "Streamly.Internal.Data.MutArray.Mut.Type.chunksOf: "+                    ++ "the size of arrays [" ++ show n+                    ++ "] must be a natural number"+        (MutArray contents start end bound :: MutArray a) <- newAs ps n+        return $ D.Skip (GroupBuffer st contents start end bound)++    step' gst (GroupBuffer st contents start end bound) = do+        r <- step (adaptState gst) st+        case r of+            D.Yield x s -> do+                liftIO $ pokeAt end contents  x+                let end1 = INDEX_NEXT(end,a)+                return $+                    if end1 >= bound+                    then D.Skip+                            (GroupYield+                                contents start end1 bound (GroupStart s))+                    else D.Skip (GroupBuffer s contents start end1 bound)+            D.Skip s ->+                return $ D.Skip (GroupBuffer s contents start end bound)+            D.Stop ->+                return+                    $ D.Skip (GroupYield contents start end bound GroupFinish)++    step' _ (GroupYield contents start end bound next) =+        return $ D.Yield (MutArray contents start end bound) next++    step' _ GroupFinish = return D.Stop++-- | @chunksOf n stream@ groups the elements in the input stream into arrays of+-- @n@ elements each.+--+-- Same as the following but may be more efficient:+--+-- >>> chunksOf n = Stream.foldMany (MutArray.writeN n)+--+-- /Pre-release/+{-# INLINE_NORMAL chunksOf #-}+chunksOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> D.Stream m (MutArray a)+-- XXX the idiomatic implementation leads to large regression in the D.reverse'+-- benchmark. It seems it has difficulty producing optimized code when+-- converting to StreamK. Investigate GHC optimizations.+-- chunksOf n = D.foldMany (writeN n)+chunksOf = chunksOfAs Unpinned++-- | Like 'chunksOf' but creates pinned arrays.+{-# INLINE_NORMAL pinnedChunksOf #-}+pinnedChunksOf :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> D.Stream m (MutArray a)+-- pinnedChunksOf n = D.foldMany (pinnedWriteN n)+pinnedChunksOf = chunksOfAs Pinned++-- XXX This should take a PinnedState+-- XXX buffer to a list instead?+-- | Buffer the stream into arrays in memory.+{-# INLINE arrayStreamKFromStreamDAs #-}+arrayStreamKFromStreamDAs :: forall m a. (MonadIO m, Unbox a) =>+    PinnedState -> D.Stream m a -> m (StreamK m (MutArray a))+arrayStreamKFromStreamDAs ps =+    let n = allocBytesToElemCount (undefined :: a) defaultChunkSize+     in D.foldr K.cons K.nil . chunksOfAs ps n++-------------------------------------------------------------------------------+-- Streams of arrays - Flattening+-------------------------------------------------------------------------------++data FlattenState s contents a =+      OuterLoop s+    | InnerLoop s contents !Int !Int++-- | Use the "reader" unfold instead.+--+-- @flattenArrays = unfoldMany reader@+--+-- We can try this if there are any fusion issues in the unfold.+--+{-# INLINE_NORMAL flattenArrays #-}+flattenArrays :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+flattenArrays (D.Stream step state) = D.Stream step' (OuterLoop state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield MutArray{..} s ->+                D.Skip (InnerLoop s arrContents arrStart arrEnd)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p end) | assert (p <= end) (p == end) =+        return $ D.Skip $ OuterLoop st++    step' _ (InnerLoop st contents p end) = do+        x <- liftIO $ peekAt p contents+        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)++-- | Use the "readerRev" unfold instead.+--+-- @flattenArrays = unfoldMany readerRev@+--+-- We can try this if there are any fusion issues in the unfold.+--+{-# INLINE_NORMAL flattenArraysRev #-}+flattenArraysRev :: forall m a. (MonadIO m, Unbox a)+    => D.Stream m (MutArray a) -> D.Stream m a+flattenArraysRev (D.Stream step state) = D.Stream step' (OuterLoop state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (OuterLoop st) = do+        r <- step (adaptState gst) st+        return $ case r of+            D.Yield MutArray{..} s ->+                let p = INDEX_PREV(arrEnd,a)+                 in D.Skip (InnerLoop s arrContents p arrStart)+            D.Skip s -> D.Skip (OuterLoop s)+            D.Stop -> D.Stop++    step' _ (InnerLoop st _ p start) | p < start =+        return $ D.Skip $ OuterLoop st++    step' _ (InnerLoop st contents p start) = do+        x <- liftIO $ peekAt p contents+        let cur = INDEX_PREV(p,a)+        return $ D.Yield x (InnerLoop st contents cur start)++-------------------------------------------------------------------------------+-- Unfolds+-------------------------------------------------------------------------------++data ArrayUnsafe a = ArrayUnsafe+    {-# UNPACK #-} !MutByteArray   -- contents+    {-# UNPACK #-} !Int                -- index 1+    {-# UNPACK #-} !Int                -- index 2++toArrayUnsafe :: MutArray a -> ArrayUnsafe a+toArrayUnsafe (MutArray contents start end _) = ArrayUnsafe contents start end++fromArrayUnsafe ::+#ifdef DEVBUILD+    Unbox a =>+#endif+    ArrayUnsafe a -> MutArray a+fromArrayUnsafe (ArrayUnsafe contents start end) =+         MutArray contents start end end++{-# INLINE_NORMAL producerWith #-}+producerWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> Producer m (MutArray a) a+producerWith liftio = Producer step (return . toArrayUnsafe) extract+    where++    {-# INLINE_LATE step #-}+    step (ArrayUnsafe _ cur end)+        | assert (cur <= end) (cur == end) = return D.Stop+    step (ArrayUnsafe contents cur end) = do+            -- When we use a purely lazy Monad like Identity, we need to force a+            -- few actions for correctness and execution order sanity. We want+            -- the peek to occur right here and not lazily at some later point+            -- because we want the peek to be ordered with respect to the touch.+            !x <- liftio $ peekAt cur contents+            return $ D.Yield x (ArrayUnsafe contents (INDEX_NEXT(cur,a)) end)++    extract = return . fromArrayUnsafe++-- | Resumable unfold of an array.+--+{-# INLINE_NORMAL producer #-}+producer :: forall m a. (MonadIO m, Unbox a) => Producer m (MutArray a) a+producer = producerWith liftIO++-- | Unfold an array into a stream.+--+{-# INLINE_NORMAL reader #-}+reader :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+reader = Producer.simplify producer++{-# INLINE_NORMAL readerRevWith #-}+readerRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> Unfold m (MutArray a) a+readerRevWith liftio = Unfold step inject+    where++    inject (MutArray contents start end _) =+        let p = INDEX_PREV(end,a)+         in return $ ArrayUnsafe contents start p++    {-# INLINE_LATE step #-}+    step (ArrayUnsafe _ start p) | p < start = return D.Stop+    step (ArrayUnsafe contents start p) = do+        !x <- liftio $ peekAt p contents+        return $ D.Yield x (ArrayUnsafe contents start (INDEX_PREV(p,a)))++-- | Unfold an array into a stream in reverse order.+--+{-# INLINE_NORMAL readerRev #-}+readerRev :: forall m a. (MonadIO m, Unbox a) => Unfold m (MutArray a) a+readerRev = readerRevWith liftIO++-------------------------------------------------------------------------------+-- to Lists and streams+-------------------------------------------------------------------------------++{-+-- Use foldr/build fusion to fuse with list consumers+-- This can be useful when using the IsList instance+{-# INLINE_LATE toListFB #-}+toListFB :: forall a b. Unbox a => (a -> b -> b) -> b -> MutArray a -> b+toListFB c n MutArray{..} = go arrStart+    where++    go p | assert (p <= arrEnd) (p == arrEnd) = n+    go p =+        -- unsafeInlineIO allows us to run this in Identity monad for pure+        -- toList/foldr case which makes them much faster due to not+        -- accumulating the list and fusing better with the pure consumers.+        --+        -- This should be safe as the array contents are guaranteed to be+        -- evaluated/written to before we peek at them.+        -- XXX+        let !x = unsafeInlineIO $ do+                    r <- peekAt arrContents p+                    return r+        in c x (go (PTR_NEXT(p,a)))+-}++-- XXX Monadic foldr/build fusion?+-- Reference: https://www.researchgate.net/publication/220676509_Monadic_augment_and_generalised_short_cut_fusion++-- | Convert a 'MutArray' into a list.+--+{-# INLINE toList #-}+toList :: forall m a. (MonadIO m, Unbox a) => MutArray a -> m [a]+toList MutArray{..} = liftIO $ go arrStart+    where++    go p | assert (p <= arrEnd) (p == arrEnd) = return []+    go p = do+        x <- peekAt p arrContents+        (:) x <$> go (INDEX_NEXT(p,a))++{-# INLINE_NORMAL toStreamDWith #-}+toStreamDWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a+toStreamDWith liftio MutArray{..} = D.Stream step arrStart++    where++    {-# INLINE_LATE step #-}+    step _ p | assert (p <= arrEnd) (p == arrEnd) = return D.Stop+    step _ p = liftio $ do+        r <- peekAt p arrContents+        return $ D.Yield r (INDEX_NEXT(p,a))++-- | Convert a 'MutArray' into a stream.+--+-- >>> read = Stream.unfold MutArray.reader+--+{-# INLINE_NORMAL read #-}+read :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a+read = toStreamDWith liftIO++{-# INLINE toStreamKWith #-}+toStreamKWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+toStreamKWith liftio MutArray{..} = go arrStart++    where++    go p | assert (p <= arrEnd) (p == arrEnd) = K.nil+         | otherwise =+        let elemM = peekAt p arrContents+        in liftio elemM `K.consM` go (INDEX_NEXT(p,a))++{-# INLINE toStreamK #-}+toStreamK :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+toStreamK = toStreamKWith liftIO++{-# INLINE_NORMAL toStreamDRevWith #-}+toStreamDRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> D.Stream m a+toStreamDRevWith liftio MutArray{..} =+    let p = INDEX_PREV(arrEnd,a)+    in D.Stream step p++    where++    {-# INLINE_LATE step #-}+    step _ p | p < arrStart = return D.Stop+    step _ p = liftio $ do+        r <- peekAt p arrContents+        return $ D.Yield r (INDEX_PREV(p,a))++-- | Convert a 'MutArray' into a stream in reverse order.+--+-- >>> readRev = Stream.unfold MutArray.readerRev+--+{-# INLINE_NORMAL readRev #-}+readRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> D.Stream m a+readRev = toStreamDRevWith liftIO++{-# INLINE toStreamKRevWith #-}+toStreamKRevWith ::+       forall m a. (Monad m, Unbox a)+    => (forall b. IO b -> m b) -> MutArray a -> StreamK m a+toStreamKRevWith liftio MutArray {..} =+    let p = INDEX_PREV(arrEnd,a)+    in go p++    where++    go p | p < arrStart = K.nil+         | otherwise =+        let elemM = peekAt p arrContents+        in liftio elemM `K.consM` go (INDEX_PREV(p,a))++{-# INLINE toStreamKRev #-}+toStreamKRev :: forall m a. (MonadIO m, Unbox a) => MutArray a -> StreamK m a+toStreamKRev = toStreamKRevWith liftIO++-------------------------------------------------------------------------------+-- Folding+-------------------------------------------------------------------------------++-- XXX Need something like "MutArray m a" enforcing monadic action to avoid the+-- possibility of such APIs.+--+-- | Strict left fold of an array.+{-# INLINE_NORMAL foldl' #-}+foldl' :: (MonadIO m, Unbox a) => (b -> a -> b) -> b -> MutArray a -> m b+foldl' f z arr = D.foldl' f z $ read arr++-- | Right fold of an array.+{-# INLINE_NORMAL foldr #-}+foldr :: (MonadIO m, Unbox a) => (a -> b -> b) -> b -> MutArray a -> m b+foldr f z arr = D.foldr f z $ read arr++-------------------------------------------------------------------------------+-- Folds+-------------------------------------------------------------------------------++-- Note: Arrays may be allocated with a specific alignment at the beginning of+-- the array. If you need to maintain that alignment on reallocations then you+-- can resize the array manually before append, using an aligned resize+-- operation.++-- XXX Keep the bound intact to not lose any free space? Perf impact?++-- | @writeAppendNUnsafe n arr@ appends up to @n@ input items to the supplied+-- array.+--+-- Unsafe: Do not drive the fold beyond @n@ elements, it will lead to memory+-- corruption or segfault.+--+-- Any free space left in the array after appending @n@ elements is lost.+--+-- /Internal/+{-# INLINE_NORMAL writeAppendNUnsafe #-}+writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) =>+       Int+    -> m (MutArray a)+    -> Fold m a (MutArray a)+writeAppendNUnsafe n action =+    fmap fromArrayUnsafe $ FL.foldlM' step initial++    where++    initial = do+        assert (n >= 0) (return ())+        arr@(MutArray _ _ end bound) <- action+        let free = bound - end+            needed = n * SIZE_OF(a)+        -- XXX We can also reallocate if the array has too much free space,+        -- otherwise we lose that space.+        arr1 <-+            if free < needed+            then noinline reallocWith "writeAppendNUnsafeWith" (+ needed) needed arr+            else return arr+        return $ toArrayUnsafe arr1++    step (ArrayUnsafe contents start end) x = do+        liftIO $ pokeAt end contents x+        return $ ArrayUnsafe contents start (INDEX_NEXT(end,a))++-- | Append @n@ elements to an existing array. Any free space left in the array+-- after appending @n@ elements is lost.+--+-- >>> writeAppendN n initial = Fold.take n (MutArray.writeAppendNUnsafe n initial)+--+{-# INLINE_NORMAL writeAppendN #-}+writeAppendN :: forall m a. (MonadIO m, Unbox a) =>+    Int -> m (MutArray a) -> Fold m a (MutArray a)+writeAppendN n initial = FL.take n (writeAppendNUnsafe n initial)++-- | @writeAppendWith realloc action@ mutates the array generated by @action@ to+-- append the input stream. If there is no reserved space available in the+-- array it is reallocated to a size in bytes  determined by @realloc oldSize@,+-- where @oldSize@ is the current size of the array in bytes.+--+-- Note that the returned array may be a mutated version of original array.+--+-- >>> writeAppendWith sizer = Fold.foldlM' (MutArray.snocWith sizer)+--+-- /Pre-release/+{-# INLINE writeAppendWith #-}+writeAppendWith :: forall m a. (MonadIO m, Unbox a) =>+    (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)+writeAppendWith sizer = FL.foldlM' (snocWith sizer)++-- | @append action@ mutates the array generated by @action@ to append the+-- input stream. If there is no reserved space available in the array it is+-- reallocated to double the size.+--+-- Note that the returned array may be a mutated version of original array.+--+-- >>> writeAppend = MutArray.writeAppendWith (* 2)+--+{-# INLINE writeAppend #-}+writeAppend :: forall m a. (MonadIO m, Unbox a) =>+    m (MutArray a) -> Fold m a (MutArray a)+writeAppend = writeAppendWith (* 2)++-- XXX We can carry bound as well in the state to make sure we do not lose the+-- remaining capacity. Need to check perf impact.+--+-- | Like 'writeNUnsafe' but takes a new array allocator @alloc size@ function+-- as argument.+--+-- >>> writeNWithUnsafe alloc n = MutArray.writeAppendNUnsafe (alloc n) n+--+-- /Pre-release/+{-# INLINE_NORMAL writeNWithUnsafe #-}+writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial++    where++    initial = toArrayUnsafe <$> alloc (max n 0)++    step (ArrayUnsafe contents start end) x = do+        liftIO $ pokeAt end contents x+        return+          $ ArrayUnsafe contents start (INDEX_NEXT(end,a))++{-# INLINE_NORMAL writeNUnsafeAs #-}+writeNUnsafeAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Fold m a (MutArray a)+writeNUnsafeAs ps = writeNWithUnsafe (newAs ps)++-- | Like 'writeN' but does not check the array bounds when writing. The fold+-- driver must not call the step function more than 'n' times otherwise it will+-- corrupt the memory and crash. This function exists mainly because any+-- conditional in the step function blocks fusion causing 10x performance+-- slowdown.+--+-- >>> writeNUnsafe = MutArray.writeNWithUnsafe MutArray.new+--+{-# INLINE_NORMAL writeNUnsafe #-}+writeNUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+writeNUnsafe = writeNUnsafeAs Unpinned++-- | Like 'writeNUnsafe' but creates a pinned array.+{-# INLINE_NORMAL pinnedWriteNUnsafe #-}+pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+pinnedWriteNUnsafe = writeNUnsafeAs Pinned++-- | @writeNWith alloc n@ folds a maximum of @n@ elements into an array+-- allocated using the @alloc@ function.+--+-- >>> writeNWith alloc n = Fold.take n (MutArray.writeNWithUnsafe alloc n)+-- >>> writeNWith alloc n = MutArray.writeAppendN (alloc n) n+--+{-# INLINE_NORMAL writeNWith #-}+writeNWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeNWith alloc n = FL.take n (writeNWithUnsafe alloc n)++{-# INLINE_NORMAL writeNAs #-}+writeNAs ::+       forall m a. (MonadIO m, Unbox a)+    => PinnedState+    -> Int+    -> Fold m a (MutArray a)+writeNAs ps = writeNWith (newAs ps)++-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an+-- 'MutArray'.+--+-- >>> writeN = MutArray.writeNWith MutArray.new+-- >>> writeN n = Fold.take n (MutArray.writeNUnsafe n)+-- >>> writeN n = MutArray.writeAppendN n (MutArray.new n)+--+{-# INLINE_NORMAL writeN #-}+writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+writeN = writeNAs Unpinned++-- | Like 'writeN' but creates a pinned array.+{-# INLINE_NORMAL pinnedWriteN #-}+pinnedWriteN ::+       forall m a. (MonadIO m, Unbox a)+    => Int+    -> Fold m a (MutArray a)+pinnedWriteN = writeNAs Pinned++-- | Like writeNWithUnsafe but writes the array in reverse order.+--+-- /Internal/+{-# INLINE_NORMAL writeRevNWithUnsafe #-}+writeRevNWithUnsafe :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeRevNWithUnsafe alloc n = fromArrayUnsafe <$> FL.foldlM' step initial++    where++    toArrayUnsafeRev (MutArray contents _ _ bound) =+         ArrayUnsafe contents bound bound++    initial = toArrayUnsafeRev <$> alloc (max n 0)++    step (ArrayUnsafe contents start end) x = do+        let ptr = INDEX_PREV(start,a)+        liftIO $ pokeAt ptr contents x+        return+          $ ArrayUnsafe contents ptr end++-- | Like writeNWith but writes the array in reverse order.+--+-- /Internal/+{-# INLINE_NORMAL writeRevNWith #-}+writeRevNWith :: forall m a. (MonadIO m, Unbox a)+    => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)+writeRevNWith alloc n = FL.take n (writeRevNWithUnsafe alloc n)++-- | Like writeN but writes the array in reverse order.+--+-- /Pre-release/+{-# INLINE_NORMAL writeRevN #-}+writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)+writeRevN = writeRevNWith new++-- | @pinnedWriteNAligned align n@ folds a maximum of @n@ elements from the+-- input stream to a 'MutArray' aligned to the given size.+--+-- >>> pinnedWriteNAligned align = MutArray.writeNWith (MutArray.pinnedNewAligned align)+-- >>> pinnedWriteNAligned align n = MutArray.writeAppendN n (MutArray.pinnedNewAligned align n)+--+-- /Pre-release/+--+{-# INLINE_NORMAL pinnedWriteNAligned #-}+pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)+    => Int -> Int -> Fold m a (MutArray a)+pinnedWriteNAligned align = writeNWith (pinnedNewAligned align)++-- XXX Buffer to a list instead?++-- | Buffer a stream into a stream of arrays.+--+-- >>> writeChunks n = Fold.many (MutArray.writeN n) Fold.toStreamK+--+-- Breaking an array into an array stream  can be useful to consume a large+-- array sequentially such that memory of the array is released incrementatlly.+--+-- See also: 'arrayStreamKFromStreamD'.+--+-- /Unimplemented/+--+{-# INLINE_NORMAL writeChunks #-}+writeChunks :: (MonadIO m, Unbox a) =>+    Int -> Fold m a (StreamK n (MutArray a))+writeChunks n = FL.many (writeN n) FL.toStreamK++{-# INLINE_NORMAL writeWithAs #-}+writeWithAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> Fold m a (MutArray a)+-- writeWithAs ps n = FL.rmapM rightSize $ writeAppendWith (* 2) (newAs ps n)+writeWithAs ps elemCount =+    FL.rmapM extract $ FL.foldlM' step initial++    where++    initial = do+        when (elemCount < 0) $ error "writeWith: elemCount is negative"+        liftIO $ newAs ps elemCount++    step arr@(MutArray _ start end bound) x+        | INDEX_NEXT(end,a) > bound = do+        let oldSize = end - start+            newSize = max (oldSize * 2) 1+        arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr+        snocUnsafe arr1 x+    step arr x = snocUnsafe arr x++    extract = liftIO . rightSize++-- XXX Compare writeWith with fromStreamD which uses an array of streams+-- implementation. We can write this using writeChunks above if that is faster.+-- If writeWith is faster then we should use that to implement+-- fromStreamD.+--+-- XXX The realloc based implementation needs to make one extra copy if we use+-- shrinkToFit.  On the other hand, the stream of arrays implementation may+-- buffer the array chunk pointers in memory but it does not have to shrink as+-- we know the exact size in the end. However, memory copying does not seem to+-- be as expensive as the allocations. Therefore, we need to reduce the number+-- of allocations instead. Also, the size of allocations matters, right sizing+-- an allocation even at the cost of copying sems to help.  Should be measured+-- on a big stream with heavy calls to toArray to see the effect.+--+-- XXX check if GHC's memory allocator is efficient enough. We can try the C+-- malloc to compare against.++-- | @writeWith minCount@ folds the whole input to a single array. The array+-- starts at a size big enough to hold minCount elements, the size is doubled+-- every time the array needs to be grown.+--+-- /Caution! Do not use this on infinite streams./+--+-- >>> f n = MutArray.writeAppendWith (* 2) (MutArray.new n)+-- >>> writeWith n = Fold.rmapM MutArray.rightSize (f n)+-- >>> writeWith n = Fold.rmapM MutArray.fromArrayStreamK (MutArray.writeChunks n)+--+-- /Pre-release/+{-# INLINE_NORMAL writeWith #-}+writeWith :: forall m a. (MonadIO m, Unbox a)+    => Int -> Fold m a (MutArray a)+-- writeWith n = FL.rmapM rightSize $ writeAppendWith (* 2) (new n)+writeWith = writeWithAs Unpinned++-- | Fold the whole input to a single array.+--+-- Same as 'writeWith' using an initial array size of 'arrayChunkBytes' bytes+-- rounded up to the element size.+--+-- /Caution! Do not use this on infinite streams./+--+{-# INLINE write #-}+write :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+write = writeWith (allocBytesToElemCount (undefined :: a) arrayChunkBytes)++-- | Like 'write' but creates a pinned array.+{-# INLINE pinnedWrite #-}+pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)+pinnedWrite =+    writeWithAs Pinned (allocBytesToElemCount (undefined :: a) arrayChunkBytes)++-------------------------------------------------------------------------------+-- construct from streams, known size+-------------------------------------------------------------------------------++{-# INLINE_NORMAL fromStreamDNAs #-}+fromStreamDNAs :: forall m a. (MonadIO m, Unbox a)+    => PinnedState -> Int -> D.Stream m a -> m (MutArray a)+fromStreamDNAs ps limit str = do+    (arr :: MutArray a) <- liftIO $ newAs ps limit+    end <- D.foldlM' (fwrite (arrContents arr)) (return $ arrEnd arr) $ D.take limit str+    return $ arr {arrEnd = end}++    where++    fwrite arrContents ptr x = do+        liftIO $ pokeAt ptr arrContents  x+        return $ INDEX_NEXT(ptr,a)++-- | Use the 'writeN' fold instead.+--+-- >>> fromStreamDN n = Stream.fold (MutArray.writeN n)+--+{-# INLINE_NORMAL fromStreamDN #-}+fromStreamDN :: forall m a. (MonadIO m, Unbox a)+    => Int -> D.Stream m a -> m (MutArray a)+-- fromStreamDN n = D.fold (writeN n)+fromStreamDN = fromStreamDNAs Unpinned++-- | Create a 'MutArray' from the first N elements of a list. The array is+-- allocated to size N, if the list terminates before N elements then the+-- array may hold less than N elements.+--+{-# INLINABLE fromListN #-}+fromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+fromListN n xs = fromStreamDN n $ D.fromList xs++-- | Like 'fromListN' but creates a pinned array.+{-# INLINABLE pinnedFromListN #-}+pinnedFromListN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+pinnedFromListN n xs = fromStreamDNAs Pinned n $ D.fromList xs++-- | Like fromListN but writes the array in reverse order.+--+-- /Pre-release/+{-# INLINE fromListRevN #-}+fromListRevN :: (MonadIO m, Unbox a) => Int -> [a] -> m (MutArray a)+fromListRevN n xs = D.fold (writeRevN n) $ D.fromList xs++-- | Convert a pure stream in Identity monad to a mutable array.+{-# INLINABLE fromPureStream #-}+fromPureStream :: (MonadIO m, Unbox a) => Stream Identity a -> m (MutArray a)+fromPureStream xs =+    liftIO $ D.fold write $ D.morphInner (return . runIdentity) xs++-------------------------------------------------------------------------------+-- convert stream to a single array+-------------------------------------------------------------------------------++{-# INLINE arrayStreamKLength #-}+arrayStreamKLength :: (Monad m, Unbox a) => StreamK m (MutArray a) -> m Int+arrayStreamKLength as = K.foldl' (+) 0 (K.map length as)++-- | Convert an array stream to an array. Note that this requires peak memory+-- that is double the size of the array stream.+--+{-# INLINE fromArrayStreamK #-}+fromArrayStreamK :: (Unbox a, MonadIO m) =>+    StreamK m (MutArray a) -> m (MutArray a)+fromArrayStreamK as = do+    len <- arrayStreamKLength as+    fromStreamDN len $ D.unfoldMany reader $ D.fromStreamK as++{-# INLINE fromStreamDAs #-}+fromStreamDAs ::+       (MonadIO m, Unbox a) => PinnedState -> D.Stream m a -> m (MutArray a)+fromStreamDAs ps m = arrayStreamKFromStreamDAs ps m >>= fromArrayStreamK++-- CAUTION: a very large number (millions) of arrays can degrade performance+-- due to GC overhead because we need to buffer the arrays before we flatten+-- all the arrays.+--+-- XXX Compare if this is faster or "fold write".+--+-- | We could take the approach of doubling the memory allocation on each+-- overflow. This would result in more or less the same amount of copying as in+-- the chunking approach. However, if we have to shrink in the end then it may+-- result in an extra copy of the entire data.+--+-- >>> fromStreamD = StreamD.fold MutArray.write+--+{-# INLINE fromStreamD #-}+fromStreamD :: (MonadIO m, Unbox a) => D.Stream m a -> m (MutArray a)+fromStreamD = fromStreamDAs Unpinned++-- | Create a 'MutArray' from a list. The list must be of finite size.+--+{-# INLINE fromList #-}+fromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+fromList xs = fromStreamD $ D.fromList xs++-- | Like 'fromList' but creates a pinned array.+{-# INLINE pinnedFromList #-}+pinnedFromList :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+pinnedFromList xs = fromStreamDAs Pinned $ D.fromList xs++-- XXX We are materializing the whole list first for getting the length. Check+-- if the 'fromList' like chunked implementation would fare better.++-- | Like 'fromList' but writes the contents of the list in reverse order.+{-# INLINE fromListRev #-}+fromListRev :: (MonadIO m, Unbox a) => [a] -> m (MutArray a)+fromListRev xs = fromListRevN (Prelude.length xs) xs++-------------------------------------------------------------------------------+-- Cloning+-------------------------------------------------------------------------------++{-# INLINE cloneAs #-}+cloneAs ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => PinnedState -> MutArray a -> m (MutArray a)+cloneAs ps src =+    liftIO $ do+        let startSrc = arrStart src+            srcLen = arrEnd src - startSrc+        newArrContents <-+            Unboxed.cloneSliceUnsafeAs ps startSrc srcLen (arrContents src)+        return $ MutArray newArrContents 0 srcLen srcLen++{-# INLINE clone #-}+clone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => MutArray a -> m (MutArray a)+clone = cloneAs Unpinned++{-# INLINE pinnedClone #-}+pinnedClone ::+    ( MonadIO m+#ifdef DEVBUILD+    , Unbox a+#endif+    )+    => MutArray a -> m (MutArray a)+pinnedClone = cloneAs Pinned++-------------------------------------------------------------------------------+-- Combining+-------------------------------------------------------------------------------++-- | Copy two arrays into a newly allocated array. If the first array is pinned+-- the spliced array is also pinned.+{-# INLINE spliceCopy #-}+spliceCopy :: forall m a. MonadIO m =>+#ifdef DEVBUILD+    Unbox a =>+#endif+    MutArray a -> MutArray a -> m (MutArray a)+spliceCopy arr1 arr2 = liftIO $ do+    let start1 = arrStart arr1+        start2 = arrStart arr2+        len1 = arrEnd arr1 - start1+        len2 = arrEnd arr2 - start2+    let newLen = len1 + len2+    newArrContents <-+        if Unboxed.isPinned (arrContents arr1)+        then Unboxed.pinnedNew newLen+        else Unboxed.new newLen+    let len = len1 + len2+    putSliceUnsafe (arrContents arr1) start1 newArrContents 0 len1+    putSliceUnsafe (arrContents arr2) start2 newArrContents len1 len2+    return $ MutArray newArrContents 0 len len++-- | Really really unsafe, appends the second array into the first array. If+-- the first array does not have enough space it may cause silent data+-- corruption or if you are lucky a segfault.+{-# INLINE spliceUnsafe #-}+spliceUnsafe :: MonadIO m =>+    MutArray a -> MutArray a -> m (MutArray a)+spliceUnsafe dst src =+    liftIO $ do+         let startSrc = arrStart src+             srcLen = arrEnd src - startSrc+             endDst = arrEnd dst+         assertM(endDst + srcLen <= arrBound dst)+         putSliceUnsafe+             (arrContents src) startSrc (arrContents dst) endDst srcLen+         return $ dst {arrEnd = endDst + srcLen}++-- | @spliceWith sizer dst src@ mutates @dst@ to append @src@. If there is no+-- reserved space available in @dst@ it is reallocated to a size determined by+-- the @sizer dstBytes srcBytes@ function, where @dstBytes@ is the size of the+-- first array and @srcBytes@ is the size of the second array, in bytes.+--+-- Note that the returned array may be a mutated version of first array.+--+-- /Pre-release/+{-# INLINE spliceWith #-}+spliceWith :: forall m a. (MonadIO m, Unbox a) =>+    (Int -> Int -> Int) -> MutArray a -> MutArray a -> m (MutArray a)+spliceWith sizer dst@(MutArray _ start end bound) src = do+{-+    let f = writeAppendWith (`sizer` byteLength src) (return dst)+     in D.fold f (toStreamD src)+-}+    assert (end <= bound) (return ())+    let srcBytes = arrEnd src - arrStart src++    dst1 <-+        if end + srcBytes >= bound+        then do+            let dstBytes = end - start+                newSizeInBytes = sizer dstBytes srcBytes+            when (newSizeInBytes < dstBytes + srcBytes)+                $ error+                    $ "splice: newSize is less than the total size "+                    ++ "of arrays being appended. Please check the "+                    ++ "sizer function passed."+            liftIO $ realloc newSizeInBytes dst+        else return dst+    spliceUnsafe dst1 src++-- | The first array is mutated to append the second array. If there is no+-- reserved space available in the first array a new allocation of exact+-- required size is done.+--+-- Note that the returned array may be a mutated version of first array.+--+-- >>> splice = MutArray.spliceWith (+)+--+-- If the original array is pinned the spliced array is also pinned.+--+-- /Pre-release/+{-# INLINE splice #-}+splice :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+splice = spliceWith (+)++-- | Like 'append' but the growth of the array is exponential. Whenever a new+-- allocation is required the previous array size is at least doubled.+--+-- This is useful to reduce allocations when folding many arrays together.+--+-- Note that the returned array may be a mutated version of first array.+--+-- >>> spliceExp = MutArray.spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))+--+-- /Pre-release/+{-# INLINE spliceExp #-}+spliceExp :: (MonadIO m, Unbox a) => MutArray a -> MutArray a -> m (MutArray a)+spliceExp = spliceWith (\l1 l2 -> max (l1 * 2) (l1 + l2))++-------------------------------------------------------------------------------+-- Splitting+-------------------------------------------------------------------------------++-- | Drops the separator byte+{-# INLINE breakOn #-}+breakOn :: MonadIO m+    => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))+breakOn sep arr@MutArray{..} = asPtrUnsafe arr $ \p -> liftIO $ do+    -- XXX Instead of using asPtrUnsafe (pinning memory) we can pass unlifted+    -- Addr# to memchr and it should be safe (from ghc 8.4).+    -- XXX We do not need memchr here, we can use a Haskell equivalent.+    loc <- c_memchr p sep (fromIntegral $ byteLength arr)+    let sepIndex = loc `minusPtr` p+    return $+        if loc == nullPtr+        then (arr, Nothing)+        else+            ( MutArray+                { arrContents = arrContents+                , arrStart = arrStart+                , arrEnd = arrStart + sepIndex -- exclude the separator+                , arrBound = arrStart + sepIndex+                }+            , Just $ MutArray+                    { arrContents = arrContents+                    , arrStart = arrStart + (sepIndex + 1)+                    , arrEnd = arrEnd+                    , arrBound = arrBound+                    }+            )++-- | Create two slices of an array without copying the original array. The+-- specified index @i@ is the first index of the second slice.+--+splitAt :: forall a. Unbox a => Int -> MutArray a -> (MutArray a, MutArray a)+splitAt i arr@MutArray{..} =+    let maxIndex = length arr - 1+    in  if i < 0+        then error "sliceAt: negative array index"+        else if i > maxIndex+             then error $ "sliceAt: specified array index " ++ show i+                        ++ " is beyond the maximum index " ++ show maxIndex+             else let off = i * SIZE_OF(a)+                      p = arrStart + off+                in ( MutArray+                  { arrContents = arrContents+                  , arrStart = arrStart+                  , arrEnd = p+                  , arrBound = p+                  }+                , MutArray+                  { arrContents = arrContents+                  , arrStart = p+                  , arrEnd = arrEnd+                  , arrBound = arrBound+                  }+                )++-------------------------------------------------------------------------------+-- Casting+-------------------------------------------------------------------------------++-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The array size must be a multiple of the size of type @b@+-- otherwise accessing the last element of the array may result into a crash or+-- a random value.+--+-- /Pre-release/+--+castUnsafe ::+#ifdef DEVBUILD+    Unbox b =>+#endif+    MutArray a -> MutArray b+castUnsafe (MutArray contents start end bound) =+    MutArray contents start end bound++-- | Cast an @MutArray a@ into an @MutArray Word8@.+--+asBytes :: MutArray a -> MutArray Word8+asBytes = castUnsafe++-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The length of the array should be a multiple of the size of the+-- target element otherwise 'Nothing' is returned.+--+cast :: forall a b. Unbox b => MutArray a -> Maybe (MutArray b)+cast arr =+    let len = byteLength arr+        r = len `mod` SIZE_OF(b)+     in if r /= 0+        then Nothing+        else Just $ castUnsafe arr++-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted+-- pointer to the FFI call. For unsafe calls we do not need to pin the array.+-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in+-- before the pointer is wrapped.+--+-- From the GHC manual:+--+-- GHC, since version 8.4, guarantees that garbage collection will never occur+-- during an unsafe call, even in the bytecode interpreter, and further+-- guarantees that unsafe calls will be performed in the calling thread. Making+-- it safe to pass heap-allocated objects to unsafe functions.++-- Should we just name it asPtr, the unsafety is implicit for any pointer+-- operations. And we are safe from Haskell perspective because we will be+-- pinning the memory.++-- | Use a @MutArray a@ as @Ptr a@. This is useful when we want to pass an+-- array as a pointer to some operating system call or to a "safe" FFI call.+--+-- If the array is not pinned it is copied to pinned memory before passing it+-- to the monadic action.+--+-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised+-- that the programmer keeps this in mind and creates a pinned array+-- opportunistically before this operation occurs, to avoid the cost of a copy+-- if possible.+--+-- /Unsafe/ because of direct pointer operations. The user must ensure that+-- they are writing within the legal bounds of the array.+--+-- /Pre-release/+--+{-# INLINE asPtrUnsafe #-}+asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b+asPtrUnsafe arr f =+    Unboxed.asPtrUnsafe+        (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))++-------------------------------------------------------------------------------+-- Equality+-------------------------------------------------------------------------------++-- | Compare the length of the arrays. If the length is equal, compare the+-- lexicographical ordering of two underlying byte arrays otherwise return the+-- result of length comparison.+--+-- /Pre-release/+{-# INLINE cmp #-}+cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering+cmp arr1 arr2 =+    liftIO+        $ do+            let marr1 = getMutableByteArray# (arrContents arr1)+                marr2 = getMutableByteArray# (arrContents arr2)+                !(I# st1#) = arrStart arr1+                !(I# st2#) = arrStart arr2+                !(I# len#) = byteLength arr1+            case compare (byteLength arr1) (byteLength arr2) of+                EQ -> do+                    r <- liftIO $ IO $ \s# ->+                             let res =+                                     I#+                                         (compareByteArrays#+                                              (unsafeCoerce# marr1)+                                              st1#+                                              (unsafeCoerce# marr2)+                                              st2#+                                              len#)+                              in (# s#, res #)+                    return $ compare r 0+                x -> return x++-------------------------------------------------------------------------------+-- NFData+-------------------------------------------------------------------------------++-- | Strip elements which match with predicate from both ends.+--+-- /Pre-release/+{-# INLINE strip #-}+strip :: forall a m. (Unbox a, MonadIO m) =>+    (a -> Bool) -> MutArray a -> m (MutArray a)+strip eq arr@MutArray{..} = liftIO $ do+    st <- getStart arrStart+    end <- getLast arrEnd st+    return arr {arrStart = st, arrEnd = end, arrBound = end}++    where++    {-+    -- XXX This should have the same perf but it does not, investigate.+    getStart = do+        r <- liftIO $ D.head $ D.findIndices (not . eq) $ toStreamD arr+        pure $+            case r of+                Nothing -> arrEnd+                Just i -> PTR_INDEX(arrStart,i,a)+    -}++    getStart cur = do+        if cur < arrEnd+        then do+            r <- peekAt cur arrContents+            if eq r+            then getStart (INDEX_NEXT(cur,a))+            else return cur+        else return cur++    getLast cur low = do+        if cur > low+        then do+            let prev = INDEX_PREV(cur,a)+            r <- peekAt prev arrContents+            if eq r+            then getLast prev low+            else return cur+        else return cur++-- | Given an array sorted in ascending order except the last element being out+-- of order, use bubble sort to place the last element at the right place such+-- that the array remains sorted in ascending order.+--+-- /Pre-release/+{-# INLINE bubble #-}+bubble :: (MonadIO m, Unbox a) => (a -> a -> Ordering) -> MutArray a -> m ()+bubble cmp0 arr =+    when (l > 1) $ do+        x <- getIndexUnsafe (l - 1) arr+        go x (l - 2)++        where++        l = length arr++        go x i =+            if i >= 0+            then do+                x1 <- getIndexUnsafe i arr+                case x `cmp0` x1 of+                    LT -> do+                        putIndexUnsafe (i + 1) arr x1+                        go x (i - 1)+                    _ -> putIndexUnsafe (i + 1) arr x+            else putIndexUnsafe (i + 1) arr x
+ src/Streamly/Internal/Data/MutByteArray.hs view
@@ -0,0 +1,251 @@+{-# LANGUAGE TemplateHaskell #-}++-- This is required as all the instances in this module are orphan instances.+{-# OPTIONS_GHC -fno-warn-orphans #-}++-- |+-- Module      : Streamly.Internal.Data.MutByteArray+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--++module Streamly.Internal.Data.MutByteArray+    (+    -- * MutByteArray+      module Streamly.Internal.Data.MutByteArray.Type+    -- * Unbox+    , module Streamly.Internal.Data.Unbox+    , module Streamly.Internal.Data.Unbox.TH+    -- * Serialize+    , module Streamly.Internal.Data.Serialize.Type+    -- * Serialize TH+    , module Streamly.Internal.Data.Serialize.TH+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Data.Proxy (Proxy(..))+import Streamly.Internal.Data.Array (Array(..))+import GHC.Exts (Int(..), sizeofByteArray#, unsafeCoerce#)+import GHC.Word (Word8)++#if __GLASGOW_HASKELL__ >= 900+import GHC.Num.Integer (Integer(..))+#else+import GHC.Integer.GMP.Internals (Integer(..), BigNat(..))+#endif++import Streamly.Internal.Data.MutByteArray.Type+import Streamly.Internal.Data.Serialize.TH+import Streamly.Internal.Data.Serialize.Type+import Streamly.Internal.Data.Unbox+import Streamly.Internal.Data.Unbox.TH++--------------------------------------------------------------------------------+-- Common instances+--------------------------------------------------------------------------------++-- Note+-- ====+--+-- Even a non-functional change such as changing the order of constructors will+-- change the instance derivation.+--+-- This will not pose a problem if both, encode, and decode are done by the same+-- version of the application. There *might* be a problem if version that+-- encodes differs from the version that decodes.+--+-- We need to add some compatibility tests using different versions of+-- dependencies.+--+-- Although such chages for the most basic types won't happen we need to detect+-- if it ever happens.+--+-- Should we worry about these kind of changes and this kind of compatibility?+-- This is a problem for all types of derivations that depend on the order of+-- constructors, for example, Enum.++-- Note on Windows build+-- =====================+--+-- On Windows, having template haskell splices here fail the build with the+-- following error:+--+-- @+-- addLibrarySearchPath: C:\...  (Win32 error 3): The system cannot find the path specified.+-- @+--+-- The error might be irrelavant but having these splices triggers it. We should+-- either fix the problem or avoid the use to template haskell splices in this+-- file.+--+-- Similar issue: https://github.com/haskell/cabal/issues/4741++-- $(Serialize.deriveSerialize ''Maybe)+instance Serialize a => Serialize (Maybe a) where++    {-# INLINE addSizeTo #-}+    addSizeTo acc x =+        case x of+            Nothing -> (acc + 1)+            Just field0 -> (addSizeTo (acc + 1)) field0++    {-# INLINE deserializeAt #-}+    deserializeAt initialOffset arr endOffset = do+        (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset+        case tag :: Word8 of+            0 -> pure (i0, Nothing)+            1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, Just a0)+            _ -> error "Found invalid tag while peeking (Maybe a)"++    {-# INLINE serializeAt #-}+    serializeAt initialOffset arr val =+        case val of+            Nothing -> do+                i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)+                pure i0+            Just field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1++-- $(Serialize.deriveSerialize ''Either)+instance (Serialize a, Serialize b) => Serialize (Either a b) where++    {-# INLINE addSizeTo #-}+    addSizeTo acc x =+        case x of+            Left field0 -> (addSizeTo (acc + 1)) field0+            Right field0 -> (addSizeTo (acc + 1)) field0++    {-# INLINE deserializeAt #-}+    deserializeAt initialOffset arr endOffset = do+        (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset+        case tag :: Word8 of+            0 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, Left a0)+            1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, Right a0)+            _ -> error "Found invalid tag while peeking (Either a b)"++    {-# INLINE serializeAt #-}+    serializeAt initialOffset arr val =+        case val of+            Left field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1+            Right field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1++instance Serialize (Proxy a) where++    {-# INLINE addSizeTo #-}+    addSizeTo acc _ = (acc + 1)++    {-# INLINE deserializeAt #-}+    deserializeAt initialOffset _ _ = pure ((initialOffset + 1), Proxy)++    {-# INLINE serializeAt #-}+    serializeAt initialOffset _ _ = pure (initialOffset + 1)++--------------------------------------------------------------------------------+-- Integer+--------------------------------------------------------------------------------++data LiftedInteger+    = LIS Int+    | LIP (Array Word)+    | LIN (Array Word)++-- $(Serialize.deriveSerialize ''LiftedInteger)+instance Serialize LiftedInteger where++    {-# INLINE addSizeTo #-}+    addSizeTo acc x =+        case x of+            LIS field0 -> (addSizeTo (acc + 1)) field0+            LIP field0 -> (addSizeTo (acc + 1)) field0+            LIN field0 -> (addSizeTo (acc + 1)) field0++    {-# INLINE deserializeAt #-}+    deserializeAt initialOffset arr endOffset = do+        (i0, tag) <- ((deserializeAt initialOffset) arr) endOffset+        case tag :: Word8 of+            0 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, LIS a0)+            1 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, LIP a0)+            2 -> do (i1, a0) <- ((deserializeAt i0) arr) endOffset+                    pure (i1, LIN a0)+            _ -> error "Found invalid tag while peeking (LiftedInteger)"++    {-# INLINE serializeAt #-}+    serializeAt initialOffset arr val =+        case val of+            LIS field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (0 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1+            LIP field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (1 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1+            LIN field0 -> do+                i0 <- ((serializeAt initialOffset) arr) (2 :: Word8)+                i1 <- ((serializeAt i0) arr) field0+                pure i1++#if __GLASGOW_HASKELL__ >= 900++{-# INLINE liftInteger #-}+liftInteger :: Integer -> LiftedInteger+liftInteger (IS x) = LIS (I# x)+liftInteger (IP x) =+    LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))+liftInteger (IN x) =+    LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))++{-# INLINE unliftInteger #-}+unliftInteger :: LiftedInteger -> Integer+unliftInteger (LIS (I# x)) = IS x+unliftInteger (LIP (Array (MutByteArray x) _ _)) = IP (unsafeCoerce# x)+unliftInteger (LIN (Array (MutByteArray x) _ _)) = IN (unsafeCoerce# x)++#else++{-# INLINE liftInteger #-}+liftInteger :: Integer -> LiftedInteger+liftInteger (S# x) = LIS (I# x)+liftInteger (Jp# (BN# x)) =+    LIP (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))+liftInteger (Jn# (BN# x)) =+    LIN (Array (MutByteArray (unsafeCoerce# x)) 0 (I# (sizeofByteArray# x)))++{-# INLINE unliftInteger #-}+unliftInteger :: LiftedInteger -> Integer+unliftInteger (LIS (I# x)) = S# x+unliftInteger (LIP (Array (MutByteArray x) _ _)) =+    Jp# (BN# (unsafeCoerce# x))+unliftInteger (LIN (Array (MutByteArray x) _ _)) =+    Jn# (BN# (unsafeCoerce# x))++#endif++instance Serialize Integer where+    {-# INLINE addSizeTo #-}+    addSizeTo i a = addSizeTo i (liftInteger a)++    {-# INLINE deserializeAt #-}+    deserializeAt off arr end =+        fmap unliftInteger <$> deserializeAt off arr end++    {-# INLINE serializeAt #-}+    serializeAt off arr val = serializeAt off arr (liftInteger val)
+ src/Streamly/Internal/Data/MutByteArray/Type.hs view
@@ -0,0 +1,286 @@+{-# LANGUAGE UnboxedTuples #-}++-- |+-- Module      : Streamly.Internal.Data.MutByteArray.Type+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.MutByteArray.Type+    (+    -- ** MutByteArray+      MutByteArray(..)+    , MutableByteArray+    , getMutableByteArray#++    -- ** Pinning+    , PinnedState(..)+    , isPinned+    , pin+    , unpin++    -- ** Allocation+    , nil+    , newBytesAs+    , new+    , pinnedNew+    , pinnedNewAlignedBytes++    -- ** Access+    , sizeOfMutableByteArray+    , putSliceUnsafe+    , cloneSliceUnsafeAs+    , cloneSliceUnsafe+    , pinnedCloneSliceUnsafe+    , asPtrUnsafe+    ) where++import Control.Monad.IO.Class (MonadIO(..))+#ifdef DEBUG+import Control.Monad (when)+import Debug.Trace (trace)+#endif+import GHC.Base (IO(..))+import System.IO.Unsafe (unsafePerformIO)++import GHC.Exts++--------------------------------------------------------------------------------+-- The ArrayContents type+--------------------------------------------------------------------------------++data PinnedState+    = Pinned+    | Unpinned++-- XXX can use UnliftedNewtypes++-- | A lifted mutable byte array type wrapping @MutableByteArray# RealWorld@.+-- This is a low level array used to back high level unboxed arrays and+-- serialized data.+data MutByteArray = MutByteArray (MutableByteArray# RealWorld)++{-# DEPRECATED MutableByteArray "Please use MutByteArray instead" #-}+type MutableByteArray = MutByteArray++{-# INLINE getMutableByteArray# #-}+getMutableByteArray# :: MutByteArray -> MutableByteArray# RealWorld+getMutableByteArray# (MutByteArray mbarr) = mbarr++-- | Return the size of the array in bytes.+{-# INLINE sizeOfMutableByteArray #-}+sizeOfMutableByteArray :: MutByteArray -> IO Int+sizeOfMutableByteArray (MutByteArray arr) =+    IO $ \s ->+        case getSizeofMutableByteArray# arr s of+            (# s1, i #) -> (# s1, I# i #)++{-# INLINE touch #-}+touch :: MutByteArray -> IO ()+touch (MutByteArray contents) =+    IO $ \s -> case touch# contents s of s' -> (# s', () #)++-- XXX We can provide another API for "unsafe" FFI calls passing an unlifted+-- pointer to the FFI call. For unsafe calls we do not need to pin the array.+-- We can pass an unlifted pointer to the FFI routine to avoid GC kicking in+-- before the pointer is wrapped.+--+-- From the GHC manual:+--+-- GHC, since version 8.4, guarantees that garbage collection will never occur+-- during an unsafe call, even in the bytecode interpreter, and further+-- guarantees that unsafe calls will be performed in the calling thread. Making+-- it safe to pass heap-allocated objects to unsafe functions.++-- | Use a @MutByteArray@ as @Ptr a@. This is useful when we want to pass+-- an array as a pointer to some operating system call or to a "safe" FFI call.+--+-- If the array is not pinned it is copied to pinned memory before passing it+-- to the monadic action.+--+-- /Performance Notes:/ Forces a copy if the array is not pinned. It is advised+-- that the programmer keeps this in mind and creates a pinned array+-- opportunistically before this operation occurs, to avoid the cost of a copy+-- if possible.+--+-- /Unsafe/ because of direct pointer operations. The user must ensure that+-- they are writing within the legal bounds of the array.+--+-- /Pre-release/+--+{-# INLINE asPtrUnsafe #-}+asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b+asPtrUnsafe arr f = do+  contents <- liftIO $ pin arr+  let !ptr = Ptr (byteArrayContents#+                     (unsafeCoerce# (getMutableByteArray# contents)))+  r <- f ptr+  liftIO $ touch contents+  return r++--------------------------------------------------------------------------------+-- Creation+--------------------------------------------------------------------------------++{-# NOINLINE nil #-}+nil :: MutByteArray+nil = unsafePerformIO $ new 0++{-# INLINE new #-}+new :: Int -> IO MutByteArray+new nbytes | nbytes < 0 =+  errorWithoutStackTrace "newByteArray: size must be >= 0"+new (I# nbytes) = IO $ \s ->+    case newByteArray# nbytes s of+        (# s', mbarr# #) ->+           let c = MutByteArray mbarr#+            in (# s', c #)++{-# INLINE pinnedNew #-}+pinnedNew :: Int -> IO MutByteArray+pinnedNew nbytes | nbytes < 0 =+  errorWithoutStackTrace "pinnedNewByteArray: size must be >= 0"+pinnedNew (I# nbytes) = IO $ \s ->+    case newPinnedByteArray# nbytes s of+        (# s', mbarr# #) ->+           let c = MutByteArray mbarr#+            in (# s', c #)++{-# INLINE pinnedNewAlignedBytes #-}+pinnedNewAlignedBytes :: Int -> Int -> IO MutByteArray+pinnedNewAlignedBytes nbytes _align | nbytes < 0 =+  errorWithoutStackTrace "pinnedNewAlignedBytes: size must be >= 0"+pinnedNewAlignedBytes (I# nbytes) (I# align) = IO $ \s ->+    case newAlignedPinnedByteArray# nbytes align s of+        (# s', mbarr# #) ->+           let c = MutByteArray mbarr#+            in (# s', c #)++{-# INLINE newBytesAs #-}+newBytesAs :: PinnedState -> Int -> IO MutByteArray+newBytesAs Unpinned = new+newBytesAs Pinned = pinnedNew++-------------------------------------------------------------------------------+-- Copying+-------------------------------------------------------------------------------++-- | Put a sub range of a source array into a subrange of a destination array.+-- This is not safe as it does not check the bounds of neither the src array+-- nor the destination array.+{-# INLINE putSliceUnsafe #-}+putSliceUnsafe ::+       MonadIO m+    => MutByteArray+    -> Int+    -> MutByteArray+    -> Int+    -> Int+    -> m ()+putSliceUnsafe src srcStartBytes dst dstStartBytes lenBytes = liftIO $ do+#ifdef DEBUG+    srcLen <- sizeOfMutableByteArray src+    dstLen <- sizeOfMutableByteArray dst+    when (srcLen - srcStartBytes < lenBytes)+        $ error $ "putSliceUnsafe: src overflow: start" ++ show srcStartBytes+            ++ " end " ++ show srcLen ++ " len " ++ show lenBytes+    when (dstLen - dstStartBytes < lenBytes)+        $ error $ "putSliceUnsafe: dst overflow: start" ++ show dstStartBytes+            ++ " end " ++ show dstLen ++ " len " ++ show lenBytes+#endif+    let !(I# srcStartBytes#) = srcStartBytes+        !(I# dstStartBytes#) = dstStartBytes+        !(I# lenBytes#) = lenBytes+    let arrS# = getMutableByteArray# src+        arrD# = getMutableByteArray# dst+    IO $ \s# -> (# copyMutableByteArray#+                    arrS# srcStartBytes# arrD# dstStartBytes# lenBytes# s#+                , () #)++-- | Unsafe as it does not check whether the start offset and length supplied+-- are valid inside the array.+{-# INLINE cloneSliceUnsafeAs #-}+cloneSliceUnsafeAs :: MonadIO m =>+    PinnedState -> Int -> Int -> MutByteArray -> m MutByteArray+cloneSliceUnsafeAs ps srcOff srcLen src =+    liftIO $ do+        mba <- newBytesAs ps srcLen+        putSliceUnsafe src srcOff mba 0 srcLen+        return mba++-- | @cloneSliceUnsafe offset len arr@ clones a slice of the supplied array+-- starting at the given offset and equal to the given length.+{-# INLINE cloneSliceUnsafe #-}+cloneSliceUnsafe :: MonadIO m => Int -> Int -> MutByteArray -> m MutByteArray+cloneSliceUnsafe = cloneSliceUnsafeAs Unpinned++-- | @pinnedCloneSliceUnsafe offset len arr@+{-# INLINE pinnedCloneSliceUnsafe #-}+pinnedCloneSliceUnsafe :: MonadIO m =>+    Int -> Int -> MutByteArray -> m MutByteArray+pinnedCloneSliceUnsafe = cloneSliceUnsafeAs Pinned++-------------------------------------------------------------------------------+-- Pinning & Unpinning+-------------------------------------------------------------------------------++-- | Return 'True' if the array is allocated in pinned memory.+{-# INLINE isPinned #-}+isPinned :: MutByteArray -> Bool+isPinned (MutByteArray arr#) =+    let pinnedInt = I# (isMutableByteArrayPinned# arr#)+     in pinnedInt /= 0+++{-# INLINE cloneMutableArrayWith# #-}+cloneMutableArrayWith#+    :: (Int# -> State# RealWorld -> (# State# RealWorld+                                     , MutableByteArray# RealWorld #))+    -> MutableByteArray# RealWorld+    -> State# RealWorld+    -> (# State# RealWorld, MutableByteArray# RealWorld #)+cloneMutableArrayWith# alloc# arr# s# =+    case getSizeofMutableByteArray# arr# s# of+        (# s1#, i# #) ->+            case alloc# i# s1# of+                (# s2#, arr1# #) ->+                    case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of+                        s3# -> (# s3#, arr1# #)++-- | Return a copy of the array in pinned memory if unpinned, else return the+-- original array.+{-# INLINE pin #-}+pin :: MutByteArray -> IO MutByteArray+pin arr@(MutByteArray marr#) =+    if isPinned arr+    then return arr+    else+#ifdef DEBUG+      do+        -- XXX dump stack trace+        trace ("pin: Copying array") (return ())+#endif+        IO+             $ \s# ->+                   case cloneMutableArrayWith# newPinnedByteArray# marr# s# of+                       (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)++-- | Return a copy of the array in unpinned memory if pinned, else return the+-- original array.+{-# INLINE unpin #-}+unpin :: MutByteArray -> IO MutByteArray+unpin arr@(MutByteArray marr#) =+    if not (isPinned arr)+    then return arr+    else+#ifdef DEBUG+      do+        -- XXX dump stack trace+        trace ("unpin: Copying array") (return ())+#endif+        IO+             $ \s# ->+                   case cloneMutableArrayWith# newByteArray# marr# s# of+                       (# s1#, marr1# #) -> (# s1#, MutByteArray marr1# #)
src/Streamly/Internal/Data/Parser.hs view
@@ -1,14 +1,3621 @@--- |--- Module      : Streamly.Internal.Data.Parser--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Parser-    ( module Streamly.Internal.Data.Parser.ParserD-    )-where--import Streamly.Internal.Data.Parser.ParserD+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Parser+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC++module Streamly.Internal.Data.Parser+    (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup++      module Streamly.Internal.Data.Parser.Type+    --, module Streamly.Internal.Data.Parser.Tee++    -- * Types+    , Parser (..)+    , ParseError (..)+    , Step (..)+    , Initial (..)++    -- * Downgrade to Fold+    , toFold++    -- First order parsers+    -- * Accumulators+    , fromFold+    , fromFoldMaybe++    -- * Map on input+    , postscan++    -- * Element parsers+    , peek++    -- All of these can be expressed in terms of either+    , one+    , oneEq+    , oneNotEq+    , oneOf+    , noneOf+    , eof+    , satisfy+    , maybe+    , either++    -- * Sequence parsers (tokenizers)+    --+    -- | Parsers chained in series, if one parser terminates the composition+    -- terminates. Currently we are using folds to collect the output of the+    -- parsers but we can use Parsers instead of folds to make the composition+    -- more powerful. For example, we can do:+    --+    -- takeEndByOrMax cond n p = takeEndBy cond (take n p)+    -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)+    -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)+    , lookAhead++    -- ** By length+    -- | Grab a sequence of input elements without inspecting them+    , takeBetween+    -- , take -- takeBetween 0 n+    , takeEQ -- takeBetween n n+    , takeGE -- takeBetween n maxBound+    -- , takeGE1 -- take1 -- takeBetween 1 n+    , takeP++    -- Grab a sequence of input elements by inspecting them+    -- ** Exact match+    , listEq+    , listEqBy+    , streamEqBy+    , subsequenceBy++    -- ** By predicate+    , takeWhile+    , takeWhileP+    , takeWhile1+    , dropWhile++    -- ** Separated by elements+    -- | Separator could be in prefix postion ('takeStartBy'), or suffix+    -- position ('takeEndBy'). See 'deintercalate', 'sepBy' etc for infix+    -- separator parsing, also see 'intersperseQuotedBy' fold.++    -- These can be implemented modularly with refolds, using takeWhile and+    -- satisfy.+    , takeEndBy+    , takeEndBy_+    , takeEndByEsc+    -- , takeEndByEsc_+    , takeStartBy+    , takeStartBy_+    , takeEitherSepBy+    , wordBy++    -- ** Grouped by element comparison+    , groupBy+    , groupByRolling+    , groupByRollingEither++    -- ** Framed by elements+    -- | Also see 'intersperseQuotedBy' fold.+    -- Framed by a one or more ocurrences of a separator around a word like+    -- spaces or quotes. No nesting.+    , wordFramedBy -- XXX Remove this? Covered by wordWithQuotes?+    , wordWithQuotes+    , wordKeepQuotes+    , wordProcessQuotes++    -- Framed by separate start and end characters, potentially nested.+    -- blockWithQuotes allows quotes inside a block. However,+    -- takeFramedByGeneric can be used to express takeStartBy, takeEndBy and+    -- block with escaping.+    -- , takeFramedBy+    , takeFramedBy_+    , takeFramedByEsc_+    , takeFramedByGeneric+    , blockWithQuotes++    -- Matching strings+    -- , prefixOf -- match any prefix of a given string+    -- , suffixOf -- match any suffix of a given string+    -- , infixOf -- match any substring of a given string++    -- ** Spanning+    , span+    , spanBy+    , spanByRolling++    -- Second order parsers (parsers using parsers)+    -- * Binary Combinators+    {-+    -- ** Parallel Applicatives+    , teeWith+    , teeWithFst+    , teeWithMin+    -- , teeTill -- like manyTill but parallel+    -}++    {-+    -- ** Parallel Alternatives+    , shortest+    , longest+    -- , fastest+    -}++    -- * N-ary Combinators+    -- ** Sequential Collection+    , sequence++    -- ** Sequential Repetition+    , count+    , countBetween+    -- , countBetweenTill+    , manyP+    , many+    , some++    -- ** Interleaved Repetition+    -- Use two folds, run a primary parser, its rejected values go to the+    -- secondary parser.+    , deintercalate+    , deintercalate1+    , deintercalateAll+    -- , deintercalatePrefix+    -- , deintercalateSuffix++    -- *** Special cases+    -- | TODO: traditional implmentations of these may be of limited use. For+    -- example, consider parsing lines separated by @\\r\\n@. The main parser+    -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we+    -- can apply the "sep" parser.+    --+    -- We can instead implement these as special cases of deintercalate.+    --+    -- @+    -- , endBy+    -- , sepEndBy+    -- , beginBy+    -- , sepBeginBy+    -- , sepAroundBy+    -- @+    , sepBy1+    , sepBy+    , sepByAll++    , manyTillP+    , manyTill+    , manyThen++    -- -- * Distribution+    --+    -- A simple and stupid impl would be to just convert the stream to an array+    -- and give the array reference to all consumers. The array can be grown on+    -- demand by any consumer and truncated when nonbody needs it.+    --+    -- -- ** Distribute to collection+    -- -- ** Distribute to repetition++    -- ** Interleaved collection+    -- |+    --+    -- 1. Round robin+    -- 2. Priority based+    , roundRobin++    -- -- ** Interleaved repetition+    -- repeat one parser and when it fails run an error recovery parser+    -- e.g. to find a key frame in the stream after an error++    -- ** Collection of Alternatives+    -- | Unimplemented+    --+    -- @+    -- , shortestN+    -- , longestN+    -- , fastestN -- first N successful in time+    -- , choiceN  -- first N successful in position+    -- @+    -- , choice   -- first successful in position++    -- ** Repeated Alternatives+    , retryMaxTotal+    , retryMaxSuccessive+    , retry++    -- ** Zipping Input+    , zipWithM+    , zip+    , indexed+    , makeIndexFilter+    , sampleFromthen++     -- * Deprecated+    , next+    )+where++#include "inline.hs"+#include "assert.hs"++import Control.Monad (when)+import Data.Bifunctor (first)+import Fusion.Plugin.Types (Fuse(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.SVar.Type (defState)+import Streamly.Internal.Data.Either.Strict (Either'(..))+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))+import Streamly.Internal.Data.Stream.Type (Stream)++import qualified Data.Foldable as Foldable+import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.Stream.Generate as D++import Streamly.Internal.Data.Parser.Type+--import Streamly.Internal.Data.Parser.Tee -- It's empty++import Prelude hiding+       (any, all, take, takeWhile, sequence, concatMap, maybe, either, span+       , zip, filter, dropWhile)++#include "DocTestDataParser.hs"++-------------------------------------------------------------------------------+-- Downgrade a parser to a Fold+-------------------------------------------------------------------------------++-- | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the+-- parser fails or tries to backtrack.+--+-- This can be useful in combinators that accept a Fold and we know that a+-- Parser cannot fail or failure exception is acceptable as there is no way to+-- recover.+--+-- /Pre-release/+--+{-# INLINE toFold #-}+toFold :: Monad m => Parser a m b -> Fold m a b+toFold (Parser pstep pinitial pextract) = Fold step initial extract final++    where++    initial = do+        r <- pinitial+        case r of+            IPartial s -> return $ FL.Partial s+            IDone b -> return $ FL.Done b+            IError err ->+                error $ "toFold: parser throws error in initial" ++ err++    perror n = error $ "toFold: parser backtracks in Partial: " ++ show n+    cerror n = error $ "toFold: parser backtracks in Continue: " ++ show n+    derror n = error $ "toFold: parser backtracks in Done: " ++ show n+    eerror err = error $ "toFold: parser throws error: " ++ err++    step st a = do+        r <- pstep st a+        case r of+            Partial 0 s -> return $ FL.Partial s+            Continue 0 s -> return $ FL.Partial s+            Done 0 b -> return $ FL.Done b+            Partial n _ -> perror n+            Continue n _ -> cerror n+            Done n _ -> derror n+            Error err -> eerror err++    extract = error "toFold: parser cannot be used for scanning"++    final st = do+        r <- pextract st+        case r of+            Done 0 b -> return b+            Partial n _ -> perror n+            Continue n _ -> cerror n+            Done n _ -> derror n+            Error err -> eerror err++-------------------------------------------------------------------------------+-- Upgrade folds to parses+-------------------------------------------------------------------------------++-- | Make a 'Parser' from a 'Fold'. This parser sends all of its input to the+-- fold.+--+{-# INLINE fromFold #-}+fromFold :: Monad m => Fold m a b -> Parser a m b+fromFold (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    initial = do+        res <- finitial+        return+            $ case res of+                  FL.Partial s1 -> IPartial s1+                  FL.Done b -> IDone b++    step s a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Partial s1 -> Partial 0 s1+                  FL.Done b -> Done 0 b++    extract = fmap (Done 0) . ffinal++-- | Convert a Maybe returning fold to an error returning parser. The first+-- argument is the error message that the parser would return when the fold+-- returns Nothing.+--+-- /Pre-release/+--+{-# INLINE fromFoldMaybe #-}+fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b+fromFoldMaybe errMsg (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        res <- finitial+        return+            $ case res of+                  FL.Partial s1 -> IPartial s1+                  FL.Done b ->+                        case b of+                            Just x -> IDone x+                            Nothing -> IError errMsg++    step s a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Partial s1 -> Partial 0 s1+                  FL.Done b ->+                        case b of+                            Just x -> Done 0 x+                            Nothing -> Error errMsg++    extract s = do+        res <- ffinal s+        case res of+            Just x -> return $ Done 0 x+            Nothing -> return $ Error errMsg++-------------------------------------------------------------------------------+-- Failing Parsers+-------------------------------------------------------------------------------++-- | Peek the head element of a stream, without consuming it. Fails if it+-- encounters end of input.+--+-- >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]+-- Right (1,1)+--+-- @+-- peek = lookAhead (satisfy True)+-- @+--+{-# INLINE peek #-}+peek :: Monad m => Parser a m a+peek = Parser step initial extract++    where++    initial = return $ IPartial ()++    step () a = return $ Done 1 a++    extract () = return $ Error "peek: end of input"++-- | Succeeds if we are at the end of input, fails otherwise.+--+-- >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]+-- Right (1,())+--+{-# INLINE eof #-}+eof :: Monad m => Parser a m ()+eof = Parser step initial extract++    where++    initial = return $ IPartial ()++    step () _ = return $ Error "eof: not at end of input"++    extract () = return $ Done 0 ()++-- | Return the next element of the input. Returns 'Nothing'+-- on end of input. Also known as 'head'.+--+-- /Pre-release/+--+{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}+{-# INLINE next #-}+next :: Monad m => Parser a m (Maybe a)+next = Parser step initial extract++  where++  initial = pure $ IPartial ()++  step () a = pure $ Done 0 (Just a)++  extract () = pure $ Done 0 Nothing++-- | Map an 'Either' returning function on the next element in the stream.  If+-- the function returns 'Left err', the parser fails with the error message+-- @err@ otherwise returns the 'Right' value.+--+-- /Pre-release/+--+{-# INLINE either #-}+either :: Monad m => (a -> Either String b) -> Parser a m b+either f = Parser step initial extract++    where++    initial = return $ IPartial ()++    step () a = return $+        case f a of+            Right b -> Done 0 b+            Left err -> Error err++    extract () = return $ Error "end of input"++-- | Map a 'Maybe' returning function on the next element in the stream. The+-- parser fails if the function returns 'Nothing' otherwise returns the 'Just'+-- value.+--+-- >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right+-- >>> maybe f = Parser.either (toEither . f)+--+-- >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)+--+-- /Pre-release/+--+{-# INLINE maybe #-}+maybe :: Monad m => (a -> Maybe b) -> Parser a m b+-- maybe f = either (Maybe.maybe (Left "maybe: predicate failed") Right . f)+maybe parserF = Parser step initial extract++    where++    initial = return $ IPartial ()++    step () a = return $+        case parserF a of+            Just b -> Done 0 b+            Nothing -> Error "maybe: predicate failed"++    extract () = return $ Error "maybe: end of input"++-- | Returns the next element if it passes the predicate, fails otherwise.+--+-- >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]+-- Right 1+--+-- >>> toMaybe f x = if f x then Just x else Nothing+-- >>> satisfy f = Parser.maybe (toMaybe f)+--+{-# INLINE satisfy #-}+satisfy :: Monad m => (a -> Bool) -> Parser a m a+-- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)+satisfy predicate = Parser step initial extract++    where++    initial = return $ IPartial ()++    step () a = return $+        if predicate a+        then Done 0 a+        else Error "satisfy: predicate failed"++    extract () = return $ Error "satisfy: end of input"++-- | Consume one element from the head of the stream.  Fails if it encounters+-- end of input.+--+-- >>> one = Parser.satisfy $ const True+--+{-# INLINE one #-}+one :: Monad m => Parser a m a+one = satisfy $ const True++-- Alternate names: "only", "onlyThis".++-- | Match a specific element.+--+-- >>> oneEq x = Parser.satisfy (== x)+--+{-# INLINE oneEq #-}+oneEq :: (Monad m, Eq a) => a -> Parser a m a+oneEq x = satisfy (== x)++-- Alternate names: "exclude", "notThis".++-- | Match anything other than the supplied element.+--+-- >>> oneNotEq x = Parser.satisfy (/= x)+--+{-# INLINE oneNotEq #-}+oneNotEq :: (Monad m, Eq a) => a -> Parser a m a+oneNotEq x = satisfy (/= x)++-- | Match any one of the elements in the supplied list.+--+-- >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)+--+-- When performance matters a pattern matching predicate could be more+-- efficient than a 'Foldable' datatype:+--+-- @+-- let p x =+--    case x of+--       'a' -> True+--       'e' -> True+--        _  -> False+-- in satisfy p+-- @+--+-- GHC may use a binary search instead of linear search in the list.+-- Alternatively, you can also use an array instead of list for storage and+-- search.+--+{-# INLINE oneOf #-}+oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a+oneOf xs = satisfy (`Foldable.elem` xs)++-- | See performance notes in 'oneOf'.+--+-- >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)+--+{-# INLINE noneOf #-}+noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a+noneOf xs = satisfy (`Foldable.notElem` xs)++-------------------------------------------------------------------------------+-- Taking elements+-------------------------------------------------------------------------------++-- Required to fuse "take" with "many" in "chunksOf", for ghc-9.x+{-# ANN type Tuple'Fused Fuse #-}+data Tuple'Fused a b = Tuple'Fused !a !b deriving Show++-- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input+-- elements and folds them using the supplied fold.+--+-- Stops after @n@ elements.+-- Fails if the stream ends before @m@ elements could be taken.+--+-- Examples: -+--+-- @+-- >>> :{+--   takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)+--     where prsr = Parser.takeBetween low high Fold.toList+-- :}+--+-- @+--+-- >>> takeBetween' 2 4 [1, 2, 3, 4, 5]+-- Right [1,2,3,4]+--+-- >>> takeBetween' 2 4 [1, 2]+-- Right [1,2]+--+-- >>> takeBetween' 2 4 [1]+-- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")+--+-- >>> takeBetween' 0 0 [1, 2]+-- Right []+--+-- >>> takeBetween' 0 1 []+-- Right []+--+-- @takeBetween@ is the most general take operation, other take operations can+-- be defined in terms of takeBetween. For example:+--+-- >>> take n = Parser.takeBetween 0 n+-- >>> takeEQ n = Parser.takeBetween n n+-- >>> takeGE n = Parser.takeBetween n maxBound+--+-- /Pre-release/+--+{-# INLINE takeBetween #-}+takeBetween :: Monad m => Int -> Int -> Fold m a b -> Parser a m b+takeBetween low high (Fold fstep finitial _ ffinal) =++    Parser step initial (extract streamErr)++    where++    streamErr i =+           "takeBetween: Expecting alteast " ++ show low+        ++ " elements, got " ++ show i++    invalidRange =+        "takeBetween: lower bound - " ++ show low+            ++ " is greater than higher bound - " ++ show high++    foldErr :: Int -> String+    foldErr i =+        "takeBetween: the collecting fold terminated after"+            ++ " consuming" ++ show i ++ " elements"+            ++ " minimum" ++ show low ++ " elements needed"++    -- Exactly the same as snext except different constructors, we can possibly+    -- deduplicate the two.+    {-# INLINE inext #-}+    inext i res =+        let i1 = i + 1+        in case res of+            FL.Partial s -> do+                let s1 = Tuple'Fused i1 s+                if i1 < high+                -- XXX ideally this should be a Continue instead+                then return $ IPartial s1+                else iextract foldErr s1+            FL.Done b ->+                return+                    $ if i1 >= low+                      then IDone b+                      else IError (foldErr i1)++    initial = do+        when (low >= 0 && high >= 0 && low > high)+            $ error invalidRange++        finitial >>= inext (-1)++    -- Keep the impl same as inext+    {-# INLINE snext #-}+    snext i res =+        let i1 = i + 1+        in case res of+            FL.Partial s -> do+                let s1 = Tuple'Fused i1 s+                if i1 < low+                then return $ Continue 0 s1+                else if i1 < high+                then return $ Partial 0 s1+                else fmap (Done 0) (ffinal s)+            FL.Done b ->+                return+                    $ if i1 >= low+                      then Done 0 b+                      else Error (foldErr i1)++    step (Tuple'Fused i s) a = fstep s a >>= snext i++    extract f (Tuple'Fused i s)+        | i >= low && i <= high = fmap (Done 0) (ffinal s)+        | otherwise = return $ Error (f i)++    -- XXX Need to make Initial return type Step to deduplicate this+    iextract f (Tuple'Fused i s)+        | i >= low && i <= high = fmap IDone (ffinal s)+        | otherwise = return $ IError (f i)++-- | Stops after taking exactly @n@ input elements.+--+-- * Stops - after consuming @n@ elements.+-- * Fails - if the stream or the collecting fold ends before it can collect+--           exactly @n@ elements.+--+-- >>> Stream.parse (Parser.takeEQ 2 Fold.toList) $ Stream.fromList [1,0,1]+-- Right [1,0]+--+-- >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]+-- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")+--+{-# INLINE takeEQ #-}+takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b+takeEQ n (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    initial = do+        res <- finitial+        case res of+            FL.Partial s ->+                if n > 0+                then return $ IPartial $ Tuple'Fused 1 s+                else fmap IDone (ffinal s)+            FL.Done b -> return $+                if n > 0+                then IError+                         $ "takeEQ: Expecting exactly " ++ show n+                             ++ " elements, fold terminated without"+                             ++ " consuming any elements"+                else IDone b++    step (Tuple'Fused i1 r) a = do+        res <- fstep r a+        if n > i1+        then+            return+                $ case res of+                    FL.Partial s -> Continue 0 $ Tuple'Fused (i1 + 1) s+                    FL.Done _ ->+                        Error+                            $ "takeEQ: Expecting exactly " ++ show n+                                ++ " elements, fold terminated on " ++ show i1+        else+            -- assert (n == i1)+            Done 0+                <$> case res of+                        FL.Partial s -> ffinal s+                        FL.Done b -> return b++    extract (Tuple'Fused i _) =+        -- Using the count "i" in the message below causes large performance+        -- regression unless we use Fuse annotation on Tuple.+        return+            $ Error+            $ "takeEQ: Expecting exactly " ++ show n+                ++ " elements, input terminated on " ++ show (i - 1)++{-# ANN type TakeGEState Fuse #-}+data TakeGEState s =+      TakeGELT !Int !s+    | TakeGEGE !s++-- | Take at least @n@ input elements, but can collect more.+--+-- * Stops - when the collecting fold stops.+-- * Fails - if the stream or the collecting fold ends before producing @n@+--           elements.+--+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]+-- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")+--+-- >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]+-- Right [1,0,1,0,1]+--+-- /Pre-release/+--+{-# INLINE takeGE #-}+takeGE :: Monad m => Int -> Fold m a b -> Parser a m b+takeGE n (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    initial = do+        res <- finitial+        case res of+            FL.Partial s ->+                if n > 0+                then return $ IPartial $ TakeGELT 1 s+                else return $ IPartial $ TakeGEGE s+            FL.Done b -> return $+                if n > 0+                then IError+                         $ "takeGE: Expecting at least " ++ show n+                             ++ " elements, fold terminated without"+                             ++ " consuming any elements"+                else IDone b++    step (TakeGELT i1 r) a = do+        res <- fstep r a+        if n > i1+        then+            return+                $ case res of+                      FL.Partial s -> Continue 0 $ TakeGELT (i1 + 1) s+                      FL.Done _ ->+                        Error+                            $ "takeGE: Expecting at least " ++ show n+                                ++ " elements, fold terminated on " ++ show i1+        else+            -- assert (n <= i1)+            return+                $ case res of+                      FL.Partial s -> Partial 0 $ TakeGEGE s+                      FL.Done b -> Done 0 b+    step (TakeGEGE r) a = do+        res <- fstep r a+        return+            $ case res of+                  FL.Partial s -> Partial 0 $ TakeGEGE s+                  FL.Done b -> Done 0 b++    extract (TakeGELT i _) =+        return+            $ Error+            $ "takeGE: Expecting at least " ++ show n+                ++ " elements, input terminated on " ++ show (i - 1)+    extract (TakeGEGE r) = fmap (Done 0) $ ffinal r++-------------------------------------------------------------------------------+-- Conditional splitting+-------------------------------------------------------------------------------++-- XXX We should perhaps use only takeWhileP and rename it to takeWhile.++-- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the+-- input. The combinator stops when the condition fails or if the collecting+-- parser stops.+--+-- Other interesting parsers can be implemented in terms of this parser:+--+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)+-- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)+--+-- Stops: when the condition fails or the collecting parser stops.+-- Fails: when the collecting parser fails.+--+-- /Pre-release/+--+{-# INLINE takeWhileP #-}+takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+takeWhileP predicate (Parser pstep pinitial pextract) =+    Parser step pinitial pextract++    where++    step s a =+        if predicate a+        then pstep s a+        else do+            r <- pextract s+            -- XXX need a map on count+            case r of+                Error err -> return $ Error err+                Done n s1 -> return $ Done (n + 1) s1+                Partial _ _ -> error "Bug: takeWhileP: Partial in extract"+                Continue n s1 -> return $ Continue (n + 1) s1++-- | Collect stream elements until an element fails the predicate. The element+-- on which the predicate fails is returned back to the input stream.+--+-- * Stops - when the predicate fails or the collecting fold stops.+-- * Fails - never.+--+-- >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]+-- Right [0,0]+--+-- >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)+--+-- We can implement a @breakOn@ using 'takeWhile':+--+-- @+-- breakOn p = takeWhile (not p)+-- @+--+{-# INLINE takeWhile #-}+takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+-- takeWhile cond f = takeWhileP cond (fromFold f)+takeWhile predicate (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        res <- finitial+        return $ case res of+            FL.Partial s -> IPartial s+            FL.Done b -> IDone b++    step s a =+        if predicate a+        then do+            fres <- fstep s a+            return+                $ case fres of+                      FL.Partial s1 -> Partial 0 s1+                      FL.Done b -> Done 0 b+        else Done 1 <$> ffinal s++    extract s = fmap (Done 0) (ffinal s)++{-+-- XXX This may not be composable because of the b argument. We can instead+-- return a "Reparse b a m b" so that those can be composed.+{-# INLINE takeWhile1X #-}+takeWhile1 :: Monad m => b -> (a -> Bool) -> Refold m b a b -> Parser a m b+-- We can implement this using satisfy and takeWhile. We can use "satisfy+-- p", fold the result with the refold and then use the "takeWhile p" and+-- fold that using the refold.+takeWhile1 acc cond f = undefined+-}++-- | Like 'takeWhile' but takes at least one element otherwise fails.+--+-- >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)+--+{-# INLINE takeWhile1 #-}+takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+-- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)+takeWhile1 predicate (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        res <- finitial+        return $ case res of+            FL.Partial s -> IPartial (Left' s)+            FL.Done _ ->+                IError+                    $ "takeWhile1: fold terminated without consuming:"+                          ++ " any element"++    {-# INLINE process #-}+    process s a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Partial s1 -> Partial 0 (Right' s1)+                  FL.Done b -> Done 0 b++    step (Left' s) a =+        if predicate a+        then process s a+        else return $ Error "takeWhile1: predicate failed on first element"+    step (Right' s) a =+        if predicate a+        then process s a+        else do+            b <- ffinal s+            return $ Done 1 b++    extract (Left' _) = return $ Error "takeWhile1: end of input"+    extract (Right' s) = fmap (Done 0) (ffinal s)++-- | Drain the input as long as the predicate succeeds, running the effects and+-- discarding the results.+--+-- This is also called @skipWhile@ in some parsing libraries.+--+-- >>> dropWhile p = Parser.takeWhile p Fold.drain+--+{-# INLINE dropWhile #-}+dropWhile :: Monad m => (a -> Bool) -> Parser a m ()+dropWhile p = takeWhile p FL.drain++-------------------------------------------------------------------------------+-- Separators+-------------------------------------------------------------------------------++{-# ANN type FramedEscState Fuse #-}+data FramedEscState s =+    FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int++-- XXX We can remove Maybe from esc+{-# INLINE takeFramedByGeneric #-}+takeFramedByGeneric :: Monad m =>+       Maybe (a -> Bool) -- is escape char?+    -> Maybe (a -> Bool) -- is frame begin?+    -> Maybe (a -> Bool) -- is frame end?+    -> Fold m a b+    -> Parser a m b+takeFramedByGeneric esc begin end (Fold fstep finitial _ ffinal) =++    Parser step initial extract++    where++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (FrameEscInit s)+                FL.Done _ ->+                    error "takeFramedByGeneric: fold done without input"++    {-# INLINE process #-}+    process s a n = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)+                FL.Done b -> Done 0 b++    {-# INLINE processNoEsc #-}+    processNoEsc s a n =+        case end of+            Just isEnd ->+                case begin of+                    Just isBegin ->+                        -- takeFramedBy case+                        if isEnd a+                        then+                            if n == 0+                            then Done 0 <$> ffinal s+                            else process s a (n - 1)+                        else+                            let n1 = if isBegin a then n + 1 else n+                             in process s a n1+                    Nothing -> -- takeEndBy case+                        if isEnd a+                        then Done 0 <$> ffinal s+                        else process s a n+            Nothing -> -- takeStartBy case+                case begin of+                    Just isBegin ->+                        if isBegin a+                        then Done 0 <$> ffinal s+                        else process s a n+                    Nothing ->+                        error $ "takeFramedByGeneric: "+                            ++ "Both begin and end frame predicate missing"++    {-# INLINE processCheckEsc #-}+    processCheckEsc s a n =+        case esc of+            Just isEsc ->+                if isEsc a+                then return $ Partial 0 $ FrameEscEsc s n+                else processNoEsc s a n+            Nothing -> processNoEsc s a n++    step (FrameEscInit s) a =+        case begin of+            Just isBegin ->+                if isBegin a+                then return $ Partial 0 (FrameEscGo s 0)+                else return $ Error "takeFramedByGeneric: missing frame start"+            Nothing ->+                case end of+                    Just isEnd ->+                        if isEnd a+                        then Done 0 <$> ffinal s+                        else processCheckEsc s a 0+                    Nothing ->+                        error "Both begin and end frame predicate missing"+    step (FrameEscGo s n) a = processCheckEsc s a n+    step (FrameEscEsc s n) a = process s a n++    err = return . Error++    extract (FrameEscInit _) =+        err "takeFramedByGeneric: empty token"+    extract (FrameEscGo s _) =+        case begin of+            Just _ ->+                case end of+                    Nothing -> fmap (Done 0) $ ffinal s+                    Just _ -> err "takeFramedByGeneric: missing frame end"+            Nothing -> err "takeFramedByGeneric: missing closing frame"+    extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"++data BlockParseState s =+      BlockInit !s+    | BlockUnquoted !Int !s+    | BlockQuoted !Int !s+    | BlockQuotedEsc !Int !s++-- Blocks can be of different types e.g. {} or (). We only parse from the+-- perspective of the outermost block type. The nesting of that block are+-- checked. Any other block types nested inside it are opaque to us and can be+-- parsed when the contents of the block are parsed.++-- XXX Put a limit on nest level to keep the API safe.++-- | Parse a block enclosed within open, close brackets. Block contents may be+-- quoted, brackets inside quotes are ignored. Quoting characters can be used+-- within quotes if escaped. A block can have a nested block inside it.+--+-- Quote begin and end chars are the same. Block brackets and quote chars must+-- not overlap. Block start and end brackets must be different for nesting+-- blocks within blocks.+--+-- >>> p = Parser.blockWithQuotes (== '\\') (== '"') '{' '}' Fold.toList+-- >>> Stream.parse p $ Stream.fromList "{msg: \"hello world\"}"+-- Right "msg: \"hello world\""+--+{-# INLINE blockWithQuotes #-}+blockWithQuotes :: (Monad m, Eq a) =>+       (a -> Bool)  -- ^ escape char+    -> (a -> Bool)  -- ^ quote char, to quote inside brackets+    -> a  -- ^ Block opening bracket+    -> a  -- ^ Block closing bracket+    -> Fold m a b+    -> Parser a m b+blockWithQuotes isEsc isQuote bopen bclose+    (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (BlockInit s)+                FL.Done _ ->+                    error "blockWithQuotes: fold finished without input"++    {-# INLINE process #-}+    process s a nextState = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (nextState s1)+                FL.Done b -> Done 0 b++    step (BlockInit s) a =+        return+            $ if a == bopen+              then Continue 0 $ BlockUnquoted 1 s+              else Error "blockWithQuotes: missing block start"+    step (BlockUnquoted level s) a+        | a == bopen = process s a (BlockUnquoted (level + 1))+        | a == bclose =+            if level == 1+            then fmap (Done 0) (ffinal s)+            else process s a (BlockUnquoted (level - 1))+        | isQuote a = process s a (BlockQuoted level)+        | otherwise = process s a (BlockUnquoted level)+    step (BlockQuoted level s) a+        | isEsc a = process s a (BlockQuotedEsc level)+        | otherwise =+            if isQuote a+            then process s a (BlockUnquoted level)+            else process s a (BlockQuoted level)+    step (BlockQuotedEsc level s) a = process s a (BlockQuoted level)++    err = return . Error++    extract (BlockInit s) = fmap (Done 0) $ ffinal s+    extract (BlockUnquoted level _) =+        err $ "blockWithQuotes: finished at block nest level " ++ show level+    extract (BlockQuoted level _) =+        err $ "blockWithQuotes: finished, inside an unfinished quote, "+            ++ "at block nest level " ++ show level+    extract (BlockQuotedEsc level _) =+        err $ "blockWithQuotes: finished, inside an unfinished quote, "+            ++ "after an escape char, at block nest level " ++ show level++-- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by+-- the supplied predicate. The separator is also taken with the token.+--+-- This can be combined with other parsers to implement other interesting+-- parsers as follows:+--+-- >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)+-- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)+--+-- >>> takeEndBy = Parser.takeEndByEsc (const False)+--+-- See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting+-- parser in the takeEndBy parser can decide whether to fail or not if the+-- stream does not end with separator.+--+-- /Pre-release/+--+{-# INLINE takeEndBy #-}+takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+-- takeEndBy = takeEndByEsc (const False)+takeEndBy cond (Parser pstep pinitial pextract) =++    Parser step initial pextract++    where++    initial = pinitial++    step s a = do+        res <- pstep s a+        if not (cond a)+        then return res+        else extractStep pextract res++-- | Like 'takeEndBy' but the separator elements can be escaped using an+-- escape char determined by the first predicate. The escape characters are+-- removed.+--+-- /pre-release/+{-# INLINE takeEndByEsc #-}+takeEndByEsc :: Monad m =>+    (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b+takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =++    Parser step initial extract++    where++    initial = first Left' <$> pinitial++    step (Left' s) a = do+        if isEsc a+        then return $ Partial 0 $ Right' s+        else do+            res <- pstep s a+            if not (isSep a)+            then return $ first Left' res+            else fmap (first Left') $ extractStep pextract res++    step (Right' s) a = do+        res <- pstep s a+        return $ first Left' res++    extract (Left' s) = fmap (first Left') $ pextract s+    extract (Right' _) =+        return $ Error "takeEndByEsc: trailing escape"++-- | Like 'takeEndBy' but the separator is dropped.+--+-- See also "Streamly.Data.Fold.takeEndBy_".+--+-- /Pre-release/+--+{-# INLINE takeEndBy_ #-}+takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b+{-+takeEndBy_ isEnd p =+    takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)+-}+takeEndBy_ cond (Parser pstep pinitial pextract) =++    Parser step pinitial pextract++    where++    step s a =+        if cond a+        then pextract s+        else pstep s a++-- | Take either the separator or the token. Separator is a Left value and+-- token is Right value.+--+-- /Unimplemented/+{-# INLINE takeEitherSepBy #-}+takeEitherSepBy :: -- Monad m =>+    (a -> Bool) -> Fold m (Either a b) c -> Parser a m c+takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond++-- | Parse a token that starts with an element chosen by the predicate.  The+-- parser fails if the input does not start with the selected element.+--+-- * Stops - when the predicate succeeds in non-leading position.+-- * Fails - when the predicate fails in the leading position.+--+-- >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)+--+-- Examples: -+--+-- >>> p = Parser.takeStartBy (== ',') Fold.toList+-- >>> leadingComma = Stream.parse p . Stream.fromList+-- >>> leadingComma "a,b"+-- Left (ParseError "takeStartBy: missing frame start")+-- ...+-- >>> leadingComma ",,"+-- Right ","+-- >>> leadingComma ",a,b"+-- Right ",a"+-- >>> leadingComma ""+-- Right ""+--+-- /Pre-release/+--+{-# INLINE takeStartBy #-}+takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+takeStartBy cond (Fold fstep finitial _ ffinal) =++    Parser step initial extract++    where++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (Left' s)+                FL.Done _ -> IError "takeStartBy: fold done without input"++    {-# INLINE process #-}+    process s a = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Partial 0 (Right' s1)+                FL.Done b -> Done 0 b++    step (Left' s) a =+        if cond a+        then process s a+        else return $ Error "takeStartBy: missing frame start"+    step (Right' s) a =+        if not (cond a)+        then process s a+        else Done 1 <$> ffinal s++    extract (Left' s) = fmap (Done 0) $ ffinal s+    extract (Right' s) = fmap (Done 0) $ ffinal s++-- | Like 'takeStartBy' but drops the separator.+--+-- >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing+--+{-# INLINE takeStartBy_ #-}+takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing++-- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a+-- begin and end predicate, and an escape character. The frame begin and end+-- characters lose their special meaning if preceded by the escape character.+--+-- Nested frames are allowed if begin and end markers are different, nested+-- frames must be balanced unless escaped, nested frame markers are emitted as+-- it is.+--+-- For example,+--+-- >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList+-- >>> Stream.parse p $ Stream.fromList "{hello}"+-- Right "hello"+-- >>> Stream.parse p $ Stream.fromList "{hello {world}}"+-- Right "hello {world}"+-- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"+-- Right "hello {world"+-- >>> Stream.parse p $ Stream.fromList "{hello {world}"+-- Left (ParseError "takeFramedByEsc_: missing frame end")+--+-- /Pre-release/+{-# INLINE takeFramedByEsc_ #-}+takeFramedByEsc_ :: Monad m =>+    (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b+-- takeFramedByEsc_ isEsc isEnd p =+--    takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)+takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial _ ffinal ) =++    Parser step initial extract++    where++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (FrameEscInit s)+                FL.Done _ ->+                    error "takeFramedByEsc_: fold done without input"++    {-# INLINE process #-}+    process s a n = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)+                FL.Done b -> Done 0 b++    step (FrameEscInit s) a =+        if isBegin a+        then return $ Partial 0 (FrameEscGo s 0)+        else return $ Error "takeFramedByEsc_: missing frame start"+    step (FrameEscGo s n) a =+        if isEsc a+        then return $ Partial 0 $ FrameEscEsc s n+        else do+            if not (isEnd a)+            then+                let n1 = if isBegin a then n + 1 else n+                 in process s a n1+            else+                if n == 0+                then Done 0 <$> ffinal s+                else process s a (n - 1)+    step (FrameEscEsc s n) a = process s a n++    err = return . Error++    extract (FrameEscInit _) = err "takeFramedByEsc_: empty token"+    extract (FrameEscGo _ _) = err "takeFramedByEsc_: missing frame end"+    extract (FrameEscEsc _ _) = err "takeFramedByEsc_: trailing escape"++data FramedState s = FrameInit !s | FrameGo !s Int++-- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an+-- end predicate.+--+-- >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)+--+{-# INLINE takeFramedBy_ #-}+takeFramedBy_ :: Monad m =>+    (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b+-- takeFramedBy_ isBegin isEnd =+--    takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)+takeFramedBy_ isBegin isEnd (Fold fstep finitial _ ffinal) =++    Parser step initial extract++    where++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (FrameInit s)+                FL.Done _ ->+                    error "takeFramedBy_: fold done without input"++    {-# INLINE process #-}+    process s a n = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (FrameGo s1 n)+                FL.Done b -> Done 0 b++    step (FrameInit s) a =+        if isBegin a+        then return $ Continue 0 (FrameGo s 0)+        else return $ Error "takeFramedBy_: missing frame start"+    step (FrameGo s n) a+        | not (isEnd a) =+            let n1 = if isBegin a then n + 1 else n+             in process s a n1+        | n == 0 = Done 0 <$> ffinal s+        | otherwise = process s a (n - 1)++    err = return . Error++    extract (FrameInit _) = err "takeFramedBy_: empty token"+    extract (FrameGo _ _) = err "takeFramedBy_: missing frame end"++-------------------------------------------------------------------------------+-- Grouping and words+-------------------------------------------------------------------------------++data WordByState s b = WBLeft !s | WBWord !s | WBRight !b++-- Note we can also get words using something like:+-- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)+--+-- But that won't be as efficient and ergonomic.++-- | Like 'splitOn' but strips leading, trailing, and repeated separators.+-- Therefore, @".a..b."@ having '.' as the separator would be parsed as+-- @["a","b"]@.  In other words, its like parsing words from whitespace+-- separated text.+--+-- * Stops - when it finds a word separator after a non-word element+-- * Fails - never.+--+-- >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)+--+-- @+-- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)+-- @+--+{-# INLINE wordBy #-}+wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b+wordBy predicate (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    {-# INLINE worder #-}+    worder s a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Partial s1 -> Partial 0 $ WBWord s1+                  FL.Done b -> Done 0 b++    initial = do+        res <- finitial+        return+            $ case res of+                  FL.Partial s -> IPartial $ WBLeft s+                  FL.Done b -> IDone b++    step (WBLeft s) a =+        if not (predicate a)+        then worder s a+        else return $ Partial 0 $ WBLeft s+    step (WBWord s) a =+        if not (predicate a)+        then worder s a+        else do+            b <- ffinal s+            return $ Partial 0 $ WBRight b+    step (WBRight b) a =+        return+            $ if not (predicate a)+              then Done 1 b+              else Partial 0 $ WBRight b++    extract (WBLeft s) = fmap (Done 0) $ ffinal s+    extract (WBWord s) = fmap (Done 0) $ ffinal s+    extract (WBRight b) = return (Done 0 b)++data WordFramedState s b =+      WordFramedSkipPre !s+    | WordFramedWord !s !Int+    | WordFramedEsc !s !Int+    | WordFramedSkipPost !b++-- | Like 'wordBy' but treats anything inside a pair of quotes as a single+-- word, the quotes can be escaped by an escape character.  Recursive quotes+-- are possible if quote begin and end characters are different, quotes must be+-- balanced. Outermost quotes are stripped.+--+-- >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList+-- >>> Stream.parse braces $ Stream.fromList "{ab} cd"+-- Right "ab"+-- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"+-- Right "abcd"+-- >>> Stream.parse braces $ Stream.fromList "a{b} cd"+-- Right "ab"+-- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"+-- Right "a{b}"+--+-- >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList+-- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""+-- Right "ab"+--+{-# INLINE wordFramedBy #-}+wordFramedBy :: Monad m =>+       (a -> Bool)  -- ^ Matches escape elem?+    -> (a -> Bool)  -- ^ Matches left quote?+    -> (a -> Bool)  -- ^ matches right quote?+    -> (a -> Bool)  -- ^ matches word separator?+    -> Fold m a b+    -> Parser a m b+wordFramedBy isEsc isBegin isEnd isSep+    (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (WordFramedSkipPre s)+                FL.Done _ ->+                    error "wordFramedBy: fold done without input"++    {-# INLINE process #-}+    process s a n = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (WordFramedWord s1 n)+                FL.Done b -> Done 0 b++    step (WordFramedSkipPre s) a+        | isEsc a = return $ Continue 0 $ WordFramedEsc s 0+        | isSep a = return $ Partial 0 $ WordFramedSkipPre s+        | isBegin a = return $ Continue 0 $ WordFramedWord s 1+        | isEnd a =+            return $ Error "wordFramedBy: missing frame start"+        | otherwise = process s a 0+    step (WordFramedWord s n) a+        | isEsc a = return $ Continue 0 $ WordFramedEsc s n+        | n == 0 && isSep a = do+            b <- ffinal s+            return $ Partial 0 $ WordFramedSkipPost b+        | otherwise = do+            -- We need to use different order for checking begin and end for+            -- the n == 0 and n == 1 case so that when the begin and end+            -- character is the same we treat the one after begin as end.+            if n == 0+            then+               -- Need to check isBegin first+               if isBegin a+               then return $ Continue 0 $ WordFramedWord s 1+               else if isEnd a+                    then return $ Error "wordFramedBy: missing frame start"+                    else process s a n+            else+               -- Need to check isEnd first+                if isEnd a+                then+                   if n == 1+                   then return $ Continue 0 $ WordFramedWord s 0+                   else process s a (n - 1)+                else if isBegin a+                     then process s a (n + 1)+                     else process s a n+    step (WordFramedEsc s n) a = process s a n+    step (WordFramedSkipPost b) a =+        return+            $ if not (isSep a)+              then Done 1 b+              else Partial 0 $ WordFramedSkipPost b++    err = return . Error++    extract (WordFramedSkipPre s) = fmap (Done 0) $ ffinal s+    extract (WordFramedWord s n) =+        if n == 0+        then fmap (Done 0) $ ffinal s+        else err "wordFramedBy: missing frame end"+    extract (WordFramedEsc _ _) =+        err "wordFramedBy: trailing escape"+    extract (WordFramedSkipPost b) = return (Done 0 b)++data WordQuotedState s b a =+      WordQuotedSkipPre !s+    | WordUnquotedWord !s+    | WordQuotedWord !s !Int !a !a+    | WordUnquotedEsc !s+    | WordQuotedEsc !s !Int !a !a+    | WordQuotedSkipPost !b++-- | Quote and bracket aware word splitting with escaping. Like 'wordBy' but+-- word separators within specified quotes or brackets are ignored. Quotes and+-- escape characters can be processed. If the end quote is different from the+-- start quote it is called a bracket. The following quoting rules apply:+--+-- * In an unquoted string a character may be preceded by an escape character.+-- The escape character is removed and the character following it is treated+-- literally with no special meaning e.g. e.g. h\ e\ l\ l\ o is a single word,+-- \n is same as n.+-- * Any part of the word can be placed within quotes. Inside quotes all+-- characters are treated literally with no special meaning. Quoting character+-- itself cannot be used within quotes unless escape processing within quotes+-- is applied to allow it.+-- * Optionally escape processing for quoted part can be specified. Escape+-- character has no special meaning inside quotes unless it is followed by a+-- character that has a escape translation specified, in that case the escape+-- character is removed, and the specified translation is applied to the+-- character following it. This can be used to escape the quoting character+-- itself within quotes.+-- * There can be multiple quoting characters, when a quote starts, all other+-- quoting characters within that quote lose any special meaning until the+-- quote is closed.+-- * A starting quote char without an ending char generates a parse error. An+-- ending bracket char without a corresponding bracket begin is ignored.+-- * Brackets can be nested.+--+-- We should note that unquoted and quoted escape processing are different. In+-- unquoted part escape character is always removed. In quoted part it is+-- removed only if followed by a special meaning character. This is consistent+-- with how shell performs escape processing.++-- Examples of quotes - "double quotes", 'single quotes', (parens), {braces},+-- ((nested) brackets).+--+-- Example:+--+-- >>> :{+-- >>> q x =+-- >>>     case x of+-- >>>         '"' -> Just x+-- >>>         '\'' -> Just x+-- >>>         _ -> Nothing+-- >>> :}+--+-- >>> p = Parser.wordKeepQuotes (== '\\') q isSpace Fold.toList+-- >>> Stream.parse p $ Stream.fromList "a\"b'c\";'d\"e'f ghi"+-- Right "a\"b'c\";'d\"e'f"+--+-- Note that outer quotes and backslashes from the input string are consumed by+-- Haskell, therefore, the actual input string passed to the parser is:+-- a"b'c";'d"e'f ghi+--+-- Similarly, when printing, double quotes are escaped by Haskell.+--+-- Limitations:+--+-- Shell like quote processing can be performed by using quote char specific+-- escape processing, single quotes with no escapes, and double quotes with+-- escapes.+--+-- JSON string processing can also be achieved except the "\uXXXX" style+-- escaping for Unicode characters.+--+{-# INLINE wordWithQuotes #-}+wordWithQuotes :: (Monad m, Eq a) =>+       Bool            -- ^ Retain the quotes and escape chars in the output+    -> (a -> a -> Maybe a)  -- ^ quote char -> escaped char -> translated char+    -> a               -- ^ Matches an escape elem?+    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.+    -> (a -> Bool)     -- ^ Matches a word separator?+    -> Fold m a b+    -> Parser a m b+wordWithQuotes keepQuotes tr escChar toRight isSep+    (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    -- Can be used to generate parse error for a bracket end without a bracket+    -- begin.+    isInvalid = const False++    isEsc = (== escChar)++    initial =  do+        res <- finitial+        return $+            case res of+                FL.Partial s -> IPartial (WordQuotedSkipPre s)+                FL.Done _ ->+                    error "wordKeepQuotes: fold done without input"++    {-# INLINE processQuoted #-}+    processQuoted s a n ql qr = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (WordQuotedWord s1 n ql qr)+                FL.Done b -> Done 0 b++    {-# INLINE processUnquoted #-}+    processUnquoted s a = do+        res <- fstep s a+        return+            $ case res of+                FL.Partial s1 -> Continue 0 (WordUnquotedWord s1)+                FL.Done b -> Done 0 b++    step (WordQuotedSkipPre s) a+        | isEsc a = return $ Continue 0 $ WordUnquotedEsc s+        | isSep a = return $ Partial 0 $ WordQuotedSkipPre s+        | otherwise =+            case toRight a of+                Just qr ->+                  if keepQuotes+                  then processQuoted s a 1 a qr+                  else return $ Continue 0 $ WordQuotedWord s 1 a qr+                Nothing+                    | isInvalid a ->+                        return $ Error "wordKeepQuotes: invalid unquoted char"+                    | otherwise -> processUnquoted s a+    step (WordUnquotedWord s) a+        | isEsc a = return $ Continue 0 $ WordUnquotedEsc s+        | isSep a = do+            b <- ffinal s+            return $ Partial 0 $ WordQuotedSkipPost b+        | otherwise = do+            case toRight a of+                Just qr ->+                    if keepQuotes+                    then processQuoted s a 1 a qr+                    else return $ Continue 0 $ WordQuotedWord s 1 a qr+                Nothing ->+                    if isInvalid a+                    then return $ Error "wordKeepQuotes: invalid unquoted char"+                    else processUnquoted s a+    step (WordQuotedWord s n ql qr) a+        | isEsc a = return $ Continue 0 $ WordQuotedEsc s n ql qr+        {-+        -- XXX Will this ever occur? Will n ever be 0?+        | n == 0 && isSep a = do+            b <- fextract s+            return $ Partial 0 $ WordQuotedSkipPost b+        -}+        | otherwise = do+                if a == qr+                then+                   if n == 1+                   then if keepQuotes+                        then processUnquoted s a+                        else return $ Continue 0 $ WordUnquotedWord s+                   else processQuoted s a (n - 1) ql qr+                else if a == ql+                     then processQuoted s a (n + 1) ql qr+                     else processQuoted s a n ql qr+    step (WordUnquotedEsc s) a = processUnquoted s a+    step (WordQuotedEsc s n ql qr) a =+        case tr ql a of+            Nothing -> do+                res <- fstep s escChar+                case res of+                    FL.Partial s1 -> processQuoted s1 a n ql qr+                    FL.Done b -> return $ Done 0 b+            Just x -> processQuoted s x n ql qr+    step (WordQuotedSkipPost b) a =+        return+            $ if not (isSep a)+              then Done 1 b+              else Partial 0 $ WordQuotedSkipPost b++    err = return . Error++    extract (WordQuotedSkipPre s) = fmap (Done 0) $ ffinal s+    extract (WordUnquotedWord s) = fmap (Done 0) $ ffinal s+    extract (WordQuotedWord s n _ _) =+        if n == 0+        then fmap (Done 0) $ ffinal s+        else err "wordWithQuotes: missing frame end"+    extract WordQuotedEsc {} =+        err "wordWithQuotes: trailing escape"+    extract (WordUnquotedEsc _) =+        err "wordWithQuotes: trailing escape"+    extract (WordQuotedSkipPost b) = return (Done 0 b)++-- | 'wordWithQuotes' without processing the quotes and escape function+-- supplied to escape the quote char within a quote. Can be used to parse words+-- keeping the quotes and escapes intact.+--+-- >>> wordKeepQuotes = Parser.wordWithQuotes True (\_ _ -> Nothing)+--+{-# INLINE wordKeepQuotes #-}+wordKeepQuotes :: (Monad m, Eq a) =>+       a               -- ^ Escape char+    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.+    -> (a -> Bool)     -- ^ Matches a word separator?+    -> Fold m a b+    -> Parser a m b+wordKeepQuotes =+    -- Escape the quote char itself+    wordWithQuotes True (\q x -> if q == x then Just x else Nothing)++-- See the "Quoting Rules" section in the "bash" manual page for a primer on+-- how quotes are used by shells.++-- | 'wordWithQuotes' with quote processing applied and escape function+-- supplied to escape the quote char within a quote. Can be ysed to parse words+-- and processing the quoting and escaping at the same time.+--+-- >>> wordProcessQuotes = Parser.wordWithQuotes False (\_ _ -> Nothing)+--+{-# INLINE wordProcessQuotes #-}+wordProcessQuotes :: (Monad m, Eq a) =>+        a              -- ^ Escape char+    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.+    -> (a -> Bool)     -- ^ Matches a word separator?+    -> Fold m a b+    -> Parser a m b+wordProcessQuotes =+    -- Escape the quote char itself+    wordWithQuotes False (\q x -> if q == x then Just x else Nothing)++{-# ANN type GroupByState Fuse #-}+data GroupByState a s+    = GroupByInit !s+    | GroupByGrouping !a !s++-- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the+-- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is+-- 'True' @b@ is also assigned to the same group.  If @a \`cmp` c@ is 'True'+-- then @c@ is also assigned to the same group and so on. When the comparison+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and+-- the result of the fold is the result of the parser.+--+-- * Stops - when the comparison fails.+-- * Fails - never.+--+-- >>> :{+--  runGroupsBy eq =+--      Stream.fold Fold.toList+--          . Stream.parseMany (Parser.groupBy eq Fold.toList)+--          . Stream.fromList+-- :}+--+-- >>> runGroupsBy (<) []+-- []+--+-- >>> runGroupsBy (<) [1]+-- [Right [1]]+--+-- >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]+-- [Right [3,5,4],Right [1,2],Right [0]]+--+{-# INLINE groupBy #-}+groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b+groupBy eq (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    {-# INLINE grouper #-}+    grouper s a0 a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Done b -> Done 0 b+                  FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)++    initial = do+        res <- finitial+        return+            $ case res of+                  FL.Partial s -> IPartial $ GroupByInit s+                  FL.Done b -> IDone b++    step (GroupByInit s) a = grouper s a a+    step (GroupByGrouping a0 s) a =+        if eq a0 a+        then grouper s a0 a+        else Done 1 <$> ffinal s++    extract (GroupByInit s) = fmap (Done 0) $ ffinal s+    extract (GroupByGrouping _ s) = fmap (Done 0) $ ffinal s++-- | Unlike 'groupBy' this combinator performs a rolling comparison of two+-- successive elements in the input stream.  Assuming the input stream+-- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser+-- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is+-- 'True' @b@ is also assigned to the same group.  If @b \`cmp` c@ is 'True'+-- then @c@ is also assigned to the same group and so on. When the comparison+-- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and+-- the result of the fold is the result of the parser.+--+-- * Stops - when the comparison fails.+-- * Fails - never.+--+-- >>> :{+--  runGroupsByRolling eq =+--      Stream.fold Fold.toList+--          . Stream.parseMany (Parser.groupByRolling eq Fold.toList)+--          . Stream.fromList+-- :}+--+-- >>> runGroupsByRolling (<) []+-- []+--+-- >>> runGroupsByRolling (<) [1]+-- [Right [1]]+--+-- >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]+-- [Right [3,5],Right [4],Right [1,2],Right [0]]+--+-- /Pre-release/+--+{-# INLINE groupByRolling #-}+groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b+groupByRolling eq (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    {-# INLINE grouper #-}+    grouper s a = do+        res <- fstep s a+        return+            $ case res of+                  FL.Done b -> Done 0 b+                  FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)++    initial = do+        res <- finitial+        return+            $ case res of+                  FL.Partial s -> IPartial $ GroupByInit s+                  FL.Done b -> IDone b++    step (GroupByInit s) a = grouper s a+    step (GroupByGrouping a0 s) a =+        if eq a0 a+        then grouper s a+        else Done 1 <$> ffinal s++    extract (GroupByInit s) = fmap (Done 0) $ ffinal s+    extract (GroupByGrouping _ s) = fmap (Done 0) $ ffinal s++{-# ANN type GroupByStatePair Fuse #-}+data GroupByStatePair a s1 s2+    = GroupByInitPair !s1 !s2+    | GroupByGroupingPair !a !s1 !s2+    | GroupByGroupingPairL !a !s1 !s2+    | GroupByGroupingPairR !a !s1 !s2++-- | Like 'groupByRolling', but if the predicate is 'True' then collects using+-- the first fold as long as the predicate holds 'True', if the predicate is+-- 'False' collects using the second fold as long as it remains 'False'.+-- Returns 'Left' for the first case and 'Right' for the second case.+--+-- For example, if we want to detect sorted sequences in a stream, both+-- ascending and descending cases we can use 'groupByRollingEither (<=)+-- Fold.toList Fold.toList'.+--+-- /Pre-release/+{-# INLINE groupByRollingEither #-}+groupByRollingEither :: Monad m =>+    (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)+groupByRollingEither+    eq+    (Fold fstep1 finitial1 _ ffinal1)+    (Fold fstep2 finitial2 _ ffinal2) = Parser step initial extract++    where++    {-# INLINE grouper #-}+    grouper s1 s2 a = do+        return $ Continue 0 (GroupByGroupingPair a s1 s2)++    {-# INLINE grouperL2 #-}+    grouperL2 s1 s2 a = do+        res <- fstep1 s1 a+        return+            $ case res of+                FL.Done b -> Done 0 (Left b)+                FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)++    {-# INLINE grouperL #-}+    grouperL s1 s2 a0 a = do+        res <- fstep1 s1 a0+        case res of+            FL.Done b -> return $ Done 0 (Left b)+            FL.Partial s11 -> grouperL2 s11 s2 a++    {-# INLINE grouperR2 #-}+    grouperR2 s1 s2 a = do+        res <- fstep2 s2 a+        return+            $ case res of+                FL.Done b -> Done 0 (Right b)+                FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)++    {-# INLINE grouperR #-}+    grouperR s1 s2 a0 a = do+        res <- fstep2 s2 a0+        case res of+            FL.Done b -> return $ Done 0 (Right b)+            FL.Partial s21 -> grouperR2 s1 s21 a++    initial = do+        res1 <- finitial1+        res2 <- finitial2+        return+            $ case res1 of+                FL.Partial s1 ->+                    case res2 of+                        FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2+                        FL.Done b -> IDone (Right b)+                FL.Done b -> IDone (Left b)++    step (GroupByInitPair s1 s2) a = grouper s1 s2 a++    step (GroupByGroupingPair a0 s1 s2) a =+        if not (eq a0 a)+        then grouperL s1 s2 a0 a+        else grouperR s1 s2 a0 a++    step (GroupByGroupingPairL a0 s1 s2) a =+        if not (eq a0 a)+        then grouperL2 s1 s2 a+        else Done 1 . Left <$> ffinal1 s1++    step (GroupByGroupingPairR a0 s1 s2) a =+        if eq a0 a+        then grouperR2 s1 s2 a+        else Done 1 . Right <$> ffinal2 s2++    extract (GroupByInitPair s1 _) = Done 0 . Left <$> ffinal1 s1+    extract (GroupByGroupingPairL _ s1 _) = Done 0 . Left <$> ffinal1 s1+    extract (GroupByGroupingPairR _ _ s2) = Done 0 . Right <$> ffinal2 s2+    extract (GroupByGroupingPair a s1 _) = do+                res <- fstep1 s1 a+                case res of+                    FL.Done b -> return $ Done 0 (Left b)+                    FL.Partial s11 -> Done 0 . Left <$> ffinal1 s11++-- XXX use an Unfold instead of a list?+-- XXX custom combinators for matching list, array and stream?+-- XXX rename to listBy?++-- | Match the given sequence of elements using the given comparison function.+-- Returns the original sequence if successful.+--+-- Definition:+--+-- >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs+--+-- Examples:+--+-- >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"+-- Right "string"+--+-- >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"+-- Left (ParseError "streamEqBy: mismtach occurred")+--+{-# INLINE listEqBy #-}+listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]+listEqBy cmp xs = streamEqByInternal cmp (D.fromList xs) *> fromPure xs+{-+listEqBy cmp str = Parser step initial extract++    where++    -- XXX Should return IDone in initial for [] case+    initial = return $ IPartial str++    step [] _ = return $ Done 0 str+    step [x] a =+        return+            $ if x `cmp` a+              then Done 0 str+              else Error "listEqBy: failed, yet to match the last element"+    step (x:xs) a =+        return+            $ if x `cmp` a+              then Continue 0 xs+              else Error+                       $ "listEqBy: failed, yet to match "+                       ++ show (length xs + 1) ++ " elements"++    extract xs =+        return+            $ Error+            $ "listEqBy: end of input, yet to match "+            ++ show (length xs) ++ " elements"+-}++{-# INLINE streamEqByInternal #-}+streamEqByInternal :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()+streamEqByInternal cmp (D.Stream sstep state) = Parser step initial extract++    where++    initial = do+        r <- sstep defState state+        case r of+            D.Yield x s -> return $ IPartial (Just' x, s)+            D.Stop -> return $ IDone ()+            -- Need Skip/Continue in initial to loop right here+            D.Skip s -> return $ IPartial (Nothing', s)++    step (Just' x, st) a =+        if x `cmp` a+          then do+            r <- sstep defState st+            return+                $ case r of+                    D.Yield x1 s -> Continue 0 (Just' x1, s)+                    D.Stop -> Done 0 ()+                    D.Skip s -> Continue 1 (Nothing', s)+          else return $ Error "streamEqBy: mismtach occurred"+    step (Nothing', st) a = do+        r <- sstep defState st+        return+            $ case r of+                D.Yield x s -> do+                    if x `cmp` a+                    then Continue 0 (Nothing', s)+                    else Error "streamEqBy: mismatch occurred"+                D.Stop -> Done 1 ()+                D.Skip s -> Continue 1 (Nothing', s)++    extract _ = return $ Error "streamEqBy: end of input"++-- | Like 'listEqBy' but uses a stream instead of a list and does not return+-- the stream.+--+{-# INLINE streamEqBy #-}+streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()+-- XXX Somehow composing this with "*>" is much faster on the microbenchmark.+-- Need to investigate why.+streamEqBy cmp stream = streamEqByInternal cmp stream *> fromPure ()++-- Rename to "list".+-- | Match the input sequence with the supplied list and return it if+-- successful.+--+-- >>> listEq = Parser.listEqBy (==)+--+{-# INLINE listEq #-}+listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]+listEq = listEqBy (==)++-- | Match if the input stream is a subsequence of the argument stream i.e. all+-- the elements of the input stream occur, in order, in the argument stream.+-- The elements do not have to occur consecutively. A sequence is considered a+-- subsequence of itself.+{-# INLINE subsequenceBy #-}+subsequenceBy :: -- Monad m =>+    (a -> a -> Bool) -> Stream m a -> Parser a m ()+subsequenceBy = undefined++{-+-- Should go in Data.Parser.Regex in streamly package so that it can depend on+-- regex backends.+{-# INLINE regexPosix #-}+regexPosix :: -- Monad m =>+    Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))+regexPosix = undefined++{-# INLINE regexPCRE #-}+regexPCRE :: -- Monad m =>+    Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))+regexPCRE = undefined+-}++-------------------------------------------------------------------------------+-- Transformations on input+-------------------------------------------------------------------------------++-- Initial needs a "Continue" constructor to implement scans on parsers. As a+-- parser can always return a Continue in initial when we feed the fold's+-- initial result to it. We can work this around for postscan by introducing an+-- initial state and calling "initial" only on the first input.++-- | Stateful scan on the input of a parser using a Fold.+--+-- /Unimplemented/+--+{-# INLINE postscan #-}+postscan :: -- Monad m =>+    Fold m a b -> Parser b m c -> Parser a m c+postscan = undefined++{-# INLINE zipWithM #-}+zipWithM :: Monad m =>+    (a -> b -> m c) -> D.Stream m a -> Fold m c x -> Parser b m x+zipWithM zf (D.Stream sstep state) (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        fres <- finitial+        case fres of+            FL.Partial fs -> do+                r <- sstep defState state+                case r of+                    D.Yield x s -> return $ IPartial (Just' x, s, fs)+                    D.Stop -> do+                        x <- ffinal fs+                        return $ IDone x+                    -- Need Skip/Continue in initial to loop right here+                    D.Skip s -> return $ IPartial (Nothing', s, fs)+            FL.Done x -> return $ IDone x++    step (Just' a, st, fs) b = do+        c <- zf a b+        fres <- fstep fs c+        case fres of+            FL.Partial fs1 -> do+                r <- sstep defState st+                case r of+                    D.Yield x1 s -> return $ Continue 0 (Just' x1, s, fs1)+                    D.Stop -> do+                        x <- ffinal fs1+                        return $ Done 0 x+                    D.Skip s -> return $ Continue 1 (Nothing', s, fs1)+            FL.Done x -> return $ Done 0 x+    step (Nothing', st, fs) b = do+        r <- sstep defState st+        case r of+                D.Yield a s -> do+                    c <- zf a b+                    fres <- fstep fs c+                    case fres of+                        FL.Partial fs1 ->+                            return $ Continue 0 (Nothing', s, fs1)+                        FL.Done x -> return $ Done 0 x+                D.Stop -> do+                    x <- ffinal fs+                    return $ Done 1 x+                D.Skip s -> return $ Continue 1 (Nothing', s, fs)++    extract _ = return $ Error "zipWithM: end of input"++-- | Zip the input of a fold with a stream.+--+-- /Pre-release/+--+{-# INLINE zip #-}+zip :: Monad m => D.Stream m a -> Fold m (a, b) x -> Parser b m x+zip = zipWithM (curry return)++-- | Pair each element of a fold input with its index, starting from index 0.+--+-- /Pre-release/+{-# INLINE indexed #-}+indexed :: forall m a b. Monad m => Fold m (Int, a) b -> Parser a m b+indexed = zip (D.enumerateFromIntegral 0 :: D.Stream m Int)++-- | @makeIndexFilter indexer filter predicate@ generates a fold filtering+-- function using a fold indexing function that attaches an index to each input+-- element and a filtering function that filters using @(index, element) ->+-- Bool) as predicate.+--+-- For example:+--+-- @+-- filterWithIndex = makeIndexFilter indexed filter+-- filterWithAbsTime = makeIndexFilter timestamped filter+-- filterWithRelTime = makeIndexFilter timeIndexed filter+-- @+--+-- /Pre-release/+{-# INLINE makeIndexFilter #-}+makeIndexFilter ::+       (Fold m (s, a) b -> Parser a m b)+    -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b)+    -> (((s, a) -> Bool) -> Fold m a b -> Parser a m b)+makeIndexFilter f comb g = f . comb g . FL.lmap snd++-- | @sampleFromthen offset stride@ samples the element at @offset@ index and+-- then every element at strides of @stride@.+--+-- /Pre-release/+{-# INLINE sampleFromthen #-}+sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Parser a m b+sampleFromthen offset size =+    makeIndexFilter indexed FL.filter (\(i, _) -> (i + offset) `mod` size == 0)++--------------------------------------------------------------------------------+--- Spanning+--------------------------------------------------------------------------------++-- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the+-- input as long as the predicate @p@ is 'True'.  @f2@ consumes the rest of the+-- input.+--+-- @+-- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs+--+-- > span_ (< 1) [1,2,3]+-- ([],[1,2,3])+--+-- > span_ (< 2) [1,2,3]+-- ([1],[2,3])+--+-- > span_ (< 4) [1,2,3]+-- ([1,2,3],[])+--+-- @+--+-- /Pre-release/+{-# INLINE span #-}+span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)++-- | Break the input stream into two groups, the first group takes the input as+-- long as the predicate applied to the first element of the stream and next+-- input element holds 'True', the second group takes the rest of the input.+--+-- /Pre-release/+--+{-# INLINE spanBy #-}+spanBy ::+       Monad m+    => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)++-- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.+-- predicate is applied to the previous and the next input elements.+--+-- /Pre-release/+{-# INLINE spanByRolling #-}+spanByRolling ::+       Monad m+    => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)+spanByRolling eq f1 f2 =+    noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)++-------------------------------------------------------------------------------+-- nested parsers+-------------------------------------------------------------------------------++-- | Takes at-most @n@ input elements.+--+-- * Stops - when the collecting parser stops.+-- * Fails - when the collecting parser fails.+--+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]+-- Right [1,2]+--+-- >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]+-- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")+--+-- /Internal/+{-# INLINE takeP #-}+takeP :: Monad m => Int -> Parser a m b -> Parser a m b+takeP lim (Parser pstep pinitial pextract) = Parser step initial extract++    where++    initial = do+        res <- pinitial+        case res of+            IPartial s ->+                if lim > 0+                then return $ IPartial $ Tuple' 0 s+                else iextract s+            IDone b -> return $ IDone b+            IError e -> return $ IError e++    step (Tuple' cnt r) a = do+        assertM(cnt < lim)+        res <- pstep r a+        let cnt1 = cnt + 1+        case res of+            Partial 0 s -> do+                assertM(cnt1 >= 0)+                if cnt1 < lim+                then return $ Partial 0 $ Tuple' cnt1 s+                else do+                    r1 <- pextract s+                    return $ case r1 of+                        Done n b -> Done n b+                        Continue n s1 -> Continue n (Tuple' (cnt1 - n) s1)+                        Error err -> Error err+                        Partial _ _ -> error "takeP: Partial in extract"++            Continue 0 s -> do+                assertM(cnt1 >= 0)+                if cnt1 < lim+                then return $ Continue 0 $ Tuple' cnt1 s+                else do+                    r1 <- pextract s+                    return $ case r1 of+                        Done n b -> Done n b+                        Continue n s1 -> Continue n (Tuple' (cnt1 - n) s1)+                        Error err -> Error err+                        Partial _ _ -> error "takeP: Partial in extract"+            Partial n s -> do+                let taken = cnt1 - n+                assertM(taken >= 0)+                return $ Partial n $ Tuple' taken s+            Continue n s -> do+                let taken = cnt1 - n+                assertM(taken >= 0)+                return $ Continue n $ Tuple' taken s+            Done n b -> return $ Done n b+            Error str -> return $ Error str++    extract (Tuple' cnt r) = do+        r1 <- pextract r+        return $ case r1 of+            Done n b -> Done n b+            Continue n s1 -> Continue n (Tuple' (cnt - n) s1)+            Error err -> Error err+            Partial _ _ -> error "takeP: Partial in extract"++    -- XXX Need to make the Initial type Step to remove this+    iextract s = do+        r <- pextract s+        return $ case r of+            Done _ b -> IDone b+            Error err -> IError err+            _ -> error "Bug: takeP invalid state in initial"++-- | Run a parser without consuming the input.+--+{-# INLINE lookAhead #-}+lookAhead :: Monad m => Parser a m b -> Parser a m b+lookAhead (Parser step1 initial1 _) = Parser step initial extract++    where++    initial = do+        res <- initial1+        return $ case res of+            IPartial s -> IPartial (Tuple'Fused 0 s)+            IDone b -> IDone b+            IError e -> IError e++    step (Tuple'Fused cnt st) a = do+        r <- step1 st a+        let cnt1 = cnt + 1+        return+            $ case r of+                  Partial n s -> Continue n (Tuple'Fused (cnt1 - n) s)+                  Continue n s -> Continue n (Tuple'Fused (cnt1 - n) s)+                  Done _ b -> Done cnt1 b+                  Error err -> Error err++    -- XXX returning an error let's us backtrack.  To implement it in a way so+    -- that it terminates on eof without an error then we need a way to+    -- backtrack on eof, that will require extract to return 'Step' type.+    extract (Tuple'Fused n _) =+        return+            $ Error+            $ "lookAhead: end of input after consuming "+            ++ show n ++ " elements"++-------------------------------------------------------------------------------+-- Interleaving+-------------------------------------------------------------------------------+--+-- To deinterleave we can chain two parsers one behind the other. The input is+-- given to the first parser and the input definitively rejected by the first+-- parser is given to the second parser.+--+-- We can either have the parsers themselves buffer the input or use the shared+-- global buffer to hold it until none of the parsers need it. When the first+-- parser returns Skip (i.e. rewind) we let the second parser consume the+-- rejected input and when it is done we move the cursor forward to the first+-- parser again. This will require a "move forward" command as well.+--+-- To implement grep we can use three parsers, one to find the pattern, one+-- to store the context behind the pattern and one to store the context in+-- front of the pattern. When a match occurs we need to emit the accumulator of+-- all the three parsers. One parser can count the line numbers to provide the+-- line number info.++{-# ANN type DeintercalateAllState Fuse #-}+data DeintercalateAllState fs sp ss =+      DeintercalateAllInitL !fs+    | DeintercalateAllL !fs !sp+    | DeintercalateAllInitR !fs+    | DeintercalateAllR !fs !ss++-- XXX rename this to intercalate++-- Having deintercalateAll for accepting or rejecting entire input could be+-- useful. For example, in case of JSON parsing we get an entire block of+-- key-value pairs which we need to verify. This version may be simpler, more+-- efficient. We could implement this as a stream operation like parseMany.+--+-- XXX Also, it may be a good idea to provide a parse driver for a fold. For+-- example, in case of csv parsing as we are feeding a line to a fold we can+-- parse it.++-- | Like 'deintercalate' but the entire input must satisfy the pattern+-- otherwise the parser fails. This is many times faster than deintercalate.+--+-- >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList+-- >>> p2 = Parser.satisfy (== '+')+-- >>> p = Parser.deintercalateAll p1 p2 Fold.toList+-- >>> Stream.parse p $ Stream.fromList ""+-- Right []+-- >>> Stream.parse p $ Stream.fromList "1"+-- Right [Left "1"]+-- >>> Stream.parse p $ Stream.fromList "1+"+-- Left (ParseError "takeWhile1: end of input")+-- >>> Stream.parse p $ Stream.fromList "1+2+3"+-- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]+--+{-# INLINE deintercalateAll #-}+deintercalateAll :: Monad m =>+       Parser a m x+    -> Parser a m y+    -> Fold m (Either x y) z+    -> Parser a m z+deintercalateAll+    (Parser stepL initialL extractL)+    (Parser stepR initialR _)+    (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    errMsg p status =+        error $ "deintercalate: " ++ p ++ " parser cannot "+                ++ status ++ " without input"++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> return $ IPartial $ DeintercalateAllInitL fs+            FL.Done c -> return $ IDone c++    {-# INLINE processL #-}+    processL foldAction n nextState = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> return $ Partial n (nextState fs1)+            FL.Done c -> return $ Done n c++    {-# INLINE runStepL #-}+    runStepL fs sL a = do+        r <- stepL sL a+        case r of+            Partial n s -> return $ Partial n (DeintercalateAllL fs s)+            Continue n s -> return $ Continue n (DeintercalateAllL fs s)+            Done n b ->+                processL (fstep fs (Left b)) n DeintercalateAllInitR+            Error err -> return $ Error err++    {-# INLINE processR #-}+    processR foldAction n = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> do+                res <- initialL+                case res of+                    IPartial ps -> return $ Partial n (DeintercalateAllL fs1 ps)+                    IDone _ -> errMsg "left" "succeed"+                    IError _ -> errMsg "left" "fail"+            FL.Done c -> return $ Done n c++    {-# INLINE runStepR #-}+    runStepR fs sR a = do+        r <- stepR sR a+        case r of+            Partial n s -> return $ Partial n (DeintercalateAllR fs s)+            Continue n s -> return $ Continue n (DeintercalateAllR fs s)+            Done n b -> processR (fstep fs (Right b)) n+            Error err -> return $ Error err++    step (DeintercalateAllInitL fs) a = do+        res <- initialL+        case res of+            IPartial s -> runStepL fs s a+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"+    step (DeintercalateAllL fs sL) a = runStepL fs sL a+    step (DeintercalateAllInitR fs) a = do+        res <- initialR+        case res of+            IPartial s -> runStepR fs s a+            IDone _ -> errMsg "right" "succeed"+            IError _ -> errMsg "right" "fail"+    step (DeintercalateAllR fs sR) a = runStepR fs sR a++    {-# INLINE extractResult #-}+    extractResult n fs r = do+        res <- fstep fs r+        case res of+            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+            FL.Done c -> return (Done n c)+    extract (DeintercalateAllInitL fs) = fmap (Done 0) $ ffinal fs+    extract (DeintercalateAllL fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs (Left b)+            Error err -> return $ Error err+            Continue n s -> return $ Continue n (DeintercalateAllL fs s)+            Partial _ _ -> error "Partial in extract"+    extract (DeintercalateAllInitR fs) = fmap (Done 0) $ ffinal fs+    extract (DeintercalateAllR _ _) =+        return $ Error "deintercalateAll: input ended at 'Right' value"++{-# ANN type DeintercalateState Fuse #-}+data DeintercalateState b fs sp ss =+      DeintercalateInitL !fs+    | DeintercalateL !Int !fs !sp+    | DeintercalateInitR !fs+    | DeintercalateR !Int !fs !ss+    | DeintercalateRL !Int !b !fs !sp++-- XXX Add tests that the next character that we take after running a parser is+-- correct. Especially for the parsers that maintain a count. In the stream+-- finished case (extract) as well as not finished case.++-- | Apply two parsers alternately to an input stream. The input stream is+-- considered an interleaving of two patterns. The two parsers represent the+-- two patterns. Parsing starts at the first parser and stops at the first+-- parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty+-- input or single parse of the first parser is accepted.+--+-- >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList+-- >>> p2 = Parser.satisfy (== '+')+-- >>> p = Parser.deintercalate p1 p2 Fold.toList+-- >>> Stream.parse p $ Stream.fromList ""+-- Right []+-- >>> Stream.parse p $ Stream.fromList "1"+-- Right [Left "1"]+-- >>> Stream.parse p $ Stream.fromList "1+"+-- Right [Left "1"]+-- >>> Stream.parse p $ Stream.fromList "1+2+3"+-- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]+--+{-# INLINE deintercalate #-}+deintercalate :: Monad m =>+       Parser a m x+    -> Parser a m y+    -> Fold m (Either x y) z+    -> Parser a m z+deintercalate+    (Parser stepL initialL extractL)+    (Parser stepR initialR _)+    (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    errMsg p status =+        error $ "deintercalate: " ++ p ++ " parser cannot "+                ++ status ++ " without input"++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> return $ IPartial $ DeintercalateInitL fs+            FL.Done c -> return $ IDone c++    {-# INLINE processL #-}+    processL foldAction n nextState = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> return $ Partial n (nextState fs1)+            FL.Done c -> return $ Done n c++    {-# INLINE runStepL #-}+    runStepL cnt fs sL a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (DeintercalateL (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (DeintercalateL (cnt1 - n) fs s)+            Done n b ->+                processL (fstep fs (Left b)) n DeintercalateInitR+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    {-# INLINE processR #-}+    processR cnt b fs n = do+        res <- initialL+        case res of+            IPartial ps -> return $ Continue n (DeintercalateRL cnt b fs ps)+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"++    {-# INLINE runStepR #-}+    runStepR cnt fs sR a = do+        let cnt1 = cnt + 1+        r <- stepR sR a+        case r of+            Partial n s -> return $ Continue n (DeintercalateR (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (DeintercalateR (cnt1 - n) fs s)+            Done n b -> processR (cnt1 - n) b fs n+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    step (DeintercalateInitL fs) a = do+        res <- initialL+        case res of+            IPartial s -> runStepL 0 fs s a+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"+    step (DeintercalateL cnt fs sL) a = runStepL cnt fs sL a+    step (DeintercalateInitR fs) a = do+        res <- initialR+        case res of+            IPartial s -> runStepR 0 fs s a+            IDone _ -> errMsg "right" "succeed"+            IError _ -> errMsg "right" "fail"+    step (DeintercalateR cnt fs sR) a = runStepR cnt fs sR a+    step (DeintercalateRL cnt bR fs sL) a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (DeintercalateRL (cnt1 - n) bR fs s)+            Continue n s -> return $ Continue n (DeintercalateRL (cnt1 - n) bR fs s)+            Done n bL -> do+                res <- fstep fs (Right bR)+                case res of+                    FL.Partial fs1 -> do+                        fres <- fstep fs1 (Left bL)+                        case fres of+                            FL.Partial fs2 ->+                                return $ Partial n (DeintercalateInitR fs2)+                            FL.Done c -> return $ Done n c+                    -- XXX We could have the fold accept pairs of (bR, bL)+                    FL.Done _ -> error "Fold terminated consuming partial input"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    {-# INLINE extractResult #-}+    extractResult n fs r = do+        res <- fstep fs r+        case res of+            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+            FL.Done c -> return (Done n c)++    extract (DeintercalateInitL fs) = fmap (Done 0) $ ffinal fs+    extract (DeintercalateL cnt fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs (Left b)+            Continue n s -> return $ Continue n (DeintercalateL (cnt - n) fs s)+            Partial _ _ -> error "Partial in extract"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs+    extract (DeintercalateInitR fs) = fmap (Done 0) $ ffinal fs+    extract (DeintercalateR cnt fs _) = fmap (Done cnt) $ ffinal fs+    extract (DeintercalateRL cnt bR fs sL) = do+        r <- extractL sL+        case r of+            Done n bL -> do+                res <- fstep fs (Right bR)+                case res of+                    FL.Partial fs1 -> extractResult n fs1 (Left bL)+                    FL.Done _ -> error "Fold terminated consuming partial input"+            Continue n s -> return $ Continue n (DeintercalateRL (cnt - n) bR fs s)+            Partial _ _ -> error "Partial in extract"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs++{-# ANN type Deintercalate1State Fuse #-}+data Deintercalate1State b fs sp ss =+      Deintercalate1InitL !Int !fs !sp+    | Deintercalate1InitR !fs+    | Deintercalate1R !Int !fs !ss+    | Deintercalate1RL !Int !b !fs !sp++-- | Apply two parsers alternately to an input stream. The input stream is+-- considered an interleaving of two patterns. The two parsers represent the+-- two patterns. Parsing starts at the first parser and stops at the first+-- parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty+-- input or single parse of the first parser is accepted.+--+-- >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList+-- >>> p2 = Parser.satisfy (== '+')+-- >>> p = Parser.deintercalate1 p1 p2 Fold.toList+-- >>> Stream.parse p $ Stream.fromList ""+-- Left (ParseError "takeWhile1: end of input")+-- >>> Stream.parse p $ Stream.fromList "1"+-- Right [Left "1"]+-- >>> Stream.parse p $ Stream.fromList "1+"+-- Right [Left "1"]+-- >>> Stream.parse p $ Stream.fromList "1+2+3"+-- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]+--+{-# INLINE deintercalate1 #-}+deintercalate1 :: Monad m =>+       Parser a m x+    -> Parser a m y+    -> Fold m (Either x y) z+    -> Parser a m z+deintercalate1+    (Parser stepL initialL extractL)+    (Parser stepR initialR _)+    (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    errMsg p status =+        error $ "deintercalate: " ++ p ++ " parser cannot "+                ++ status ++ " without input"++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> do+                pres <- initialL+                case pres of+                    IPartial s -> return $ IPartial $ Deintercalate1InitL 0 fs s+                    IDone _ -> errMsg "left" "succeed"+                    IError _ -> errMsg "left" "fail"+            FL.Done c -> return $ IDone c++    {-# INLINE processL #-}+    processL foldAction n nextState = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> return $ Partial n (nextState fs1)+            FL.Done c -> return $ Done n c++    {-# INLINE runStepInitL #-}+    runStepInitL cnt fs sL a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (Deintercalate1InitL (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (Deintercalate1InitL (cnt1 - n) fs s)+            Done n b ->+                processL (fstep fs (Left b)) n Deintercalate1InitR+            Error err -> return $ Error err++    {-# INLINE processR #-}+    processR cnt b fs n = do+        res <- initialL+        case res of+            IPartial ps -> return $ Continue n (Deintercalate1RL cnt b fs ps)+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"++    {-# INLINE runStepR #-}+    runStepR cnt fs sR a = do+        let cnt1 = cnt + 1+        r <- stepR sR a+        case r of+            Partial n s -> return $ Continue n (Deintercalate1R (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (Deintercalate1R (cnt1 - n) fs s)+            Done n b -> processR (cnt1 - n) b fs n+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    step (Deintercalate1InitL cnt fs sL) a = runStepInitL cnt fs sL a+    step (Deintercalate1InitR fs) a = do+        res <- initialR+        case res of+            IPartial s -> runStepR 0 fs s a+            IDone _ -> errMsg "right" "succeed"+            IError _ -> errMsg "right" "fail"+    step (Deintercalate1R cnt fs sR) a = runStepR cnt fs sR a+    step (Deintercalate1RL cnt bR fs sL) a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (Deintercalate1RL (cnt1 - n) bR fs s)+            Continue n s -> return $ Continue n (Deintercalate1RL (cnt1 - n) bR fs s)+            Done n bL -> do+                res <- fstep fs (Right bR)+                case res of+                    FL.Partial fs1 -> do+                        fres <- fstep fs1 (Left bL)+                        case fres of+                            FL.Partial fs2 ->+                                return $ Partial n (Deintercalate1InitR fs2)+                            FL.Done c -> return $ Done n c+                    -- XXX We could have the fold accept pairs of (bR, bL)+                    FL.Done _ -> error "Fold terminated consuming partial input"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    {-# INLINE extractResult #-}+    extractResult n fs r = do+        res <- fstep fs r+        case res of+            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+            FL.Done c -> return (Done n c)++    extract (Deintercalate1InitL cnt fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs (Left b)+            Continue n s -> return $ Continue n (Deintercalate1InitL (cnt - n) fs s)+            Partial _ _ -> error "Partial in extract"+            Error err -> return $ Error err+    extract (Deintercalate1InitR fs) = fmap (Done 0) $ ffinal fs+    extract (Deintercalate1R cnt fs _) = fmap (Done cnt) $ ffinal fs+    extract (Deintercalate1RL cnt bR fs sL) = do+        r <- extractL sL+        case r of+            Done n bL -> do+                res <- fstep fs (Right bR)+                case res of+                    FL.Partial fs1 -> extractResult n fs1 (Left bL)+                    FL.Done _ -> error "Fold terminated consuming partial input"+            Continue n s -> return $ Continue n (Deintercalate1RL (cnt - n) bR fs s)+            Partial _ _ -> error "Partial in extract"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs++{-# ANN type SepByState Fuse #-}+data SepByState fs sp ss =+      SepByInitL !fs+    | SepByL !Int !fs !sp+    | SepByInitR !fs+    | SepByR !Int !fs !ss++-- | Apply two parsers alternately to an input stream. Parsing starts at the+-- first parser and stops at the first parser. The output of the first parser+-- is emiited and the output of the second parser is discarded. It can be used+-- to parse a infix style pattern e.g. p1 p2 p1 . Empty input or single parse+-- of the first parser is accepted.+--+-- Definitions:+--+-- >>> sepBy p1 p2 f = Parser.deintercalate p1 p2 (Fold.catLefts f)+-- >>> sepBy p1 p2 f = Parser.sepBy1 p1 p2 f <|> Parser.fromEffect (Fold.extractM f)+--+-- Examples:+--+-- >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList+-- >>> p2 = Parser.satisfy (== '+')+-- >>> p = Parser.sepBy p1 p2 Fold.toList+-- >>> Stream.parse p $ Stream.fromList ""+-- Right []+-- >>> Stream.parse p $ Stream.fromList "1"+-- Right ["1"]+-- >>> Stream.parse p $ Stream.fromList "1+"+-- Right ["1"]+-- >>> Stream.parse p $ Stream.fromList "1+2+3"+-- Right ["1","2","3"]+--+{-# INLINE sepBy #-}+sepBy :: Monad m =>+    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+-- This has similar performance as the custom impl below.+-- sepBy p1 p2 f = deintercalate p1 p2 (FL.catLefts f)+sepBy+    (Parser stepL initialL extractL)+    (Parser stepR initialR _)+    (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    errMsg p status =+        error $ "sepBy: " ++ p ++ " parser cannot "+                ++ status ++ " without input"++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> return $ IPartial $ SepByInitL fs+            FL.Done c -> return $ IDone c++    {-# INLINE processL #-}+    processL foldAction n nextState = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> return $ Partial n (nextState fs1)+            FL.Done c -> return $ Done n c++    {-# INLINE runStepL #-}+    runStepL cnt fs sL a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (SepByL (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (SepByL (cnt1 - n) fs s)+            Done n b ->+                processL (fstep fs b) n SepByInitR+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    {-# INLINE processR #-}+    processR cnt fs n = do+        res <- initialL+        case res of+            IPartial ps -> return $ Continue n (SepByL cnt fs ps)+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"++    {-# INLINE runStepR #-}+    runStepR cnt fs sR a = do+        let cnt1 = cnt + 1+        r <- stepR sR a+        case r of+            Partial n s -> return $ Continue n (SepByR (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (SepByR (cnt1 - n) fs s)+            Done n _ -> processR (cnt1 - n) fs n+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    step (SepByInitL fs) a = do+        res <- initialL+        case res of+            IPartial s -> runStepL 0 fs s a+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"+    step (SepByL cnt fs sL) a = runStepL cnt fs sL a+    step (SepByInitR fs) a = do+        res <- initialR+        case res of+            IPartial s -> runStepR 0 fs s a+            IDone _ -> errMsg "right" "succeed"+            IError _ -> errMsg "right" "fail"+    step (SepByR cnt fs sR) a = runStepR cnt fs sR a++    {-# INLINE extractResult #-}+    extractResult n fs r = do+        res <- fstep fs r+        case res of+            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+            FL.Done c -> return (Done n c)++    extract (SepByInitL fs) = fmap (Done 0) $ ffinal fs+    extract (SepByL cnt fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs b+            Continue n s -> return $ Continue n (SepByL (cnt - n) fs s)+            Partial _ _ -> error "Partial in extract"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs+    extract (SepByInitR fs) = fmap (Done 0) $ ffinal fs+    extract (SepByR cnt fs _) = fmap (Done cnt) $ ffinal fs++-- | Non-backtracking version of sepBy. Several times faster.+{-# INLINE sepByAll #-}+sepByAll :: Monad m =>+    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+sepByAll p1 p2 f = deintercalateAll p1 p2 (FL.catLefts f)++-- XXX This can be implemented using refold, parse one and then continue+-- collecting the rest in that.++{-# ANN type SepBy1State Fuse #-}+data SepBy1State fs sp ss =+      SepBy1InitL !Int !fs sp+    | SepBy1L !Int !fs !sp+    | SepBy1InitR !fs+    | SepBy1R !Int !fs !ss++{-+{-# INLINE sepBy1 #-}+sepBy1 :: Monad m =>+    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+sepBy1 p sep sink = do+    x <- p+    f <- fromEffect $ FL.reduce sink+    f1 <- fromEffect $ FL.snoc f x+    many (sep >> p) f1+-}++-- | Like 'sepBy' but requires at least one successful parse.+--+-- Definition:+--+-- >>> sepBy1 p1 p2 f = Parser.deintercalate1 p1 p2 (Fold.catLefts f)+--+-- Examples:+--+-- >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList+-- >>> p2 = Parser.satisfy (== '+')+-- >>> p = Parser.sepBy1 p1 p2 Fold.toList+-- >>> Stream.parse p $ Stream.fromList ""+-- Left (ParseError "takeWhile1: end of input")+-- >>> Stream.parse p $ Stream.fromList "1"+-- Right ["1"]+-- >>> Stream.parse p $ Stream.fromList "1+"+-- Right ["1"]+-- >>> Stream.parse p $ Stream.fromList "1+2+3"+-- Right ["1","2","3"]+--+{-# INLINE sepBy1 #-}+sepBy1 :: Monad m =>+    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+sepBy1+    (Parser stepL initialL extractL)+    (Parser stepR initialR _)+    (Fold fstep finitial _ ffinal) = Parser step initial extract++    where++    errMsg p status =+        error $ "sepBy: " ++ p ++ " parser cannot "+                ++ status ++ " without input"++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> do+                pres <- initialL+                case pres of+                    IPartial s -> return $ IPartial $ SepBy1InitL 0 fs s+                    IDone _ -> errMsg "left" "succeed"+                    IError _ -> errMsg "left" "fail"+            FL.Done c -> return $ IDone c++    {-# INLINE processL #-}+    processL foldAction n nextState = do+        fres <- foldAction+        case fres of+            FL.Partial fs1 -> return $ Partial n (nextState fs1)+            FL.Done c -> return $ Done n c++    {-# INLINE runStepInitL #-}+    runStepInitL cnt fs sL a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (SepBy1InitL (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (SepBy1InitL (cnt1 - n) fs s)+            Done n b ->+                processL (fstep fs b) n SepBy1InitR+            Error err -> return $ Error err++    {-# INLINE runStepL #-}+    runStepL cnt fs sL a = do+        let cnt1 = cnt + 1+        r <- stepL sL a+        case r of+            Partial n s -> return $ Continue n (SepBy1L (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (SepBy1L (cnt1 - n) fs s)+            Done n b ->+                processL (fstep fs b) n SepBy1InitR+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    {-# INLINE processR #-}+    processR cnt fs n = do+        res <- initialL+        case res of+            IPartial ps -> return $ Continue n (SepBy1L cnt fs ps)+            IDone _ -> errMsg "left" "succeed"+            IError _ -> errMsg "left" "fail"++    {-# INLINE runStepR #-}+    runStepR cnt fs sR a = do+        let cnt1 = cnt + 1+        r <- stepR sR a+        case r of+            Partial n s -> return $ Continue n (SepBy1R (cnt1 - n) fs s)+            Continue n s -> return $ Continue n (SepBy1R (cnt1 - n) fs s)+            Done n _ -> processR (cnt1 - n) fs n+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    step (SepBy1InitL cnt fs sL) a = runStepInitL cnt fs sL a+    step (SepBy1L cnt fs sL) a = runStepL cnt fs sL a+    step (SepBy1InitR fs) a = do+        res <- initialR+        case res of+            IPartial s -> runStepR 0 fs s a+            IDone _ -> errMsg "right" "succeed"+            IError _ -> errMsg "right" "fail"+    step (SepBy1R cnt fs sR) a = runStepR cnt fs sR a++    {-# INLINE extractResult #-}+    extractResult n fs r = do+        res <- fstep fs r+        case res of+            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+            FL.Done c -> return (Done n c)++    extract (SepBy1InitL cnt fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs b+            Continue n s -> return $ Continue n (SepBy1InitL (cnt - n) fs s)+            Partial _ _ -> error "Partial in extract"+            Error err -> return $ Error err+    extract (SepBy1L cnt fs sL) = do+        r <- extractL sL+        case r of+            Done n b -> extractResult n fs b+            Continue n s -> return $ Continue n (SepBy1L (cnt - n) fs s)+            Partial _ _ -> error "Partial in extract"+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs+    extract (SepBy1InitR fs) = fmap (Done 0) $ ffinal fs+    extract (SepBy1R cnt fs _) = fmap (Done cnt) $ ffinal fs++-------------------------------------------------------------------------------+-- Interleaving a collection of parsers+-------------------------------------------------------------------------------+--+-- | Apply a collection of parsers to an input stream in a round robin fashion.+-- Each parser is applied until it stops and then we repeat starting with the+-- the first parser again.+--+-- /Unimplemented/+--+{-# INLINE roundRobin #-}+roundRobin :: -- (Foldable t, Monad m) =>+    t (Parser a m b) -> Fold m b c -> Parser a m c+roundRobin _ps _f = undefined++-------------------------------------------------------------------------------+-- Sequential Collection+-------------------------------------------------------------------------------++-- | @sequence f p@ collects sequential parses of parsers in a+-- serial stream @p@ using the fold @f@. Fails if the input ends or any+-- of the parsers fail.+--+-- /Pre-release/+--+{-# INLINE sequence #-}+sequence :: Monad m =>+    D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c+sequence (D.Stream sstep sstate) (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    initial = do+        fres <- finitial+        case fres of+            FL.Partial fs -> return $ IPartial (Nothing', sstate, fs)+            FL.Done c -> return $ IDone c++    -- state does not contain any parser+    -- yield a new parser from the stream+    step (Nothing', ss, fs) _ = do+        sres <- sstep defState ss+        case sres of+            D.Yield p ss1 -> return $ Continue 1 (Just' p, ss1, fs)+            D.Stop -> do+                c <- ffinal fs+                return $ Done 1 c+            D.Skip ss1 -> return $ Continue 1 (Nothing', ss1, fs)++    -- state holds a parser that may or may not have been+    -- initialized. pinit holds the initial parser state+    -- or modified parser state respectively+    step (Just' (Parser pstep pinit pextr), ss, fs) a = do+        ps <- pinit+        case ps of+            IPartial ps1 -> do+                pres <- pstep ps1 a+                case pres of+                    Partial n ps2 ->+                        let newP =+                              Just' $ Parser pstep (return $ IPartial ps2) pextr+                        in return $ Partial n (newP, ss, fs)+                    Continue n ps2 ->+                        let newP =+                              Just' $ Parser pstep (return $ IPartial ps2) pextr+                        in return $ Continue n (newP, ss, fs)+                    Done n b -> do+                        fres <- fstep fs b+                        case fres of+                            FL.Partial fs1 ->+                                return $ Partial n (Nothing', ss, fs1)+                            FL.Done c -> return $ Done n c+                    Error msg -> return $ Error msg+            IDone b -> do+                fres <- fstep fs b+                case fres of+                    FL.Partial fs1 ->+                        return $ Partial 1 (Nothing', ss, fs1)+                    FL.Done c -> return $ Done 1 c+            IError err -> return $ Error err++    extract (Nothing', _, fs) = fmap (Done 0) $ ffinal fs+    extract (Just' (Parser pstep pinit pextr), ss, fs) = do+        ps <- pinit+        case ps of+            IPartial ps1 ->  do+                r <- pextr ps1+                case r of+                    Done n b -> do+                        res <- fstep fs b+                        case res of+                            FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+                            FL.Done c -> return (Done n c)+                    Error err -> return $ Error err+                    Continue n s -> return $ Continue n (Just' (Parser pstep (return (IPartial s)) pextr), ss, fs)+                    Partial _ _ -> error "Partial in extract"+            IDone b -> do+                fres <- fstep fs b+                case fres of+                    FL.Partial fs1 -> fmap (Done 0) $ ffinal fs1+                    FL.Done c -> return (Done 0 c)+            IError err -> return $ Error err++-------------------------------------------------------------------------------+-- Alternative Collection+-------------------------------------------------------------------------------++{-+-- | @choice parsers@ applies the @parsers@ in order and returns the first+-- successful parse.+--+-- This is same as 'asum' but more efficient.+--+-- /Broken/+--+{-# INLINE choice #-}+choice :: (MonadCatch m, Foldable t) => t (Parser a m b) -> Parser a m b+choice = foldl1 shortest+-}++-------------------------------------------------------------------------------+-- Sequential Repetition+-------------------------------------------------------------------------------++-- | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the+-- results. Parsing stops or fails if the collecting parser stops or fails.+--+-- /Unimplemented/+--+{-# INLINE manyP #-}+manyP :: -- MonadCatch m =>+    Parser a m b -> Parser b m c -> Parser a m c+manyP _p _f = undefined++-- | Collect zero or more parses. Apply the supplied parser repeatedly on the+-- input stream and push the parse results to a downstream fold.+--+--  Stops: when the downstream fold stops or the parser fails.+--  Fails: never, produces zero or more results.+--+-- >>> many = Parser.countBetween 0 maxBound+--+-- Compare with 'Control.Applicative.many'.+--+{-# INLINE many #-}+many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+many = splitMany+-- many = countBetween 0 maxBound++-- Note: many1 would perhaps be a better name for this and consistent with+-- other names like takeWhile1. But we retain the name "some" for+-- compatibility.++-- | Collect one or more parses. Apply the supplied parser repeatedly on the+-- input stream and push the parse results to a downstream fold.+--+--  Stops: when the downstream fold stops or the parser fails.+--  Fails: if it stops without producing a single result.+--+-- >>> some p f = Parser.manyP p (Parser.takeGE 1 f)+-- >>> some = Parser.countBetween 1 maxBound+--+-- Compare with 'Control.Applicative.some'.+--+{-# INLINE some #-}+some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+some = splitSome+-- some p f = manyP p (takeGE 1 f)+-- some = countBetween 1 maxBound++-- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of+-- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if+-- the input ends or the parser fails before @m@ results are collected.+--+-- >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)+--+-- /Unimplemented/+--+{-# INLINE countBetween #-}+countBetween :: -- MonadCatch m =>+    Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c+countBetween _m _n _p = undefined+-- countBetween m n p f = manyP p (takeBetween m n f)++-- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using+-- the fold @f@.  Fails if the input ends or the parser fails before @n@+-- results are collected.+--+-- >>> count n = Parser.countBetween n n+-- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)+--+-- /Unimplemented/+--+{-# INLINE count #-}+count :: -- MonadCatch m =>+    Int -> Parser a m b -> Fold m b c -> Parser a m c+count n = countBetween n n+-- count n p f = manyP p (takeEQ n f)++-- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a+-- 'Fold'.  Parsing stops or fails if the collecting parser stops or fails.+--+-- We can implemnent parsers like the following using 'manyTillP':+--+-- @+-- countBetweenTill m n f p = manyTillP (takeBetween m n f) p+-- @+--+-- /Unimplemented/+--+{-# INLINE manyTillP #-}+manyTillP :: -- Monad m =>+    Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c+manyTillP _p1 _p2 _f = undefined+    -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f++{-# ANN type ManyTillState Fuse #-}+data ManyTillState fs sr sl+    = ManyTillR !Int !fs !sr+    | ManyTillL !fs !sl++-- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@+-- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is+-- tried again and so on. The parser stops when @test@ succeeds.  The output of+-- @test@ is discarded and the output of @chunking@ is accumulated by the+-- supplied fold. The parser fails if @chunking@ fails.+--+-- Stops when the fold @f@ stops.+--+{-# INLINE manyTill #-}+manyTill :: Monad m+    => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+manyTill (Parser stepL initialL extractL)+         (Parser stepR initialR _)+         (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    -- Caution: Mutual recursion++    scrutL fs p c d e = do+        resL <- initialL+        case resL of+            IPartial sl -> return $ c (ManyTillL fs sl)+            IDone bl -> do+                fr <- fstep fs bl+                case fr of+                    FL.Partial fs1 -> scrutR fs1 p c d e+                    FL.Done fb -> return $ d fb+            IError err -> return $ e err++    scrutR fs p c d e = do+        resR <- initialR+        case resR of+            IPartial sr -> return $ p (ManyTillR 0 fs sr)+            IDone _ -> d <$> ffinal fs+            IError _ -> scrutL fs p c d e++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> scrutR fs IPartial IPartial IDone IError+            FL.Done b -> return $ IDone b++    step (ManyTillR cnt fs st) a = do+        r <- stepR st a+        case r of+            Partial n s -> return $ Partial n (ManyTillR 0 fs s)+            Continue n s -> do+                assertM(cnt + 1 - n >= 0)+                return $ Continue n (ManyTillR (cnt + 1 - n) fs s)+            Done n _ -> do+                b <- ffinal fs+                return $ Done n b+            Error _ -> do+                resL <- initialL+                case resL of+                    IPartial sl ->+                        return $ Continue (cnt + 1) (ManyTillL fs sl)+                    IDone bl -> do+                        fr <- fstep fs bl+                        let cnt1 = cnt + 1+                        case fr of+                            FL.Partial fs1 ->+                                scrutR+                                    fs1+                                    (Partial cnt1)+                                    (Continue cnt1)+                                    (Done cnt1)+                                    Error+                            FL.Done fb -> return $ Done cnt1 fb+                    IError err -> return $ Error err+    step (ManyTillL fs st) a = do+        r <- stepL st a+        case r of+            Partial n s -> return $ Partial n (ManyTillL fs s)+            Continue n s -> return $ Continue n (ManyTillL fs s)+            Done n b -> do+                fs1 <- fstep fs b+                case fs1 of+                    FL.Partial s ->+                        scrutR s (Partial n) (Continue n) (Done n) Error+                    FL.Done b1 -> return $ Done n b1+            Error err -> return $ Error err++    extract (ManyTillL fs sR) = do+        res <- extractL sR+        case res of+            Done n b -> do+                r <- fstep fs b+                case r of+                    FL.Partial fs1 -> fmap (Done n) $ ffinal fs1+                    FL.Done c -> return (Done n c)+            Error err -> return $ Error err+            Continue n s -> return $ Continue n (ManyTillL fs s)+            Partial _ _ -> error "Partial in extract"+    extract (ManyTillR _ fs _) = fmap (Done 0) $ ffinal fs++-- | @manyThen f collect recover@ repeats the parser @collect@ on the input and+-- collects the output in the supplied fold. If the the parser @collect@ fails,+-- parser @recover@ is run until it stops and then we start repeating the+-- parser @collect@ again. The parser fails if the recovery parser fails.+--+-- For example, this can be used to find a key frame in a video stream after an+-- error.+--+-- /Unimplemented/+--+{-# INLINE manyThen #-}+manyThen :: -- (Foldable t, Monad m) =>+    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c+manyThen _parser _recover _f = undefined++-------------------------------------------------------------------------------+-- Repeated Alternatives+-------------------------------------------------------------------------------++-- | Keep trying a parser up to a maximum of @n@ failures.  When the parser+-- fails the input consumed till now is dropped and the new instance is tried+-- on the fresh input.+--+-- /Unimplemented/+--+{-# INLINE retryMaxTotal #-}+retryMaxTotal :: -- (Monad m) =>+    Int -> Parser a m b -> Fold m b c -> Parser a m c+retryMaxTotal _n _p _f  = undefined++-- | Like 'retryMaxTotal' but aborts after @n@ successive failures.+--+-- /Unimplemented/+--+{-# INLINE retryMaxSuccessive #-}+retryMaxSuccessive :: -- (Monad m) =>+    Int -> Parser a m b -> Fold m b c -> Parser a m c+retryMaxSuccessive _n _p _f = undefined++-- | Keep trying a parser until it succeeds.  When the parser fails the input+-- consumed till now is dropped and the new instance is tried on the fresh+-- input.+--+-- /Unimplemented/+--+{-# INLINE retry #-}+retry :: -- (Monad m) =>+    Parser a m b -> Parser a m b+retry _p = undefined
− src/Streamly/Internal/Data/Parser/ParserD.hs
@@ -1,3629 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Parser.ParserD--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--module Streamly.Internal.Data.Parser.ParserD-    (-    -- * Setup-    -- $setup--    -- * Types-      Parser (..)-    , ParseError (..)-    , Step (..)-    , Initial (..)--    -- * Downgrade to Fold-    , toFold--    -- First order parsers-    -- * Accumulators-    , fromFold-    , fromFoldMaybe-    , fromPure-    , fromEffect-    , die-    , dieM--    -- * Map on input-    , lmap-    , lmapM-    , postscan-    , filter--    -- * Map on output-    , rmapM--    -- * Element parsers-    , peek--    -- All of these can be expressed in terms of either-    , one-    , oneEq-    , oneNotEq-    , oneOf-    , noneOf-    , eof-    , satisfy-    , maybe-    , either--    -- * Sequence parsers (tokenizers)-    ---    -- | Parsers chained in series, if one parser terminates the composition-    -- terminates. Currently we are using folds to collect the output of the-    -- parsers but we can use Parsers instead of folds to make the composition-    -- more powerful. For example, we can do:-    ---    -- takeEndByOrMax cond n p = takeEndBy cond (take n p)-    -- takeEndByBetween cond m n p = takeEndBy cond (takeBetween m n p)-    -- takeWhileBetween cond m n p = takeWhile cond (takeBetween m n p)-    , lookAhead--    -- ** By length-    -- | Grab a sequence of input elements without inspecting them-    , takeBetween-    -- , take -- takeBetween 0 n-    , takeEQ -- takeBetween n n-    , takeGE -- takeBetween n maxBound-    -- , takeGE1 -- take1 -- takeBetween 1 n-    , takeP--    -- Grab a sequence of input elements by inspecting them-    -- ** Exact match-    , listEq-    , listEqBy-    , streamEqBy-    , subsequenceBy--    -- ** By predicate-    , takeWhile-    , takeWhileP-    , takeWhile1-    , dropWhile--    -- ** Separated by elements-    -- | Separator could be in prefix postion ('takeStartBy'), or suffix-    -- position ('takeEndBy'). See 'deintercalate', 'sepBy' etc for infix-    -- separator parsing, also see 'intersperseQuotedBy' fold.--    -- These can be implemented modularly with refolds, using takeWhile and-    -- satisfy.-    , takeEndBy-    , takeEndBy_-    , takeEndByEsc-    -- , takeEndByEsc_-    , takeStartBy-    , takeStartBy_-    , takeEitherSepBy-    , wordBy--    -- ** Grouped by element comparison-    , groupBy-    , groupByRolling-    , groupByRollingEither--    -- ** Framed by elements-    -- | Also see 'intersperseQuotedBy' fold.-    -- Framed by a one or more ocurrences of a separator around a word like-    -- spaces or quotes. No nesting.-    , wordFramedBy -- XXX Remove this? Covered by wordWithQuotes?-    , wordWithQuotes-    , wordKeepQuotes-    , wordProcessQuotes--    -- Framed by separate start and end characters, potentially nested.-    -- blockWithQuotes allows quotes inside a block. However,-    -- takeFramedByGeneric can be used to express takeStartBy, takeEndBy and-    -- block with escaping.-    -- , takeFramedBy-    , takeFramedBy_-    , takeFramedByEsc_-    , takeFramedByGeneric-    , blockWithQuotes--    -- Matching strings-    -- , prefixOf -- match any prefix of a given string-    -- , suffixOf -- match any suffix of a given string-    -- , infixOf -- match any substring of a given string--    -- ** Spanning-    , span-    , spanBy-    , spanByRolling--    -- Second order parsers (parsers using parsers)-    -- * Binary Combinators--    -- ** Sequential Applicative-    , splitWith-    , split_--    {--    -- ** Parallel Applicatives-    , teeWith-    , teeWithFst-    , teeWithMin-    -- , teeTill -- like manyTill but parallel-    -}--    -- ** Sequential Alternative-    , alt--    {--    -- ** Parallel Alternatives-    , shortest-    , longest-    -- , fastest-    -}--    -- * N-ary Combinators-    -- ** Sequential Collection-    , sequence-    , concatMap--    -- ** Sequential Repetition-    , count-    , countBetween-    -- , countBetweenTill-    , manyP-    , many-    , some--    -- ** Interleaved Repetition-    -- Use two folds, run a primary parser, its rejected values go to the-    -- secondary parser.-    , deintercalate-    , deintercalate1-    , deintercalateAll-    -- , deintercalatePrefix-    -- , deintercalateSuffix--    -- *** Special cases-    -- | TODO: traditional implmentations of these may be of limited use. For-    -- example, consider parsing lines separated by @\\r\\n@. The main parser-    -- will have to detect and exclude the sequence @\\r\\n@ anyway so that we-    -- can apply the "sep" parser.-    ---    -- We can instead implement these as special cases of deintercalate.-    ---    -- @-    -- , endBy-    -- , sepEndBy-    -- , beginBy-    -- , sepBeginBy-    -- , sepAroundBy-    -- @-    , sepBy1-    , sepBy-    , sepByAll--    , manyTillP-    , manyTill-    , manyThen--    -- -- * Distribution-    ---    -- A simple and stupid impl would be to just convert the stream to an array-    -- and give the array reference to all consumers. The array can be grown on-    -- demand by any consumer and truncated when nonbody needs it.-    ---    -- -- ** Distribute to collection-    -- -- ** Distribute to repetition--    -- ** Interleaved collection-    -- |-    ---    -- 1. Round robin-    -- 2. Priority based-    , roundRobin--    -- -- ** Interleaved repetition-    -- repeat one parser and when it fails run an error recovery parser-    -- e.g. to find a key frame in the stream after an error--    -- ** Collection of Alternatives-    -- | Unimplemented-    ---    -- @-    -- , shortestN-    -- , longestN-    -- , fastestN -- first N successful in time-    -- , choiceN  -- first N successful in position-    -- @-    -- , choice   -- first successful in position--    -- ** Repeated Alternatives-    , retryMaxTotal-    , retryMaxSuccessive-    , retry--    -- ** Zipping Input-    , zipWithM-    , zip-    , indexed-    , makeIndexFilter-    , sampleFromthen--     -- * Deprecated-    , next-    )-where--#include "inline.hs"-#include "assert.hs"--import Control.Monad (when)-import Data.Bifunctor (first)-import Fusion.Plugin.Types (Fuse(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.SVar.Type (defState)-import Streamly.Internal.Data.Either.Strict (Either'(..))-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)--import qualified Data.Foldable as Foldable-import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D--import Prelude hiding-       (any, all, take, takeWhile, sequence, concatMap, maybe, either, span-       , zip, filter, dropWhile)--- import Streamly.Internal.Data.Parser.ParserD.Tee-import Streamly.Internal.Data.Parser.ParserD.Type--#include "DocTestDataParser.hs"------------------------------------------------------------------------------------ Downgrade a parser to a Fold------------------------------------------------------------------------------------ | Make a 'Fold' from a 'Parser'. The fold just throws an exception if the--- parser fails or tries to backtrack.------ This can be useful in combinators that accept a Fold and we know that a--- Parser cannot fail or failure exception is acceptable as there is no way to--- recover.------ /Pre-release/----{-# INLINE toFold #-}-toFold :: Monad m => Parser a m b -> Fold m a b-toFold (Parser pstep pinitial pextract) = Fold step initial extract--    where--    initial = do-        r <- pinitial-        case r of-            IPartial s -> return $ FL.Partial s-            IDone b -> return $ FL.Done b-            IError err ->-                error $ "toFold: parser throws error in initial" ++ err--    perror n = error $ "toFold: parser backtracks in Partial: " ++ show n-    cerror n = error $ "toFold: parser backtracks in Continue: " ++ show n-    derror n = error $ "toFold: parser backtracks in Done: " ++ show n-    eerror err = error $ "toFold: parser throws error: " ++ err--    step st a = do-        r <- pstep st a-        case r of-            Partial 0 s -> return $ FL.Partial s-            Continue 0 s -> return $ FL.Partial s-            Done 0 b -> return $ FL.Done b-            Partial n _ -> perror n-            Continue n _ -> cerror n-            Done n _ -> derror n-            Error err -> eerror err--    extract st = do-        r <- pextract st-        case r of-            Done 0 b -> return b-            Partial n _ -> perror n-            Continue n _ -> cerror n-            Done n _ -> derror n-            Error err -> eerror err------------------------------------------------------------------------------------ Upgrade folds to parses------------------------------------------------------------------------------------ | Make a 'Parser' from a 'Fold'. This parser sends all of its input to the--- fold.----{-# INLINE fromFold #-}-fromFold :: Monad m => Fold m a b -> Parser a m b-fromFold (Fold fstep finitial fextract) = Parser step initial extract--    where--    initial = do-        res <- finitial-        return-            $ case res of-                  FL.Partial s1 -> IPartial s1-                  FL.Done b -> IDone b--    step s a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Partial s1 -> Partial 0 s1-                  FL.Done b -> Done 0 b--    extract = fmap (Done 0) . fextract---- | Convert a Maybe returning fold to an error returning parser. The first--- argument is the error message that the parser would return when the fold--- returns Nothing.------ /Pre-release/----{-# INLINE fromFoldMaybe #-}-fromFoldMaybe :: Monad m => String -> Fold m a (Maybe b) -> Parser a m b-fromFoldMaybe errMsg (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        res <- finitial-        return-            $ case res of-                  FL.Partial s1 -> IPartial s1-                  FL.Done b ->-                        case b of-                            Just x -> IDone x-                            Nothing -> IError errMsg--    step s a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Partial s1 -> Partial 0 s1-                  FL.Done b ->-                        case b of-                            Just x -> Done 0 x-                            Nothing -> Error errMsg--    extract s = do-        res <- fextract s-        case res of-            Just x -> return $ Done 0 x-            Nothing -> return $ Error errMsg------------------------------------------------------------------------------------ Failing Parsers------------------------------------------------------------------------------------ | Peek the head element of a stream, without consuming it. Fails if it--- encounters end of input.------ >>> Stream.parse ((,) <$> Parser.peek <*> Parser.satisfy (> 0)) $ Stream.fromList [1]--- Right (1,1)------ @--- peek = lookAhead (satisfy True)--- @----{-# INLINE peek #-}-peek :: Monad m => Parser a m a-peek = Parser step initial extract--    where--    initial = return $ IPartial ()--    step () a = return $ Done 1 a--    extract () = return $ Error "peek: end of input"---- | Succeeds if we are at the end of input, fails otherwise.------ >>> Stream.parse ((,) <$> Parser.satisfy (> 0) <*> Parser.eof) $ Stream.fromList [1]--- Right (1,())----{-# INLINE eof #-}-eof :: Monad m => Parser a m ()-eof = Parser step initial extract--    where--    initial = return $ IPartial ()--    step () _ = return $ Error "eof: not at end of input"--    extract () = return $ Done 0 ()---- | Return the next element of the input. Returns 'Nothing'--- on end of input. Also known as 'head'.------ /Pre-release/----{-# DEPRECATED next "Please use \"fromFold Fold.one\" instead" #-}-{-# INLINE next #-}-next :: Monad m => Parser a m (Maybe a)-next = Parser step initial extract--  where--  initial = pure $ IPartial ()--  step () a = pure $ Done 0 (Just a)--  extract () = pure $ Done 0 Nothing---- | Map an 'Either' returning function on the next element in the stream.  If--- the function returns 'Left err', the parser fails with the error message--- @err@ otherwise returns the 'Right' value.------ /Pre-release/----{-# INLINE either #-}-either :: Monad m => (a -> Either String b) -> Parser a m b-either f = Parser step initial extract--    where--    initial = return $ IPartial ()--    step () a = return $-        case f a of-            Right b -> Done 0 b-            Left err -> Error err--    extract () = return $ Error "end of input"---- | Map a 'Maybe' returning function on the next element in the stream. The--- parser fails if the function returns 'Nothing' otherwise returns the 'Just'--- value.------ >>> toEither = Maybe.maybe (Left "maybe: predicate failed") Right--- >>> maybe f = Parser.either (toEither . f)------ >>> maybe f = Parser.fromFoldMaybe "maybe: predicate failed" (Fold.maybe f)------ /Pre-release/----{-# INLINE maybe #-}-maybe :: Monad m => (a -> Maybe b) -> Parser a m b--- maybe f = either (Maybe.maybe (Left "maybe: predicate failed") Right . f)-maybe parserF = Parser step initial extract--    where--    initial = return $ IPartial ()--    step () a = return $-        case parserF a of-            Just b -> Done 0 b-            Nothing -> Error "maybe: predicate failed"--    extract () = return $ Error "maybe: end of input"---- | Returns the next element if it passes the predicate, fails otherwise.------ >>> Stream.parse (Parser.satisfy (== 1)) $ Stream.fromList [1,0,1]--- Right 1------ >>> toMaybe f x = if f x then Just x else Nothing--- >>> satisfy f = Parser.maybe (toMaybe f)----{-# INLINE satisfy #-}-satisfy :: Monad m => (a -> Bool) -> Parser a m a--- satisfy predicate = maybe (\a -> if predicate a then Just a else Nothing)-satisfy predicate = Parser step initial extract--    where--    initial = return $ IPartial ()--    step () a = return $-        if predicate a-        then Done 0 a-        else Error "satisfy: predicate failed"--    extract () = return $ Error "satisfy: end of input"---- | Consume one element from the head of the stream.  Fails if it encounters--- end of input.------ >>> one = Parser.satisfy $ const True----{-# INLINE one #-}-one :: Monad m => Parser a m a-one = satisfy $ const True---- Alternate names: "only", "onlyThis".---- | Match a specific element.------ >>> oneEq x = Parser.satisfy (== x)----{-# INLINE oneEq #-}-oneEq :: (Monad m, Eq a) => a -> Parser a m a-oneEq x = satisfy (== x)---- Alternate names: "exclude", "notThis".---- | Match anything other than the supplied element.------ >>> oneNotEq x = Parser.satisfy (/= x)----{-# INLINE oneNotEq #-}-oneNotEq :: (Monad m, Eq a) => a -> Parser a m a-oneNotEq x = satisfy (/= x)---- | Match any one of the elements in the supplied list.------ >>> oneOf xs = Parser.satisfy (`Foldable.elem` xs)------ When performance matters a pattern matching predicate could be more--- efficient than a 'Foldable' datatype:------ @--- let p x =---    case x of---       'a' -> True---       'e' -> True---        _  -> False--- in satisfy p--- @------ GHC may use a binary search instead of linear search in the list.--- Alternatively, you can also use an array instead of list for storage and--- search.----{-# INLINE oneOf #-}-oneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a-oneOf xs = satisfy (`Foldable.elem` xs)---- | See performance notes in 'oneOf'.------ >>> noneOf xs = Parser.satisfy (`Foldable.notElem` xs)----{-# INLINE noneOf #-}-noneOf :: (Monad m, Eq a, Foldable f) => f a -> Parser a m a-noneOf xs = satisfy (`Foldable.notElem` xs)------------------------------------------------------------------------------------ Taking elements------------------------------------------------------------------------------------ Required to fuse "take" with "many" in "chunksOf", for ghc-9.x-{-# ANN type Tuple'Fused Fuse #-}-data Tuple'Fused a b = Tuple'Fused !a !b deriving Show---- | @takeBetween m n@ takes a minimum of @m@ and a maximum of @n@ input--- elements and folds them using the supplied fold.------ Stops after @n@ elements.--- Fails if the stream ends before @m@ elements could be taken.------ Examples: ------- @--- >>> :{---   takeBetween' low high ls = Stream.parse prsr (Stream.fromList ls)---     where prsr = Parser.takeBetween low high Fold.toList--- :}------ @------ >>> takeBetween' 2 4 [1, 2, 3, 4, 5]--- Right [1,2,3,4]------ >>> takeBetween' 2 4 [1, 2]--- Right [1,2]------ >>> takeBetween' 2 4 [1]--- Left (ParseError "takeBetween: Expecting alteast 2 elements, got 1")------ >>> takeBetween' 0 0 [1, 2]--- Right []------ >>> takeBetween' 0 1 []--- Right []------ @takeBetween@ is the most general take operation, other take operations can--- be defined in terms of takeBetween. For example:------ >>> take n = Parser.takeBetween 0 n--- >>> takeEQ n = Parser.takeBetween n n--- >>> takeGE n = Parser.takeBetween n maxBound------ /Pre-release/----{-# INLINE takeBetween #-}-takeBetween :: Monad m => Int -> Int -> Fold m a b -> Parser a m b-takeBetween low high (Fold fstep finitial fextract) =--    Parser step initial (extract streamErr)--    where--    streamErr i =-           "takeBetween: Expecting alteast " ++ show low-        ++ " elements, got " ++ show i--    invalidRange =-        "takeBetween: lower bound - " ++ show low-            ++ " is greater than higher bound - " ++ show high--    foldErr :: Int -> String-    foldErr i =-        "takeBetween: the collecting fold terminated after"-            ++ " consuming" ++ show i ++ " elements"-            ++ " minimum" ++ show low ++ " elements needed"--    -- Exactly the same as snext except different constructors, we can possibly-    -- deduplicate the two.-    {-# INLINE inext #-}-    inext i res =-        let i1 = i + 1-        in case res of-            FL.Partial s -> do-                let s1 = Tuple'Fused i1 s-                if i1 < high-                -- XXX ideally this should be a Continue instead-                then return $ IPartial s1-                else iextract foldErr s1-            FL.Done b ->-                return-                    $ if i1 >= low-                      then IDone b-                      else IError (foldErr i1)--    initial = do-        when (low >= 0 && high >= 0 && low > high)-            $ error invalidRange--        finitial >>= inext (-1)--    -- Keep the impl same as inext-    {-# INLINE snext #-}-    snext i res =-        let i1 = i + 1-        in case res of-            FL.Partial s -> do-                let s1 = Tuple'Fused i1 s-                if i1 < high-                then return $ Continue 0 s1-                else extract foldErr s1-            FL.Done b ->-                return-                    $ if i1 >= low-                      then Done 0 b-                      else Error (foldErr i1)--    step (Tuple'Fused i s) a = fstep s a >>= snext i--    extract f (Tuple'Fused i s)-        | i >= low && i <= high = fmap (Done 0) (fextract s)-        | otherwise = return $ Error (f i)--    -- XXX Need to make Initial return type Step to deduplicate this-    iextract f (Tuple'Fused i s)-        | i >= low && i <= high = fmap IDone (fextract s)-        | otherwise = return $ IError (f i)---- | Stops after taking exactly @n@ input elements.------ * Stops - after consuming @n@ elements.--- * Fails - if the stream or the collecting fold ends before it can collect---           exactly @n@ elements.------ >>> Stream.parse (Parser.takeEQ 2 Fold.toList) $ Stream.fromList [1,0,1]--- Right [1,0]------ >>> Stream.parse (Parser.takeEQ 4 Fold.toList) $ Stream.fromList [1,0,1]--- Left (ParseError "takeEQ: Expecting exactly 4 elements, input terminated on 3")----{-# INLINE takeEQ #-}-takeEQ :: Monad m => Int -> Fold m a b -> Parser a m b-takeEQ n (Fold fstep finitial fextract) = Parser step initial extract--    where--    initial = do-        res <- finitial-        case res of-            FL.Partial s ->-                if n > 0-                then return $ IPartial $ Tuple'Fused 1 s-                else fmap IDone (fextract s)-            FL.Done b -> return $-                if n > 0-                then IError-                         $ "takeEQ: Expecting exactly " ++ show n-                             ++ " elements, fold terminated without"-                             ++ " consuming any elements"-                else IDone b--    step (Tuple'Fused i1 r) a = do-        res <- fstep r a-        if n > i1-        then-            return-                $ case res of-                    FL.Partial s -> Continue 0 $ Tuple'Fused (i1 + 1) s-                    FL.Done _ ->-                        Error-                            $ "takeEQ: Expecting exactly " ++ show n-                                ++ " elements, fold terminated on " ++ show i1-        else-            -- assert (n == i1)-            Done 0-                <$> case res of-                        FL.Partial s -> fextract s-                        FL.Done b -> return b--    extract (Tuple'Fused i _) =-        -- Using the count "i" in the message below causes large performance-        -- regression unless we use Fuse annotation on Tuple.-        return-            $ Error-            $ "takeEQ: Expecting exactly " ++ show n-                ++ " elements, input terminated on " ++ show (i - 1)--{-# ANN type TakeGEState Fuse #-}-data TakeGEState s =-      TakeGELT !Int !s-    | TakeGEGE !s---- | Take at least @n@ input elements, but can collect more.------ * Stops - when the collecting fold stops.--- * Fails - if the stream or the collecting fold ends before producing @n@---           elements.------ >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1]--- Left (ParseError "takeGE: Expecting at least 4 elements, input terminated on 3")------ >>> Stream.parse (Parser.takeGE 4 Fold.toList) $ Stream.fromList [1,0,1,0,1]--- Right [1,0,1,0,1]------ /Pre-release/----{-# INLINE takeGE #-}-takeGE :: Monad m => Int -> Fold m a b -> Parser a m b-takeGE n (Fold fstep finitial fextract) = Parser step initial extract--    where--    initial = do-        res <- finitial-        case res of-            FL.Partial s ->-                if n > 0-                then return $ IPartial $ TakeGELT 1 s-                else return $ IPartial $ TakeGEGE s-            FL.Done b -> return $-                if n > 0-                then IError-                         $ "takeGE: Expecting at least " ++ show n-                             ++ " elements, fold terminated without"-                             ++ " consuming any elements"-                else IDone b--    step (TakeGELT i1 r) a = do-        res <- fstep r a-        if n > i1-        then-            return-                $ case res of-                      FL.Partial s -> Continue 0 $ TakeGELT (i1 + 1) s-                      FL.Done _ ->-                        Error-                            $ "takeGE: Expecting at least " ++ show n-                                ++ " elements, fold terminated on " ++ show i1-        else-            -- assert (n <= i1)-            return-                $ case res of-                      FL.Partial s -> Partial 0 $ TakeGEGE s-                      FL.Done b -> Done 0 b-    step (TakeGEGE r) a = do-        res <- fstep r a-        return-            $ case res of-                  FL.Partial s -> Partial 0 $ TakeGEGE s-                  FL.Done b -> Done 0 b--    extract (TakeGELT i _) =-        return-            $ Error-            $ "takeGE: Expecting at least " ++ show n-                ++ " elements, input terminated on " ++ show (i - 1)-    extract (TakeGEGE r) = fmap (Done 0) $ fextract r------------------------------------------------------------------------------------ Conditional splitting------------------------------------------------------------------------------------ XXX We should perhaps use only takeWhileP and rename it to takeWhile.---- | Like 'takeWhile' but uses a 'Parser' instead of a 'Fold' to collect the--- input. The combinator stops when the condition fails or if the collecting--- parser stops.------ Other interesting parsers can be implemented in terms of this parser:------ >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)--- >>> takeWhileBetween cond m n p = Parser.takeWhileP cond (Parser.takeBetween m n p)------ Stops: when the condition fails or the collecting parser stops.--- Fails: when the collecting parser fails.------ /Pre-release/----{-# INLINE takeWhileP #-}-takeWhileP :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b-takeWhileP predicate (Parser pstep pinitial pextract) =-    Parser step pinitial pextract--    where--    step s a =-        if predicate a-        then pstep s a-        else do-            r <- pextract s-            -- XXX need a map on count-            case r of-                Error err -> return $ Error err-                Done n s1 -> return $ Done (n + 1) s1-                Partial _ _ -> error "Bug: takeWhileP: Partial in extract"-                Continue n s1 -> return $ Continue (n + 1) s1---- | Collect stream elements until an element fails the predicate. The element--- on which the predicate fails is returned back to the input stream.------ * Stops - when the predicate fails or the collecting fold stops.--- * Fails - never.------ >>> Stream.parse (Parser.takeWhile (== 0) Fold.toList) $ Stream.fromList [0,0,1,0,1]--- Right [0,0]------ >>> takeWhile cond f = Parser.takeWhileP cond (Parser.fromFold f)------ We can implement a @breakOn@ using 'takeWhile':------ @--- breakOn p = takeWhile (not p)--- @----{-# INLINE takeWhile #-}-takeWhile :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b--- takeWhile cond f = takeWhileP cond (fromFold f)-takeWhile predicate (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        res <- finitial-        return $ case res of-            FL.Partial s -> IPartial s-            FL.Done b -> IDone b--    step s a =-        if predicate a-        then do-            fres <- fstep s a-            return-                $ case fres of-                      FL.Partial s1 -> Partial 0 s1-                      FL.Done b -> Done 0 b-        else Done 1 <$> fextract s--    extract s = fmap (Done 0) (fextract s)--{---- XXX This may not be composable because of the b argument. We can instead--- return a "Reparse b a m b" so that those can be composed.-{-# INLINE takeWhile1X #-}-takeWhile1 :: Monad m => b -> (a -> Bool) -> Refold m b a b -> Parser a m b--- We can implement this using satisfy and takeWhile. We can use "satisfy--- p", fold the result with the refold and then use the "takeWhile p" and--- fold that using the refold.-takeWhile1 acc cond f = undefined--}---- | Like 'takeWhile' but takes at least one element otherwise fails.------ >>> takeWhile1 cond p = Parser.takeWhileP cond (Parser.takeBetween 1 maxBound p)----{-# INLINE takeWhile1 #-}-takeWhile1 :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b--- takeWhile1 cond f = takeWhileP cond (takeBetween 1 maxBound f)-takeWhile1 predicate (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        res <- finitial-        return $ case res of-            FL.Partial s -> IPartial (Left' s)-            FL.Done _ ->-                IError-                    $ "takeWhile1: fold terminated without consuming:"-                          ++ " any element"--    {-# INLINE process #-}-    process s a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Partial s1 -> Partial 0 (Right' s1)-                  FL.Done b -> Done 0 b--    step (Left' s) a =-        if predicate a-        then process s a-        else return $ Error "takeWhile1: predicate failed on first element"-    step (Right' s) a =-        if predicate a-        then process s a-        else do-            b <- fextract s-            return $ Done 1 b--    extract (Left' _) = return $ Error "takeWhile1: end of input"-    extract (Right' s) = fmap (Done 0) (fextract s)---- | Drain the input as long as the predicate succeeds, running the effects and--- discarding the results.------ This is also called @skipWhile@ in some parsing libraries.------ >>> dropWhile p = Parser.takeWhile p Fold.drain----{-# INLINE dropWhile #-}-dropWhile :: Monad m => (a -> Bool) -> Parser a m ()-dropWhile p = takeWhile p FL.drain------------------------------------------------------------------------------------ Separators----------------------------------------------------------------------------------{-# ANN type FramedEscState Fuse #-}-data FramedEscState s =-    FrameEscInit !s | FrameEscGo !s !Int | FrameEscEsc !s !Int---- XXX We can remove Maybe from esc-{-# INLINE takeFramedByGeneric #-}-takeFramedByGeneric :: Monad m =>-       Maybe (a -> Bool) -- is escape char?-    -> Maybe (a -> Bool) -- is frame begin?-    -> Maybe (a -> Bool) -- is frame end?-    -> Fold m a b-    -> Parser a m b-takeFramedByGeneric esc begin end (Fold fstep finitial fextract) =--    Parser step initial extract--    where--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (FrameEscInit s)-                FL.Done _ ->-                    error "takeFramedByGeneric: fold done without input"--    {-# INLINE process #-}-    process s a n = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)-                FL.Done b -> Done 0 b--    {-# INLINE processNoEsc #-}-    processNoEsc s a n =-        case end of-            Just isEnd ->-                case begin of-                    Just isBegin ->-                        -- takeFramedBy case-                        if isEnd a-                        then-                            if n == 0-                            then Done 0 <$> fextract s-                            else process s a (n - 1)-                        else-                            let n1 = if isBegin a then n + 1 else n-                             in process s a n1-                    Nothing -> -- takeEndBy case-                        if isEnd a-                        then Done 0 <$> fextract s-                        else process s a n-            Nothing -> -- takeStartBy case-                case begin of-                    Just isBegin ->-                        if isBegin a-                        then Done 0 <$> fextract s-                        else process s a n-                    Nothing ->-                        error $ "takeFramedByGeneric: "-                            ++ "Both begin and end frame predicate missing"--    {-# INLINE processCheckEsc #-}-    processCheckEsc s a n =-        case esc of-            Just isEsc ->-                if isEsc a-                then return $ Partial 0 $ FrameEscEsc s n-                else processNoEsc s a n-            Nothing -> processNoEsc s a n--    step (FrameEscInit s) a =-        case begin of-            Just isBegin ->-                if isBegin a-                then return $ Partial 0 (FrameEscGo s 0)-                else return $ Error "takeFramedByGeneric: missing frame start"-            Nothing ->-                case end of-                    Just isEnd ->-                        if isEnd a-                        then Done 0 <$> fextract s-                        else processCheckEsc s a 0-                    Nothing ->-                        error "Both begin and end frame predicate missing"-    step (FrameEscGo s n) a = processCheckEsc s a n-    step (FrameEscEsc s n) a = process s a n--    err = return . Error--    extract (FrameEscInit _) =-        err "takeFramedByGeneric: empty token"-    extract (FrameEscGo s _) =-        case begin of-            Just _ ->-                case end of-                    Nothing -> fmap (Done 0) $ fextract s-                    Just _ -> err "takeFramedByGeneric: missing frame end"-            Nothing -> err "takeFramedByGeneric: missing closing frame"-    extract (FrameEscEsc _ _) = err "takeFramedByGeneric: trailing escape"--data BlockParseState s =-      BlockInit !s-    | BlockUnquoted !Int !s-    | BlockQuoted !Int !s-    | BlockQuotedEsc !Int !s---- Blocks can be of different types e.g. {} or (). We only parse from the--- perspective of the outermost block type. The nesting of that block are--- checked. Any other block types nested inside it are opaque to us and can be--- parsed when the contents of the block are parsed.---- XXX Put a limit on nest level to keep the API safe.---- | Parse a block enclosed within open, close brackets. Block contents may be--- quoted, brackets inside quotes are ignored. Quoting characters can be used--- within quotes if escaped. A block can have a nested block inside it.------ Quote begin and end chars are the same. Block brackets and quote chars must--- not overlap. Block start and end brackets must be different for nesting--- blocks within blocks.------ >>> p = Parser.blockWithQuotes (== '\\') (== '"') '{' '}' Fold.toList--- >>> Stream.parse p $ Stream.fromList "{msg: \"hello world\"}"--- Right "msg: \"hello world\""----{-# INLINE blockWithQuotes #-}-blockWithQuotes :: (Monad m, Eq a) =>-       (a -> Bool)  -- ^ escape char-    -> (a -> Bool)  -- ^ quote char, to quote inside brackets-    -> a  -- ^ Block opening bracket-    -> a  -- ^ Block closing bracket-    -> Fold m a b-    -> Parser a m b-blockWithQuotes isEsc isQuote bopen bclose-    (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (BlockInit s)-                FL.Done _ ->-                    error "blockWithQuotes: fold finished without input"--    {-# INLINE process #-}-    process s a nextState = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (nextState s1)-                FL.Done b -> Done 0 b--    step (BlockInit s) a =-        return-            $ if a == bopen-              then Continue 0 $ BlockUnquoted 1 s-              else Error "blockWithQuotes: missing block start"-    step (BlockUnquoted level s) a-        | a == bopen = process s a (BlockUnquoted (level + 1))-        | a == bclose =-            if level == 1-            then fmap (Done 0) (fextract s)-            else process s a (BlockUnquoted (level - 1))-        | isQuote a = process s a (BlockQuoted level)-        | otherwise = process s a (BlockUnquoted level)-    step (BlockQuoted level s) a-        | isEsc a = process s a (BlockQuotedEsc level)-        | otherwise =-            if isQuote a-            then process s a (BlockUnquoted level)-            else process s a (BlockQuoted level)-    step (BlockQuotedEsc level s) a = process s a (BlockQuoted level)--    err = return . Error--    extract (BlockInit s) = fmap (Done 0) $ fextract s-    extract (BlockUnquoted level _) =-        err $ "blockWithQuotes: finished at block nest level " ++ show level-    extract (BlockQuoted level _) =-        err $ "blockWithQuotes: finished, inside an unfinished quote, "-            ++ "at block nest level " ++ show level-    extract (BlockQuotedEsc level _) =-        err $ "blockWithQuotes: finished, inside an unfinished quote, "-            ++ "after an escape char, at block nest level " ++ show level---- | @takeEndBy cond parser@ parses a token that ends by a separator chosen by--- the supplied predicate. The separator is also taken with the token.------ This can be combined with other parsers to implement other interesting--- parsers as follows:------ >>> takeEndByLE cond n p = Parser.takeEndBy cond (Parser.fromFold $ Fold.take n p)--- >>> takeEndByBetween cond m n p = Parser.takeEndBy cond (Parser.takeBetween m n p)------ >>> takeEndBy = Parser.takeEndByEsc (const False)------ See also "Streamly.Data.Fold.takeEndBy". Unlike the fold, the collecting--- parser in the takeEndBy parser can decide whether to fail or not if the--- stream does not end with separator.------ /Pre-release/----{-# INLINE takeEndBy #-}-takeEndBy :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b--- takeEndBy = takeEndByEsc (const False)-takeEndBy cond (Parser pstep pinitial pextract) =--    Parser step initial pextract--    where--    initial = pinitial--    step s a = do-        res <- pstep s a-        if not (cond a)-        then return res-        else extractStep pextract res---- | Like 'takeEndBy' but the separator elements can be escaped using an--- escape char determined by the first predicate. The escape characters are--- removed.------ /pre-release/-{-# INLINE takeEndByEsc #-}-takeEndByEsc :: Monad m =>-    (a -> Bool) -> (a -> Bool) -> Parser a m b -> Parser a m b-takeEndByEsc isEsc isSep (Parser pstep pinitial pextract) =--    Parser step initial extract--    where--    initial = first Left' <$> pinitial--    step (Left' s) a = do-        if isEsc a-        then return $ Partial 0 $ Right' s-        else do-            res <- pstep s a-            if not (isSep a)-            then return $ first Left' res-            else fmap (first Left') $ extractStep pextract res--    step (Right' s) a = do-        res <- pstep s a-        return $ first Left' res--    extract (Left' s) = fmap (first Left') $ pextract s-    extract (Right' _) =-        return $ Error "takeEndByEsc: trailing escape"---- | Like 'takeEndBy' but the separator is dropped.------ See also "Streamly.Data.Fold.takeEndBy_".------ /Pre-release/----{-# INLINE takeEndBy_ #-}-takeEndBy_ :: (a -> Bool) -> Parser a m b -> Parser a m b-{--takeEndBy_ isEnd p =-    takeFramedByGeneric Nothing Nothing (Just isEnd) (toFold p)--}-takeEndBy_ cond (Parser pstep pinitial pextract) =--    Parser step pinitial pextract--    where--    step s a =-        if cond a-        then pextract s-        else pstep s a---- | Take either the separator or the token. Separator is a Left value and--- token is Right value.------ /Unimplemented/-{-# INLINE takeEitherSepBy #-}-takeEitherSepBy :: -- Monad m =>-    (a -> Bool) -> Fold m (Either a b) c -> Parser a m c-takeEitherSepBy _cond = undefined -- D.toParserK . D.takeEitherSepBy cond---- | Parse a token that starts with an element chosen by the predicate.  The--- parser fails if the input does not start with the selected element.------ * Stops - when the predicate succeeds in non-leading position.--- * Fails - when the predicate fails in the leading position.------ >>> splitWithPrefix p f = Stream.parseMany (Parser.takeStartBy p f)------ Examples: ------- >>> p = Parser.takeStartBy (== ',') Fold.toList--- >>> leadingComma = Stream.parse p . Stream.fromList--- >>> leadingComma "a,b"--- Left (ParseError "takeStartBy: missing frame start")--- ...--- >>> leadingComma ",,"--- Right ","--- >>> leadingComma ",a,b"--- Right ",a"--- >>> leadingComma ""--- Right ""------ /Pre-release/----{-# INLINE takeStartBy #-}-takeStartBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b-takeStartBy cond (Fold fstep finitial fextract) =--    Parser step initial extract--    where--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (Left' s)-                FL.Done _ -> IError "takeStartBy: fold done without input"--    {-# INLINE process #-}-    process s a = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Partial 0 (Right' s1)-                FL.Done b -> Done 0 b--    step (Left' s) a =-        if cond a-        then process s a-        else return $ Error "takeStartBy: missing frame start"-    step (Right' s) a =-        if not (cond a)-        then process s a-        else Done 1 <$> fextract s--    extract (Left' s) = fmap (Done 0) $ fextract s-    extract (Right' s) = fmap (Done 0) $ fextract s---- | Like 'takeStartBy' but drops the separator.------ >>> takeStartBy_ isBegin = Parser.takeFramedByGeneric Nothing (Just isBegin) Nothing----{-# INLINE takeStartBy_ #-}-takeStartBy_ :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b-takeStartBy_ isBegin = takeFramedByGeneric Nothing (Just isBegin) Nothing---- | @takeFramedByEsc_ isEsc isBegin isEnd fold@ parses a token framed using a--- begin and end predicate, and an escape character. The frame begin and end--- characters lose their special meaning if preceded by the escape character.------ Nested frames are allowed if begin and end markers are different, nested--- frames must be balanced unless escaped, nested frame markers are emitted as--- it is.------ For example,------ >>> p = Parser.takeFramedByEsc_ (== '\\') (== '{') (== '}') Fold.toList--- >>> Stream.parse p $ Stream.fromList "{hello}"--- Right "hello"--- >>> Stream.parse p $ Stream.fromList "{hello {world}}"--- Right "hello {world}"--- >>> Stream.parse p $ Stream.fromList "{hello \\{world}"--- Right "hello {world"--- >>> Stream.parse p $ Stream.fromList "{hello {world}"--- Left (ParseError "takeFramedByEsc_: missing frame end")------ /Pre-release/-{-# INLINE takeFramedByEsc_ #-}-takeFramedByEsc_ :: Monad m =>-    (a -> Bool) -> (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b--- takeFramedByEsc_ isEsc isEnd p =---    takeFramedByGeneric (Just isEsc) Nothing (Just isEnd) (toFold p)-takeFramedByEsc_ isEsc isBegin isEnd (Fold fstep finitial fextract) =--    Parser step initial extract--    where--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (FrameEscInit s)-                FL.Done _ ->-                    error "takeFramedByEsc_: fold done without input"--    {-# INLINE process #-}-    process s a n = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (FrameEscGo s1 n)-                FL.Done b -> Done 0 b--    step (FrameEscInit s) a =-        if isBegin a-        then return $ Partial 0 (FrameEscGo s 0)-        else return $ Error "takeFramedByEsc_: missing frame start"-    step (FrameEscGo s n) a =-        if isEsc a-        then return $ Partial 0 $ FrameEscEsc s n-        else do-            if not (isEnd a)-            then-                let n1 = if isBegin a then n + 1 else n-                 in process s a n1-            else-                if n == 0-                then Done 0 <$> fextract s-                else process s a (n - 1)-    step (FrameEscEsc s n) a = process s a n--    err = return . Error--    extract (FrameEscInit _) = err "takeFramedByEsc_: empty token"-    extract (FrameEscGo _ _) = err "takeFramedByEsc_: missing frame end"-    extract (FrameEscEsc _ _) = err "takeFramedByEsc_: trailing escape"--data FramedState s = FrameInit !s | FrameGo !s Int---- | @takeFramedBy_ isBegin isEnd fold@ parses a token framed by a begin and an--- end predicate.------ >>> takeFramedBy_ = Parser.takeFramedByEsc_ (const False)----{-# INLINE takeFramedBy_ #-}-takeFramedBy_ :: Monad m =>-    (a -> Bool) -> (a -> Bool) -> Fold m a b -> Parser a m b--- takeFramedBy_ isBegin isEnd =---    takeFramedByGeneric (Just (const False)) (Just isBegin) (Just isEnd)-takeFramedBy_ isBegin isEnd (Fold fstep finitial fextract) =--    Parser step initial extract--    where--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (FrameInit s)-                FL.Done _ ->-                    error "takeFramedBy_: fold done without input"--    {-# INLINE process #-}-    process s a n = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (FrameGo s1 n)-                FL.Done b -> Done 0 b--    step (FrameInit s) a =-        if isBegin a-        then return $ Continue 0 (FrameGo s 0)-        else return $ Error "takeFramedBy_: missing frame start"-    step (FrameGo s n) a-        | not (isEnd a) =-            let n1 = if isBegin a then n + 1 else n-             in process s a n1-        | n == 0 = Done 0 <$> fextract s-        | otherwise = process s a (n - 1)--    err = return . Error--    extract (FrameInit _) = err "takeFramedBy_: empty token"-    extract (FrameGo _ _) = err "takeFramedBy_: missing frame end"------------------------------------------------------------------------------------ Grouping and words----------------------------------------------------------------------------------data WordByState s b = WBLeft !s | WBWord !s | WBRight !b---- Note we can also get words using something like:--- sepBy FL.toList (takeWhile (not . p) Fold.toList) (dropWhile p)------ But that won't be as efficient and ergonomic.---- | Like 'splitOn' but strips leading, trailing, and repeated separators.--- Therefore, @".a..b."@ having '.' as the separator would be parsed as--- @["a","b"]@.  In other words, its like parsing words from whitespace--- separated text.------ * Stops - when it finds a word separator after a non-word element--- * Fails - never.------ >>> wordBy = Parser.wordFramedBy (const False) (const False) (const False)------ @--- S.wordsBy pred f = S.parseMany (PR.wordBy pred f)--- @----{-# INLINE wordBy #-}-wordBy :: Monad m => (a -> Bool) -> Fold m a b -> Parser a m b-wordBy predicate (Fold fstep finitial fextract) = Parser step initial extract--    where--    {-# INLINE worder #-}-    worder s a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Partial s1 -> Partial 0 $ WBWord s1-                  FL.Done b -> Done 0 b--    initial = do-        res <- finitial-        return-            $ case res of-                  FL.Partial s -> IPartial $ WBLeft s-                  FL.Done b -> IDone b--    step (WBLeft s) a =-        if not (predicate a)-        then worder s a-        else return $ Partial 0 $ WBLeft s-    step (WBWord s) a =-        if not (predicate a)-        then worder s a-        else do-            b <- fextract s-            return $ Partial 0 $ WBRight b-    step (WBRight b) a =-        return-            $ if not (predicate a)-              then Done 1 b-              else Partial 0 $ WBRight b--    extract (WBLeft s) = fmap (Done 0) $ fextract s-    extract (WBWord s) = fmap (Done 0) $ fextract s-    extract (WBRight b) = return (Done 0 b)--data WordFramedState s b =-      WordFramedSkipPre !s-    | WordFramedWord !s !Int-    | WordFramedEsc !s !Int-    | WordFramedSkipPost !b---- | Like 'wordBy' but treats anything inside a pair of quotes as a single--- word, the quotes can be escaped by an escape character.  Recursive quotes--- are possible if quote begin and end characters are different, quotes must be--- balanced. Outermost quotes are stripped.------ >>> braces = Parser.wordFramedBy (== '\\') (== '{') (== '}') isSpace Fold.toList--- >>> Stream.parse braces $ Stream.fromList "{ab} cd"--- Right "ab"--- >>> Stream.parse braces $ Stream.fromList "{ab}{cd}"--- Right "abcd"--- >>> Stream.parse braces $ Stream.fromList "a{b} cd"--- Right "ab"--- >>> Stream.parse braces $ Stream.fromList "a{{b}} cd"--- Right "a{b}"------ >>> quotes = Parser.wordFramedBy (== '\\') (== '"') (== '"') isSpace Fold.toList--- >>> Stream.parse quotes $ Stream.fromList "\"a\"\"b\""--- Right "ab"----{-# INLINE wordFramedBy #-}-wordFramedBy :: Monad m =>-       (a -> Bool)  -- ^ Matches escape elem?-    -> (a -> Bool)  -- ^ Matches left quote?-    -> (a -> Bool)  -- ^ matches right quote?-    -> (a -> Bool)  -- ^ matches word separator?-    -> Fold m a b-    -> Parser a m b-wordFramedBy isEsc isBegin isEnd isSep-    (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (WordFramedSkipPre s)-                FL.Done _ ->-                    error "wordFramedBy: fold done without input"--    {-# INLINE process #-}-    process s a n = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (WordFramedWord s1 n)-                FL.Done b -> Done 0 b--    step (WordFramedSkipPre s) a-        | isEsc a = return $ Continue 0 $ WordFramedEsc s 0-        | isSep a = return $ Partial 0 $ WordFramedSkipPre s-        | isBegin a = return $ Continue 0 $ WordFramedWord s 1-        | isEnd a =-            return $ Error "wordFramedBy: missing frame start"-        | otherwise = process s a 0-    step (WordFramedWord s n) a-        | isEsc a = return $ Continue 0 $ WordFramedEsc s n-        | n == 0 && isSep a = do-            b <- fextract s-            return $ Partial 0 $ WordFramedSkipPost b-        | otherwise = do-            -- We need to use different order for checking begin and end for-            -- the n == 0 and n == 1 case so that when the begin and end-            -- character is the same we treat the one after begin as end.-            if n == 0-            then-               -- Need to check isBegin first-               if isBegin a-               then return $ Continue 0 $ WordFramedWord s 1-               else if isEnd a-                    then return $ Error "wordFramedBy: missing frame start"-                    else process s a n-            else-               -- Need to check isEnd first-                if isEnd a-                then-                   if n == 1-                   then return $ Continue 0 $ WordFramedWord s 0-                   else process s a (n - 1)-                else if isBegin a-                     then process s a (n + 1)-                     else process s a n-    step (WordFramedEsc s n) a = process s a n-    step (WordFramedSkipPost b) a =-        return-            $ if not (isSep a)-              then Done 1 b-              else Partial 0 $ WordFramedSkipPost b--    err = return . Error--    extract (WordFramedSkipPre s) = fmap (Done 0) $ fextract s-    extract (WordFramedWord s n) =-        if n == 0-        then fmap (Done 0) $ fextract s-        else err "wordFramedBy: missing frame end"-    extract (WordFramedEsc _ _) =-        err "wordFramedBy: trailing escape"-    extract (WordFramedSkipPost b) = return (Done 0 b)--data WordQuotedState s b a =-      WordQuotedSkipPre !s-    | WordUnquotedWord !s-    | WordQuotedWord !s !Int !a !a-    | WordUnquotedEsc !s-    | WordQuotedEsc !s !Int !a !a-    | WordQuotedSkipPost !b---- | Quote and bracket aware word splitting with escaping. Like 'wordBy' but--- word separators within specified quotes or brackets are ignored. Quotes and--- escape characters can be processed. If the end quote is different from the--- start quote it is called a bracket. The following quoting rules apply:------ * In an unquoted string a character may be preceded by an escape character.--- The escape character is removed and the character following it is treated--- literally with no special meaning e.g. e.g. h\ e\ l\ l\ o is a single word,--- \n is same as n.--- * Any part of the word can be placed within quotes. Inside quotes all--- characters are treated literally with no special meaning. Quoting character--- itself cannot be used within quotes unless escape processing within quotes--- is applied to allow it.--- * Optionally escape processing for quoted part can be specified. Escape--- character has no special meaning inside quotes unless it is followed by a--- character that has a escape translation specified, in that case the escape--- character is removed, and the specified translation is applied to the--- character following it. This can be used to escape the quoting character--- itself within quotes.--- * There can be multiple quoting characters, when a quote starts, all other--- quoting characters within that quote lose any special meaning until the--- quote is closed.--- * A starting quote char without an ending char generates a parse error. An--- ending bracket char without a corresponding bracket begin is ignored.--- * Brackets can be nested.------ We should note that unquoted and quoted escape processing are different. In--- unquoted part escape character is always removed. In quoted part it is--- removed only if followed by a special meaning character. This is consistent--- with how shell performs escape processing.---- Examples of quotes - "double quotes", 'single quotes', (parens), {braces},--- ((nested) brackets).------ Example:------ >>> :{--- >>> q x =--- >>>     case x of--- >>>         '"' -> Just x--- >>>         '\'' -> Just x--- >>>         _ -> Nothing--- >>> :}------ >>> p = Parser.wordKeepQuotes (== '\\') q isSpace Fold.toList--- >>> Stream.parse p $ Stream.fromList "a\"b'c\";'d\"e'f ghi"--- Right "a\"b'c\";'d\"e'f"------ Note that outer quotes and backslashes from the input string are consumed by--- Haskell, therefore, the actual input string passed to the parser is:--- a"b'c";'d"e'f ghi------ Similarly, when printing, double quotes are escaped by Haskell.------ Limitations:------ Shell like quote processing can be performed by using quote char specific--- escape processing, single quotes with no escapes, and double quotes with--- escapes.------ JSON string processing can also be achieved except the "\uXXXX" style--- escaping for Unicode characters.----{-# INLINE wordWithQuotes #-}-wordWithQuotes :: (Monad m, Eq a) =>-       Bool            -- ^ Retain the quotes and escape chars in the output-    -> (a -> a -> Maybe a)  -- ^ quote char -> escaped char -> translated char-    -> a               -- ^ Matches an escape elem?-    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.-    -> (a -> Bool)     -- ^ Matches a word separator?-    -> Fold m a b-    -> Parser a m b-wordWithQuotes keepQuotes tr escChar toRight isSep-    (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    -- Can be used to generate parse error for a bracket end without a bracket-    -- begin.-    isInvalid = const False--    isEsc = (== escChar)--    initial =  do-        res <- finitial-        return $-            case res of-                FL.Partial s -> IPartial (WordQuotedSkipPre s)-                FL.Done _ ->-                    error "wordKeepQuotes: fold done without input"--    {-# INLINE processQuoted #-}-    processQuoted s a n ql qr = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (WordQuotedWord s1 n ql qr)-                FL.Done b -> Done 0 b--    {-# INLINE processUnquoted #-}-    processUnquoted s a = do-        res <- fstep s a-        return-            $ case res of-                FL.Partial s1 -> Continue 0 (WordUnquotedWord s1)-                FL.Done b -> Done 0 b--    step (WordQuotedSkipPre s) a-        | isEsc a = return $ Continue 0 $ WordUnquotedEsc s-        | isSep a = return $ Partial 0 $ WordQuotedSkipPre s-        | otherwise =-            case toRight a of-                Just qr ->-                  if keepQuotes-                  then processQuoted s a 1 a qr-                  else return $ Continue 0 $ WordQuotedWord s 1 a qr-                Nothing-                    | isInvalid a ->-                        return $ Error "wordKeepQuotes: invalid unquoted char"-                    | otherwise -> processUnquoted s a-    step (WordUnquotedWord s) a-        | isEsc a = return $ Continue 0 $ WordUnquotedEsc s-        | isSep a = do-            b <- fextract s-            return $ Partial 0 $ WordQuotedSkipPost b-        | otherwise = do-            case toRight a of-                Just qr ->-                    if keepQuotes-                    then processQuoted s a 1 a qr-                    else return $ Continue 0 $ WordQuotedWord s 1 a qr-                Nothing ->-                    if isInvalid a-                    then return $ Error "wordKeepQuotes: invalid unquoted char"-                    else processUnquoted s a-    step (WordQuotedWord s n ql qr) a-        | isEsc a = return $ Continue 0 $ WordQuotedEsc s n ql qr-        {--        -- XXX Will this ever occur? Will n ever be 0?-        | n == 0 && isSep a = do-            b <- fextract s-            return $ Partial 0 $ WordQuotedSkipPost b-        -}-        | otherwise = do-                if a == qr-                then-                   if n == 1-                   then if keepQuotes-                        then processUnquoted s a-                        else return $ Continue 0 $ WordUnquotedWord s-                   else processQuoted s a (n - 1) ql qr-                else if a == ql-                     then processQuoted s a (n + 1) ql qr-                     else processQuoted s a n ql qr-    step (WordUnquotedEsc s) a = processUnquoted s a-    step (WordQuotedEsc s n ql qr) a =-        case tr ql a of-            Nothing -> do-                res <- fstep s escChar-                case res of-                    FL.Partial s1 -> processQuoted s1 a n ql qr-                    FL.Done b -> return $ Done 0 b-            Just x -> processQuoted s x n ql qr-    step (WordQuotedSkipPost b) a =-        return-            $ if not (isSep a)-              then Done 1 b-              else Partial 0 $ WordQuotedSkipPost b--    err = return . Error--    extract (WordQuotedSkipPre s) = fmap (Done 0) $ fextract s-    extract (WordUnquotedWord s) = fmap (Done 0) $ fextract s-    extract (WordQuotedWord s n _ _) =-        if n == 0-        then fmap (Done 0) $ fextract s-        else err "wordWithQuotes: missing frame end"-    extract WordQuotedEsc {} =-        err "wordWithQuotes: trailing escape"-    extract (WordUnquotedEsc _) =-        err "wordWithQuotes: trailing escape"-    extract (WordQuotedSkipPost b) = return (Done 0 b)---- | 'wordWithQuotes' without processing the quotes and escape function--- supplied to escape the quote char within a quote. Can be used to parse words--- keeping the quotes and escapes intact.------ >>> wordKeepQuotes = Parser.wordWithQuotes True (\_ _ -> Nothing)----{-# INLINE wordKeepQuotes #-}-wordKeepQuotes :: (Monad m, Eq a) =>-       a               -- ^ Escape char-    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.-    -> (a -> Bool)     -- ^ Matches a word separator?-    -> Fold m a b-    -> Parser a m b-wordKeepQuotes =-    -- Escape the quote char itself-    wordWithQuotes True (\q x -> if q == x then Just x else Nothing)---- See the "Quoting Rules" section in the "bash" manual page for a primer on--- how quotes are used by shells.---- | 'wordWithQuotes' with quote processing applied and escape function--- supplied to escape the quote char within a quote. Can be ysed to parse words--- and processing the quoting and escaping at the same time.------ >>> wordProcessQuotes = Parser.wordWithQuotes False (\_ _ -> Nothing)----{-# INLINE wordProcessQuotes #-}-wordProcessQuotes :: (Monad m, Eq a) =>-        a              -- ^ Escape char-    -> (a -> Maybe a)  -- ^ If left quote, return right quote, else Nothing.-    -> (a -> Bool)     -- ^ Matches a word separator?-    -> Fold m a b-    -> Parser a m b-wordProcessQuotes =-    -- Escape the quote char itself-    wordWithQuotes False (\q x -> if q == x then Just x else Nothing)--{-# ANN type GroupByState Fuse #-}-data GroupByState a s-    = GroupByInit !s-    | GroupByGrouping !a !s---- | Given an input stream @[a,b,c,...]@ and a comparison function @cmp@, the--- parser assigns the element @a@ to the first group, then if @a \`cmp` b@ is--- 'True' @b@ is also assigned to the same group.  If @a \`cmp` c@ is 'True'--- then @c@ is also assigned to the same group and so on. When the comparison--- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and--- the result of the fold is the result of the parser.------ * Stops - when the comparison fails.--- * Fails - never.------ >>> :{---  runGroupsBy eq =---      Stream.fold Fold.toList---          . Stream.parseMany (Parser.groupBy eq Fold.toList)---          . Stream.fromList--- :}------ >>> runGroupsBy (<) []--- []------ >>> runGroupsBy (<) [1]--- [Right [1]]------ >>> runGroupsBy (<) [3, 5, 4, 1, 2, 0]--- [Right [3,5,4],Right [1,2],Right [0]]----{-# INLINE groupBy #-}-groupBy :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b-groupBy eq (Fold fstep finitial fextract) = Parser step initial extract--    where--    {-# INLINE grouper #-}-    grouper s a0 a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Done b -> Done 0 b-                  FL.Partial s1 -> Partial 0 (GroupByGrouping a0 s1)--    initial = do-        res <- finitial-        return-            $ case res of-                  FL.Partial s -> IPartial $ GroupByInit s-                  FL.Done b -> IDone b--    step (GroupByInit s) a = grouper s a a-    step (GroupByGrouping a0 s) a =-        if eq a0 a-        then grouper s a0 a-        else Done 1 <$> fextract s--    extract (GroupByInit s) = fmap (Done 0) $ fextract s-    extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s---- | Unlike 'groupBy' this combinator performs a rolling comparison of two--- successive elements in the input stream.  Assuming the input stream--- is @[a,b,c,...]@ and the comparison function is @cmp@, the parser--- first assigns the element @a@ to the first group, then if @a \`cmp` b@ is--- 'True' @b@ is also assigned to the same group.  If @b \`cmp` c@ is 'True'--- then @c@ is also assigned to the same group and so on. When the comparison--- fails the parser is terminated. Each group is folded using the 'Fold' @f@ and--- the result of the fold is the result of the parser.------ * Stops - when the comparison fails.--- * Fails - never.------ >>> :{---  runGroupsByRolling eq =---      Stream.fold Fold.toList---          . Stream.parseMany (Parser.groupByRolling eq Fold.toList)---          . Stream.fromList--- :}------ >>> runGroupsByRolling (<) []--- []------ >>> runGroupsByRolling (<) [1]--- [Right [1]]------ >>> runGroupsByRolling (<) [3, 5, 4, 1, 2, 0]--- [Right [3,5],Right [4],Right [1,2],Right [0]]------ /Pre-release/----{-# INLINE groupByRolling #-}-groupByRolling :: Monad m => (a -> a -> Bool) -> Fold m a b -> Parser a m b-groupByRolling eq (Fold fstep finitial fextract) = Parser step initial extract--    where--    {-# INLINE grouper #-}-    grouper s a = do-        res <- fstep s a-        return-            $ case res of-                  FL.Done b -> Done 0 b-                  FL.Partial s1 -> Partial 0 (GroupByGrouping a s1)--    initial = do-        res <- finitial-        return-            $ case res of-                  FL.Partial s -> IPartial $ GroupByInit s-                  FL.Done b -> IDone b--    step (GroupByInit s) a = grouper s a-    step (GroupByGrouping a0 s) a =-        if eq a0 a-        then grouper s a-        else Done 1 <$> fextract s--    extract (GroupByInit s) = fmap (Done 0) $ fextract s-    extract (GroupByGrouping _ s) = fmap (Done 0) $ fextract s--{-# ANN type GroupByStatePair Fuse #-}-data GroupByStatePair a s1 s2-    = GroupByInitPair !s1 !s2-    | GroupByGroupingPair !a !s1 !s2-    | GroupByGroupingPairL !a !s1 !s2-    | GroupByGroupingPairR !a !s1 !s2---- | Like 'groupByRolling', but if the predicate is 'True' then collects using--- the first fold as long as the predicate holds 'True', if the predicate is--- 'False' collects using the second fold as long as it remains 'False'.--- Returns 'Left' for the first case and 'Right' for the second case.------ For example, if we want to detect sorted sequences in a stream, both--- ascending and descending cases we can use 'groupByRollingEither (<=)--- Fold.toList Fold.toList'.------ /Pre-release/-{-# INLINE groupByRollingEither #-}-groupByRollingEither :: Monad m =>-    (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (Either b c)-groupByRollingEither-    eq-    (Fold fstep1 finitial1 fextract1)-    (Fold fstep2 finitial2 fextract2) = Parser step initial extract--    where--    {-# INLINE grouper #-}-    grouper s1 s2 a = do-        return $ Continue 0 (GroupByGroupingPair a s1 s2)--    {-# INLINE grouperL2 #-}-    grouperL2 s1 s2 a = do-        res <- fstep1 s1 a-        return-            $ case res of-                FL.Done b -> Done 0 (Left b)-                FL.Partial s11 -> Partial 0 (GroupByGroupingPairL a s11 s2)--    {-# INLINE grouperL #-}-    grouperL s1 s2 a0 a = do-        res <- fstep1 s1 a0-        case res of-            FL.Done b -> return $ Done 0 (Left b)-            FL.Partial s11 -> grouperL2 s11 s2 a--    {-# INLINE grouperR2 #-}-    grouperR2 s1 s2 a = do-        res <- fstep2 s2 a-        return-            $ case res of-                FL.Done b -> Done 0 (Right b)-                FL.Partial s21 -> Partial 0 (GroupByGroupingPairR a s1 s21)--    {-# INLINE grouperR #-}-    grouperR s1 s2 a0 a = do-        res <- fstep2 s2 a0-        case res of-            FL.Done b -> return $ Done 0 (Right b)-            FL.Partial s21 -> grouperR2 s1 s21 a--    initial = do-        res1 <- finitial1-        res2 <- finitial2-        return-            $ case res1 of-                FL.Partial s1 ->-                    case res2 of-                        FL.Partial s2 -> IPartial $ GroupByInitPair s1 s2-                        FL.Done b -> IDone (Right b)-                FL.Done b -> IDone (Left b)--    step (GroupByInitPair s1 s2) a = grouper s1 s2 a--    step (GroupByGroupingPair a0 s1 s2) a =-        if not (eq a0 a)-        then grouperL s1 s2 a0 a-        else grouperR s1 s2 a0 a--    step (GroupByGroupingPairL a0 s1 s2) a =-        if not (eq a0 a)-        then grouperL2 s1 s2 a-        else Done 1 . Left <$> fextract1 s1--    step (GroupByGroupingPairR a0 s1 s2) a =-        if eq a0 a-        then grouperR2 s1 s2 a-        else Done 1 . Right <$> fextract2 s2--    extract (GroupByInitPair s1 _) = Done 0 . Left <$> fextract1 s1-    extract (GroupByGroupingPairL _ s1 _) = Done 0 . Left <$> fextract1 s1-    extract (GroupByGroupingPairR _ _ s2) = Done 0 . Right <$> fextract2 s2-    extract (GroupByGroupingPair a s1 _) = do-                res <- fstep1 s1 a-                case res of-                    FL.Done b -> return $ Done 0 (Left b)-                    FL.Partial s11 -> Done 0 . Left <$> fextract1 s11---- XXX use an Unfold instead of a list?--- XXX custom combinators for matching list, array and stream?--- XXX rename to listBy?---- | Match the given sequence of elements using the given comparison function.--- Returns the original sequence if successful.------ Definition:------ >>> listEqBy cmp xs = Parser.streamEqBy cmp (Stream.fromList xs) *> Parser.fromPure xs------ Examples:------ >>> Stream.parse (Parser.listEqBy (==) "string") $ Stream.fromList "string"--- Right "string"------ >>> Stream.parse (Parser.listEqBy (==) "mismatch") $ Stream.fromList "match"--- Left (ParseError "streamEqBy: mismtach occurred")----{-# INLINE listEqBy #-}-listEqBy :: Monad m => (a -> a -> Bool) -> [a] -> Parser a m [a]-listEqBy cmp xs = streamEqByInternal cmp (D.fromList xs) *> fromPure xs-{--listEqBy cmp str = Parser step initial extract--    where--    -- XXX Should return IDone in initial for [] case-    initial = return $ IPartial str--    step [] _ = return $ Done 0 str-    step [x] a =-        return-            $ if x `cmp` a-              then Done 0 str-              else Error "listEqBy: failed, yet to match the last element"-    step (x:xs) a =-        return-            $ if x `cmp` a-              then Continue 0 xs-              else Error-                       $ "listEqBy: failed, yet to match "-                       ++ show (length xs + 1) ++ " elements"--    extract xs =-        return-            $ Error-            $ "listEqBy: end of input, yet to match "-            ++ show (length xs) ++ " elements"--}--{-# INLINE streamEqByInternal #-}-streamEqByInternal :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()-streamEqByInternal cmp (D.Stream sstep state) = Parser step initial extract--    where--    initial = do-        r <- sstep defState state-        case r of-            D.Yield x s -> return $ IPartial (Just' x, s)-            D.Stop -> return $ IDone ()-            -- Need Skip/Continue in initial to loop right here-            D.Skip s -> return $ IPartial (Nothing', s)--    step (Just' x, st) a =-        if x `cmp` a-          then do-            r <- sstep defState st-            return-                $ case r of-                    D.Yield x1 s -> Continue 0 (Just' x1, s)-                    D.Stop -> Done 0 ()-                    D.Skip s -> Continue 1 (Nothing', s)-          else return $ Error "streamEqBy: mismtach occurred"-    step (Nothing', st) a = do-        r <- sstep defState st-        return-            $ case r of-                D.Yield x s -> do-                    if x `cmp` a-                    then Continue 0 (Nothing', s)-                    else Error "streamEqBy: mismatch occurred"-                D.Stop -> Done 1 ()-                D.Skip s -> Continue 1 (Nothing', s)--    extract _ = return $ Error "streamEqBy: end of input"---- | Like 'listEqBy' but uses a stream instead of a list and does not return--- the stream.----{-# INLINE streamEqBy #-}-streamEqBy :: Monad m => (a -> a -> Bool) -> D.Stream m a -> Parser a m ()--- XXX Somehow composing this with "*>" is much faster on the microbenchmark.--- Need to investigate why.-streamEqBy cmp stream = streamEqByInternal cmp stream *> fromPure ()---- Rename to "list".--- | Match the input sequence with the supplied list and return it if--- successful.------ >>> listEq = Parser.listEqBy (==)----{-# INLINE listEq #-}-listEq :: (Monad m, Eq a) => [a] -> Parser a m [a]-listEq = listEqBy (==)---- | Match if the input stream is a subsequence of the argument stream i.e. all--- the elements of the input stream occur, in order, in the argument stream.--- The elements do not have to occur consecutively. A sequence is considered a--- subsequence of itself.-{-# INLINE subsequenceBy #-}-subsequenceBy :: -- Monad m =>-    (a -> a -> Bool) -> Stream m a -> Parser a m ()-subsequenceBy = undefined--{---- Should go in Data.Parser.Regex in streamly package so that it can depend on--- regex backends.-{-# INLINE regexPosix #-}-regexPosix :: -- Monad m =>-    Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))-regexPosix = undefined--{-# INLINE regexPCRE #-}-regexPCRE :: -- Monad m =>-    Regex -> Parser m a (Maybe (Array (MatchOffset, MatchLength)))-regexPCRE = undefined--}------------------------------------------------------------------------------------ Transformations on input------------------------------------------------------------------------------------ Initial needs a "Continue" constructor to implement scans on parsers. As a--- parser can always return a Continue in initial when we feed the fold's--- initial result to it. We can work this around for postscan by introducing an--- initial state and calling "initial" only on the first input.---- | Stateful scan on the input of a parser using a Fold.------ /Unimplemented/----{-# INLINE postscan #-}-postscan :: -- Monad m =>-    Fold m a b -> Parser b m c -> Parser a m c-postscan = undefined--{-# INLINE zipWithM #-}-zipWithM :: Monad m =>-    (a -> b -> m c) -> D.Stream m a -> Fold m c x -> Parser b m x-zipWithM zf (D.Stream sstep state) (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        fres <- finitial-        case fres of-            FL.Partial fs -> do-                r <- sstep defState state-                case r of-                    D.Yield x s -> return $ IPartial (Just' x, s, fs)-                    D.Stop -> do-                        x <- fextract fs-                        return $ IDone x-                    -- Need Skip/Continue in initial to loop right here-                    D.Skip s -> return $ IPartial (Nothing', s, fs)-            FL.Done x -> return $ IDone x--    step (Just' a, st, fs) b = do-        c <- zf a b-        fres <- fstep fs c-        case fres of-            FL.Partial fs1 -> do-                r <- sstep defState st-                case r of-                    D.Yield x1 s -> return $ Continue 0 (Just' x1, s, fs1)-                    D.Stop -> do-                        x <- fextract fs1-                        return $ Done 0 x-                    D.Skip s -> return $ Continue 1 (Nothing', s, fs1)-            FL.Done x -> return $ Done 0 x-    step (Nothing', st, fs) b = do-        r <- sstep defState st-        case r of-                D.Yield a s -> do-                    c <- zf a b-                    fres <- fstep fs c-                    case fres of-                        FL.Partial fs1 ->-                            return $ Continue 0 (Nothing', s, fs1)-                        FL.Done x -> return $ Done 0 x-                D.Stop -> do-                    x <- fextract fs-                    return $ Done 1 x-                D.Skip s -> return $ Continue 1 (Nothing', s, fs)--    extract _ = return $ Error "zipWithM: end of input"---- | Zip the input of a fold with a stream.------ /Pre-release/----{-# INLINE zip #-}-zip :: Monad m => D.Stream m a -> Fold m (a, b) x -> Parser b m x-zip = zipWithM (curry return)---- | Pair each element of a fold input with its index, starting from index 0.------ /Pre-release/-{-# INLINE indexed #-}-indexed :: forall m a b. Monad m => Fold m (Int, a) b -> Parser a m b-indexed = zip (D.enumerateFromIntegral 0 :: D.Stream m Int)---- | @makeIndexFilter indexer filter predicate@ generates a fold filtering--- function using a fold indexing function that attaches an index to each input--- element and a filtering function that filters using @(index, element) ->--- Bool) as predicate.------ For example:------ @--- filterWithIndex = makeIndexFilter indexed filter--- filterWithAbsTime = makeIndexFilter timestamped filter--- filterWithRelTime = makeIndexFilter timeIndexed filter--- @------ /Pre-release/-{-# INLINE makeIndexFilter #-}-makeIndexFilter ::-       (Fold m (s, a) b -> Parser a m b)-    -> (((s, a) -> Bool) -> Fold m (s, a) b -> Fold m (s, a) b)-    -> (((s, a) -> Bool) -> Fold m a b -> Parser a m b)-makeIndexFilter f comb g = f . comb g . FL.lmap snd---- | @sampleFromthen offset stride@ samples the element at @offset@ index and--- then every element at strides of @stride@.------ /Pre-release/-{-# INLINE sampleFromthen #-}-sampleFromthen :: Monad m => Int -> Int -> Fold m a b -> Parser a m b-sampleFromthen offset size =-    makeIndexFilter indexed FL.filter (\(i, _) -> (i + offset) `mod` size == 0)-------------------------------------------------------------------------------------- Spanning------------------------------------------------------------------------------------- | @span p f1 f2@ composes folds @f1@ and @f2@ such that @f1@ consumes the--- input as long as the predicate @p@ is 'True'.  @f2@ consumes the rest of the--- input.------ @--- > let span_ p xs = Stream.parse (Parser.span p Fold.toList Fold.toList) $ Stream.fromList xs------ > span_ (< 1) [1,2,3]--- ([],[1,2,3])------ > span_ (< 2) [1,2,3]--- ([1],[2,3])------ > span_ (< 4) [1,2,3]--- ([1,2,3],[])------ @------ /Pre-release/-{-# INLINE span #-}-span :: Monad m => (a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)-span p f1 f2 = noErrorUnsafeSplitWith (,) (takeWhile p f1) (fromFold f2)---- | Break the input stream into two groups, the first group takes the input as--- long as the predicate applied to the first element of the stream and next--- input element holds 'True', the second group takes the rest of the input.------ /Pre-release/----{-# INLINE spanBy #-}-spanBy ::-       Monad m-    => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)-spanBy eq f1 f2 = noErrorUnsafeSplitWith (,) (groupBy eq f1) (fromFold f2)---- | Like 'spanBy' but applies the predicate in a rolling fashion i.e.--- predicate is applied to the previous and the next input elements.------ /Pre-release/-{-# INLINE spanByRolling #-}-spanByRolling ::-       Monad m-    => (a -> a -> Bool) -> Fold m a b -> Fold m a c -> Parser a m (b, c)-spanByRolling eq f1 f2 =-    noErrorUnsafeSplitWith (,) (groupByRolling eq f1) (fromFold f2)------------------------------------------------------------------------------------ nested parsers------------------------------------------------------------------------------------ | Takes at-most @n@ input elements.------ * Stops - when the collecting parser stops.--- * Fails - when the collecting parser fails.------ >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 2 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]--- Right [1,2]------ >>> Stream.parse (Parser.takeP 4 (Parser.takeEQ 5 Fold.toList)) $ Stream.fromList [1, 2, 3, 4, 5]--- Left (ParseError "takeEQ: Expecting exactly 5 elements, input terminated on 4")------ /Internal/-{-# INLINE takeP #-}-takeP :: Monad m => Int -> Parser a m b -> Parser a m b-takeP lim (Parser pstep pinitial pextract) = Parser step initial extract--    where--    initial = do-        res <- pinitial-        case res of-            IPartial s ->-                if lim > 0-                then return $ IPartial $ Tuple' 0 s-                else iextract s-            IDone b -> return $ IDone b-            IError e -> return $ IError e--    step (Tuple' cnt r) a = do-        assertM(cnt < lim)-        res <- pstep r a-        let cnt1 = cnt + 1-        case res of-            Partial 0 s -> do-                assertM(cnt1 >= 0)-                if cnt1 < lim-                then return $ Partial 0 $ Tuple' cnt1 s-                else do-                    r1 <- pextract s-                    return $ case r1 of-                        Done n b -> Done n b-                        Continue n s1 -> Continue n (Tuple' (cnt1 - n) s1)-                        Error err -> Error err-                        Partial _ _ -> error "takeP: Partial in extract"--            Continue 0 s -> do-                assertM(cnt1 >= 0)-                if cnt1 < lim-                then return $ Continue 0 $ Tuple' cnt1 s-                else do-                    r1 <- pextract s-                    return $ case r1 of-                        Done n b -> Done n b-                        Continue n s1 -> Continue n (Tuple' (cnt1 - n) s1)-                        Error err -> Error err-                        Partial _ _ -> error "takeP: Partial in extract"-            Partial n s -> do-                let taken = cnt1 - n-                assertM(taken >= 0)-                return $ Partial n $ Tuple' taken s-            Continue n s -> do-                let taken = cnt1 - n-                assertM(taken >= 0)-                return $ Continue n $ Tuple' taken s-            Done n b -> return $ Done n b-            Error str -> return $ Error str--    extract (Tuple' cnt r) = do-        r1 <- pextract r-        return $ case r1 of-            Done n b -> Done n b-            Continue n s1 -> Continue n (Tuple' (cnt - n) s1)-            Error err -> Error err-            Partial _ _ -> error "takeP: Partial in extract"--    -- XXX Need to make the Initial type Step to remove this-    iextract s = do-        r <- pextract s-        return $ case r of-            Done _ b -> IDone b-            Error err -> IError err-            _ -> error "Bug: takeP invalid state in initial"---- | Run a parser without consuming the input.----{-# INLINE lookAhead #-}-lookAhead :: Monad m => Parser a m b -> Parser a m b-lookAhead (Parser step1 initial1 _) = Parser step initial extract--    where--    initial = do-        res <- initial1-        return $ case res of-            IPartial s -> IPartial (Tuple'Fused 0 s)-            IDone b -> IDone b-            IError e -> IError e--    step (Tuple'Fused cnt st) a = do-        r <- step1 st a-        let cnt1 = cnt + 1-        return-            $ case r of-                  Partial n s -> Continue n (Tuple'Fused (cnt1 - n) s)-                  Continue n s -> Continue n (Tuple'Fused (cnt1 - n) s)-                  Done _ b -> Done cnt1 b-                  Error err -> Error err--    -- XXX returning an error let's us backtrack.  To implement it in a way so-    -- that it terminates on eof without an error then we need a way to-    -- backtrack on eof, that will require extract to return 'Step' type.-    extract (Tuple'Fused n _) =-        return-            $ Error-            $ "lookAhead: end of input after consuming "-            ++ show n ++ " elements"------------------------------------------------------------------------------------ Interleaving-------------------------------------------------------------------------------------- To deinterleave we can chain two parsers one behind the other. The input is--- given to the first parser and the input definitively rejected by the first--- parser is given to the second parser.------ We can either have the parsers themselves buffer the input or use the shared--- global buffer to hold it until none of the parsers need it. When the first--- parser returns Skip (i.e. rewind) we let the second parser consume the--- rejected input and when it is done we move the cursor forward to the first--- parser again. This will require a "move forward" command as well.------ To implement grep we can use three parsers, one to find the pattern, one--- to store the context behind the pattern and one to store the context in--- front of the pattern. When a match occurs we need to emit the accumulator of--- all the three parsers. One parser can count the line numbers to provide the--- line number info.--{-# ANN type DeintercalateAllState Fuse #-}-data DeintercalateAllState fs sp ss =-      DeintercalateAllInitL !fs-    | DeintercalateAllL !fs !sp-    | DeintercalateAllInitR !fs-    | DeintercalateAllR !fs !ss---- XXX rename this to intercalate---- Having deintercalateAll for accepting or rejecting entire input could be--- useful. For example, in case of JSON parsing we get an entire block of--- key-value pairs which we need to verify. This version may be simpler, more--- efficient. We could implement this as a stream operation like parseMany.------ XXX Also, it may be a good idea to provide a parse driver for a fold. For--- example, in case of csv parsing as we are feeding a line to a fold we can--- parse it.---- | Like 'deintercalate' but the entire input must satisfy the pattern--- otherwise the parser fails. This is many times faster than deintercalate.------ >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList--- >>> p2 = Parser.satisfy (== '+')--- >>> p = Parser.deintercalateAll p1 p2 Fold.toList--- >>> Stream.parse p $ Stream.fromList ""--- Right []--- >>> Stream.parse p $ Stream.fromList "1"--- Right [Left "1"]--- >>> Stream.parse p $ Stream.fromList "1+"--- Left (ParseError "takeWhile1: end of input")--- >>> Stream.parse p $ Stream.fromList "1+2+3"--- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]----{-# INLINE deintercalateAll #-}-deintercalateAll :: Monad m =>-       Parser a m x-    -> Parser a m y-    -> Fold m (Either x y) z-    -> Parser a m z-deintercalateAll-    (Parser stepL initialL extractL)-    (Parser stepR initialR _)-    (Fold fstep finitial fextract) = Parser step initial extract--    where--    errMsg p status =-        error $ "deintercalate: " ++ p ++ " parser cannot "-                ++ status ++ " without input"--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> return $ IPartial $ DeintercalateAllInitL fs-            FL.Done c -> return $ IDone c--    {-# INLINE processL #-}-    processL foldAction n nextState = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> return $ Partial n (nextState fs1)-            FL.Done c -> return $ Done n c--    {-# INLINE runStepL #-}-    runStepL fs sL a = do-        r <- stepL sL a-        case r of-            Partial n s -> return $ Partial n (DeintercalateAllL fs s)-            Continue n s -> return $ Continue n (DeintercalateAllL fs s)-            Done n b ->-                processL (fstep fs (Left b)) n DeintercalateAllInitR-            Error err -> return $ Error err--    {-# INLINE processR #-}-    processR foldAction n = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> do-                res <- initialL-                case res of-                    IPartial ps -> return $ Partial n (DeintercalateAllL fs1 ps)-                    IDone _ -> errMsg "left" "succeed"-                    IError _ -> errMsg "left" "fail"-            FL.Done c -> return $ Done n c--    {-# INLINE runStepR #-}-    runStepR fs sR a = do-        r <- stepR sR a-        case r of-            Partial n s -> return $ Partial n (DeintercalateAllR fs s)-            Continue n s -> return $ Continue n (DeintercalateAllR fs s)-            Done n b -> processR (fstep fs (Right b)) n-            Error err -> return $ Error err--    step (DeintercalateAllInitL fs) a = do-        res <- initialL-        case res of-            IPartial s -> runStepL fs s a-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"-    step (DeintercalateAllL fs sL) a = runStepL fs sL a-    step (DeintercalateAllInitR fs) a = do-        res <- initialR-        case res of-            IPartial s -> runStepR fs s a-            IDone _ -> errMsg "right" "succeed"-            IError _ -> errMsg "right" "fail"-    step (DeintercalateAllR fs sR) a = runStepR fs sR a--    {-# INLINE extractResult #-}-    extractResult n fs r = do-        res <- fstep fs r-        case res of-            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-            FL.Done c -> return (Done n c)-    extract (DeintercalateAllInitL fs) = fmap (Done 0) $ fextract fs-    extract (DeintercalateAllL fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs (Left b)-            Error err -> return $ Error err-            Continue n s -> return $ Continue n (DeintercalateAllL fs s)-            Partial _ _ -> error "Partial in extract"-    extract (DeintercalateAllInitR fs) = fmap (Done 0) $ fextract fs-    extract (DeintercalateAllR _ _) =-        return $ Error "deintercalateAll: input ended at 'Right' value"--{-# ANN type DeintercalateState Fuse #-}-data DeintercalateState b fs sp ss =-      DeintercalateInitL !fs-    | DeintercalateL !Int !fs !sp-    | DeintercalateInitR !fs-    | DeintercalateR !Int !fs !ss-    | DeintercalateRL !Int !b !fs !sp---- XXX Add tests that the next character that we take after running a parser is--- correct. Especially for the parsers that maintain a count. In the stream--- finished case (extract) as well as not finished case.---- | Apply two parsers alternately to an input stream. The input stream is--- considered an interleaving of two patterns. The two parsers represent the--- two patterns. Parsing starts at the first parser and stops at the first--- parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty--- input or single parse of the first parser is accepted.------ >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList--- >>> p2 = Parser.satisfy (== '+')--- >>> p = Parser.deintercalate p1 p2 Fold.toList--- >>> Stream.parse p $ Stream.fromList ""--- Right []--- >>> Stream.parse p $ Stream.fromList "1"--- Right [Left "1"]--- >>> Stream.parse p $ Stream.fromList "1+"--- Right [Left "1"]--- >>> Stream.parse p $ Stream.fromList "1+2+3"--- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]----{-# INLINE deintercalate #-}-deintercalate :: Monad m =>-       Parser a m x-    -> Parser a m y-    -> Fold m (Either x y) z-    -> Parser a m z-deintercalate-    (Parser stepL initialL extractL)-    (Parser stepR initialR _)-    (Fold fstep finitial fextract) = Parser step initial extract--    where--    errMsg p status =-        error $ "deintercalate: " ++ p ++ " parser cannot "-                ++ status ++ " without input"--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> return $ IPartial $ DeintercalateInitL fs-            FL.Done c -> return $ IDone c--    {-# INLINE processL #-}-    processL foldAction n nextState = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> return $ Partial n (nextState fs1)-            FL.Done c -> return $ Done n c--    {-# INLINE runStepL #-}-    runStepL cnt fs sL a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (DeintercalateL (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (DeintercalateL (cnt1 - n) fs s)-            Done n b ->-                processL (fstep fs (Left b)) n DeintercalateInitR-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    {-# INLINE processR #-}-    processR cnt b fs n = do-        res <- initialL-        case res of-            IPartial ps -> return $ Continue n (DeintercalateRL cnt b fs ps)-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"--    {-# INLINE runStepR #-}-    runStepR cnt fs sR a = do-        let cnt1 = cnt + 1-        r <- stepR sR a-        case r of-            Partial n s -> return $ Continue n (DeintercalateR (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (DeintercalateR (cnt1 - n) fs s)-            Done n b -> processR (cnt1 - n) b fs n-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    step (DeintercalateInitL fs) a = do-        res <- initialL-        case res of-            IPartial s -> runStepL 0 fs s a-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"-    step (DeintercalateL cnt fs sL) a = runStepL cnt fs sL a-    step (DeintercalateInitR fs) a = do-        res <- initialR-        case res of-            IPartial s -> runStepR 0 fs s a-            IDone _ -> errMsg "right" "succeed"-            IError _ -> errMsg "right" "fail"-    step (DeintercalateR cnt fs sR) a = runStepR cnt fs sR a-    step (DeintercalateRL cnt bR fs sL) a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (DeintercalateRL (cnt1 - n) bR fs s)-            Continue n s -> return $ Continue n (DeintercalateRL (cnt1 - n) bR fs s)-            Done n bL -> do-                res <- fstep fs (Right bR)-                case res of-                    FL.Partial fs1 -> do-                        fres <- fstep fs1 (Left bL)-                        case fres of-                            FL.Partial fs2 ->-                                return $ Partial n (DeintercalateInitR fs2)-                            FL.Done c -> return $ Done n c-                    -- XXX We could have the fold accept pairs of (bR, bL)-                    FL.Done _ -> error "Fold terminated consuming partial input"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    {-# INLINE extractResult #-}-    extractResult n fs r = do-        res <- fstep fs r-        case res of-            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-            FL.Done c -> return (Done n c)--    extract (DeintercalateInitL fs) = fmap (Done 0) $ fextract fs-    extract (DeintercalateL cnt fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs (Left b)-            Continue n s -> return $ Continue n (DeintercalateL (cnt - n) fs s)-            Partial _ _ -> error "Partial in extract"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs-    extract (DeintercalateInitR fs) = fmap (Done 0) $ fextract fs-    extract (DeintercalateR cnt fs _) = fmap (Done cnt) $ fextract fs-    extract (DeintercalateRL cnt bR fs sL) = do-        r <- extractL sL-        case r of-            Done n bL -> do-                res <- fstep fs (Right bR)-                case res of-                    FL.Partial fs1 -> extractResult n fs1 (Left bL)-                    FL.Done _ -> error "Fold terminated consuming partial input"-            Continue n s -> return $ Continue n (DeintercalateRL (cnt - n) bR fs s)-            Partial _ _ -> error "Partial in extract"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs--{-# ANN type Deintercalate1State Fuse #-}-data Deintercalate1State b fs sp ss =-      Deintercalate1InitL !Int !fs !sp-    | Deintercalate1InitR !fs-    | Deintercalate1R !Int !fs !ss-    | Deintercalate1RL !Int !b !fs !sp---- | Apply two parsers alternately to an input stream. The input stream is--- considered an interleaving of two patterns. The two parsers represent the--- two patterns. Parsing starts at the first parser and stops at the first--- parser. It can be used to parse a infix style pattern e.g. p1 p2 p1 . Empty--- input or single parse of the first parser is accepted.------ >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList--- >>> p2 = Parser.satisfy (== '+')--- >>> p = Parser.deintercalate1 p1 p2 Fold.toList--- >>> Stream.parse p $ Stream.fromList ""--- Left (ParseError "takeWhile1: end of input")--- >>> Stream.parse p $ Stream.fromList "1"--- Right [Left "1"]--- >>> Stream.parse p $ Stream.fromList "1+"--- Right [Left "1"]--- >>> Stream.parse p $ Stream.fromList "1+2+3"--- Right [Left "1",Right '+',Left "2",Right '+',Left "3"]----{-# INLINE deintercalate1 #-}-deintercalate1 :: Monad m =>-       Parser a m x-    -> Parser a m y-    -> Fold m (Either x y) z-    -> Parser a m z-deintercalate1-    (Parser stepL initialL extractL)-    (Parser stepR initialR _)-    (Fold fstep finitial fextract) = Parser step initial extract--    where--    errMsg p status =-        error $ "deintercalate: " ++ p ++ " parser cannot "-                ++ status ++ " without input"--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> do-                pres <- initialL-                case pres of-                    IPartial s -> return $ IPartial $ Deintercalate1InitL 0 fs s-                    IDone _ -> errMsg "left" "succeed"-                    IError _ -> errMsg "left" "fail"-            FL.Done c -> return $ IDone c--    {-# INLINE processL #-}-    processL foldAction n nextState = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> return $ Partial n (nextState fs1)-            FL.Done c -> return $ Done n c--    {-# INLINE runStepInitL #-}-    runStepInitL cnt fs sL a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (Deintercalate1InitL (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (Deintercalate1InitL (cnt1 - n) fs s)-            Done n b ->-                processL (fstep fs (Left b)) n Deintercalate1InitR-            Error err -> return $ Error err--    {-# INLINE processR #-}-    processR cnt b fs n = do-        res <- initialL-        case res of-            IPartial ps -> return $ Continue n (Deintercalate1RL cnt b fs ps)-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"--    {-# INLINE runStepR #-}-    runStepR cnt fs sR a = do-        let cnt1 = cnt + 1-        r <- stepR sR a-        case r of-            Partial n s -> return $ Continue n (Deintercalate1R (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (Deintercalate1R (cnt1 - n) fs s)-            Done n b -> processR (cnt1 - n) b fs n-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    step (Deintercalate1InitL cnt fs sL) a = runStepInitL cnt fs sL a-    step (Deintercalate1InitR fs) a = do-        res <- initialR-        case res of-            IPartial s -> runStepR 0 fs s a-            IDone _ -> errMsg "right" "succeed"-            IError _ -> errMsg "right" "fail"-    step (Deintercalate1R cnt fs sR) a = runStepR cnt fs sR a-    step (Deintercalate1RL cnt bR fs sL) a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (Deintercalate1RL (cnt1 - n) bR fs s)-            Continue n s -> return $ Continue n (Deintercalate1RL (cnt1 - n) bR fs s)-            Done n bL -> do-                res <- fstep fs (Right bR)-                case res of-                    FL.Partial fs1 -> do-                        fres <- fstep fs1 (Left bL)-                        case fres of-                            FL.Partial fs2 ->-                                return $ Partial n (Deintercalate1InitR fs2)-                            FL.Done c -> return $ Done n c-                    -- XXX We could have the fold accept pairs of (bR, bL)-                    FL.Done _ -> error "Fold terminated consuming partial input"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    {-# INLINE extractResult #-}-    extractResult n fs r = do-        res <- fstep fs r-        case res of-            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-            FL.Done c -> return (Done n c)--    extract (Deintercalate1InitL cnt fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs (Left b)-            Continue n s -> return $ Continue n (Deintercalate1InitL (cnt - n) fs s)-            Partial _ _ -> error "Partial in extract"-            Error err -> return $ Error err-    extract (Deintercalate1InitR fs) = fmap (Done 0) $ fextract fs-    extract (Deintercalate1R cnt fs _) = fmap (Done cnt) $ fextract fs-    extract (Deintercalate1RL cnt bR fs sL) = do-        r <- extractL sL-        case r of-            Done n bL -> do-                res <- fstep fs (Right bR)-                case res of-                    FL.Partial fs1 -> extractResult n fs1 (Left bL)-                    FL.Done _ -> error "Fold terminated consuming partial input"-            Continue n s -> return $ Continue n (Deintercalate1RL (cnt - n) bR fs s)-            Partial _ _ -> error "Partial in extract"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs--{-# ANN type SepByState Fuse #-}-data SepByState fs sp ss =-      SepByInitL !fs-    | SepByL !Int !fs !sp-    | SepByInitR !fs-    | SepByR !Int !fs !ss---- | Apply two parsers alternately to an input stream. Parsing starts at the--- first parser and stops at the first parser. The output of the first parser--- is emiited and the output of the second parser is discarded. It can be used--- to parse a infix style pattern e.g. p1 p2 p1 . Empty input or single parse--- of the first parser is accepted.------ Definitions:------ >>> sepBy p1 p2 f = Parser.deintercalate p1 p2 (Fold.catLefts f)--- >>> sepBy p1 p2 f = Parser.sepBy1 p1 p2 f <|> Parser.fromEffect (Fold.extractM f)------ Examples:------ >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList--- >>> p2 = Parser.satisfy (== '+')--- >>> p = Parser.sepBy p1 p2 Fold.toList--- >>> Stream.parse p $ Stream.fromList ""--- Right []--- >>> Stream.parse p $ Stream.fromList "1"--- Right ["1"]--- >>> Stream.parse p $ Stream.fromList "1+"--- Right ["1"]--- >>> Stream.parse p $ Stream.fromList "1+2+3"--- Right ["1","2","3"]----{-# INLINE sepBy #-}-sepBy :: Monad m =>-    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c--- This has similar performance as the custom impl below.--- sepBy p1 p2 f = deintercalate p1 p2 (FL.catLefts f)-sepBy-    (Parser stepL initialL extractL)-    (Parser stepR initialR _)-    (Fold fstep finitial fextract) = Parser step initial extract--    where--    errMsg p status =-        error $ "sepBy: " ++ p ++ " parser cannot "-                ++ status ++ " without input"--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> return $ IPartial $ SepByInitL fs-            FL.Done c -> return $ IDone c--    {-# INLINE processL #-}-    processL foldAction n nextState = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> return $ Partial n (nextState fs1)-            FL.Done c -> return $ Done n c--    {-# INLINE runStepL #-}-    runStepL cnt fs sL a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (SepByL (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (SepByL (cnt1 - n) fs s)-            Done n b ->-                processL (fstep fs b) n SepByInitR-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    {-# INLINE processR #-}-    processR cnt fs n = do-        res <- initialL-        case res of-            IPartial ps -> return $ Continue n (SepByL cnt fs ps)-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"--    {-# INLINE runStepR #-}-    runStepR cnt fs sR a = do-        let cnt1 = cnt + 1-        r <- stepR sR a-        case r of-            Partial n s -> return $ Continue n (SepByR (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (SepByR (cnt1 - n) fs s)-            Done n _ -> processR (cnt1 - n) fs n-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    step (SepByInitL fs) a = do-        res <- initialL-        case res of-            IPartial s -> runStepL 0 fs s a-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"-    step (SepByL cnt fs sL) a = runStepL cnt fs sL a-    step (SepByInitR fs) a = do-        res <- initialR-        case res of-            IPartial s -> runStepR 0 fs s a-            IDone _ -> errMsg "right" "succeed"-            IError _ -> errMsg "right" "fail"-    step (SepByR cnt fs sR) a = runStepR cnt fs sR a--    {-# INLINE extractResult #-}-    extractResult n fs r = do-        res <- fstep fs r-        case res of-            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-            FL.Done c -> return (Done n c)--    extract (SepByInitL fs) = fmap (Done 0) $ fextract fs-    extract (SepByL cnt fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs b-            Continue n s -> return $ Continue n (SepByL (cnt - n) fs s)-            Partial _ _ -> error "Partial in extract"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs-    extract (SepByInitR fs) = fmap (Done 0) $ fextract fs-    extract (SepByR cnt fs _) = fmap (Done cnt) $ fextract fs---- | Non-backtracking version of sepBy. Several times faster.-{-# INLINE sepByAll #-}-sepByAll :: Monad m =>-    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c-sepByAll p1 p2 f = deintercalateAll p1 p2 (FL.catLefts f)---- XXX This can be implemented using refold, parse one and then continue--- collecting the rest in that.--{-# ANN type SepBy1State Fuse #-}-data SepBy1State fs sp ss =-      SepBy1InitL !Int !fs sp-    | SepBy1L !Int !fs !sp-    | SepBy1InitR !fs-    | SepBy1R !Int !fs !ss--{--{-# INLINE sepBy1 #-}-sepBy1 :: Monad m =>-    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c-sepBy1 p sep sink = do-    x <- p-    f <- fromEffect $ FL.reduce sink-    f1 <- fromEffect $ FL.snoc f x-    many (sep >> p) f1--}---- | Like 'sepBy' but requires at least one successful parse.------ Definition:------ >>> sepBy1 p1 p2 f = Parser.deintercalate1 p1 p2 (Fold.catLefts f)------ Examples:------ >>> p1 = Parser.takeWhile1 (not . (== '+')) Fold.toList--- >>> p2 = Parser.satisfy (== '+')--- >>> p = Parser.sepBy1 p1 p2 Fold.toList--- >>> Stream.parse p $ Stream.fromList ""--- Left (ParseError "takeWhile1: end of input")--- >>> Stream.parse p $ Stream.fromList "1"--- Right ["1"]--- >>> Stream.parse p $ Stream.fromList "1+"--- Right ["1"]--- >>> Stream.parse p $ Stream.fromList "1+2+3"--- Right ["1","2","3"]----{-# INLINE sepBy1 #-}-sepBy1 :: Monad m =>-    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c-sepBy1-    (Parser stepL initialL extractL)-    (Parser stepR initialR _)-    (Fold fstep finitial fextract) = Parser step initial extract--    where--    errMsg p status =-        error $ "sepBy: " ++ p ++ " parser cannot "-                ++ status ++ " without input"--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> do-                pres <- initialL-                case pres of-                    IPartial s -> return $ IPartial $ SepBy1InitL 0 fs s-                    IDone _ -> errMsg "left" "succeed"-                    IError _ -> errMsg "left" "fail"-            FL.Done c -> return $ IDone c--    {-# INLINE processL #-}-    processL foldAction n nextState = do-        fres <- foldAction-        case fres of-            FL.Partial fs1 -> return $ Partial n (nextState fs1)-            FL.Done c -> return $ Done n c--    {-# INLINE runStepInitL #-}-    runStepInitL cnt fs sL a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (SepBy1InitL (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (SepBy1InitL (cnt1 - n) fs s)-            Done n b ->-                processL (fstep fs b) n SepBy1InitR-            Error err -> return $ Error err--    {-# INLINE runStepL #-}-    runStepL cnt fs sL a = do-        let cnt1 = cnt + 1-        r <- stepL sL a-        case r of-            Partial n s -> return $ Continue n (SepBy1L (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (SepBy1L (cnt1 - n) fs s)-            Done n b ->-                processL (fstep fs b) n SepBy1InitR-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    {-# INLINE processR #-}-    processR cnt fs n = do-        res <- initialL-        case res of-            IPartial ps -> return $ Continue n (SepBy1L cnt fs ps)-            IDone _ -> errMsg "left" "succeed"-            IError _ -> errMsg "left" "fail"--    {-# INLINE runStepR #-}-    runStepR cnt fs sR a = do-        let cnt1 = cnt + 1-        r <- stepR sR a-        case r of-            Partial n s -> return $ Continue n (SepBy1R (cnt1 - n) fs s)-            Continue n s -> return $ Continue n (SepBy1R (cnt1 - n) fs s)-            Done n _ -> processR (cnt1 - n) fs n-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    step (SepBy1InitL cnt fs sL) a = runStepInitL cnt fs sL a-    step (SepBy1L cnt fs sL) a = runStepL cnt fs sL a-    step (SepBy1InitR fs) a = do-        res <- initialR-        case res of-            IPartial s -> runStepR 0 fs s a-            IDone _ -> errMsg "right" "succeed"-            IError _ -> errMsg "right" "fail"-    step (SepBy1R cnt fs sR) a = runStepR cnt fs sR a--    {-# INLINE extractResult #-}-    extractResult n fs r = do-        res <- fstep fs r-        case res of-            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-            FL.Done c -> return (Done n c)--    extract (SepBy1InitL cnt fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs b-            Continue n s -> return $ Continue n (SepBy1InitL (cnt - n) fs s)-            Partial _ _ -> error "Partial in extract"-            Error err -> return $ Error err-    extract (SepBy1L cnt fs sL) = do-        r <- extractL sL-        case r of-            Done n b -> extractResult n fs b-            Continue n s -> return $ Continue n (SepBy1L (cnt - n) fs s)-            Partial _ _ -> error "Partial in extract"-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs-    extract (SepBy1InitR fs) = fmap (Done 0) $ fextract fs-    extract (SepBy1R cnt fs _) = fmap (Done cnt) $ fextract fs------------------------------------------------------------------------------------ Interleaving a collection of parsers-------------------------------------------------------------------------------------- | Apply a collection of parsers to an input stream in a round robin fashion.--- Each parser is applied until it stops and then we repeat starting with the--- the first parser again.------ /Unimplemented/----{-# INLINE roundRobin #-}-roundRobin :: -- (Foldable t, Monad m) =>-    t (Parser a m b) -> Fold m b c -> Parser a m c-roundRobin _ps _f = undefined------------------------------------------------------------------------------------ Sequential Collection------------------------------------------------------------------------------------ | @sequence f p@ collects sequential parses of parsers in a--- serial stream @p@ using the fold @f@. Fails if the input ends or any--- of the parsers fail.------ /Pre-release/----{-# INLINE sequence #-}-sequence :: Monad m =>-    D.Stream m (Parser a m b) -> Fold m b c -> Parser a m c-sequence (D.Stream sstep sstate) (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    initial = do-        fres <- finitial-        case fres of-            FL.Partial fs -> return $ IPartial (Nothing', sstate, fs)-            FL.Done c -> return $ IDone c--    -- state does not contain any parser-    -- yield a new parser from the stream-    step (Nothing', ss, fs) _ = do-        sres <- sstep defState ss-        case sres of-            D.Yield p ss1 -> return $ Continue 1 (Just' p, ss1, fs)-            D.Stop -> do-                c <- fextract fs-                return $ Done 1 c-            D.Skip ss1 -> return $ Continue 1 (Nothing', ss1, fs)--    -- state holds a parser that may or may not have been-    -- initialized. pinit holds the initial parser state-    -- or modified parser state respectively-    step (Just' (Parser pstep pinit pextr), ss, fs) a = do-        ps <- pinit-        case ps of-            IPartial ps1 -> do-                pres <- pstep ps1 a-                case pres of-                    Partial n ps2 ->-                        let newP =-                              Just' $ Parser pstep (return $ IPartial ps2) pextr-                        in return $ Partial n (newP, ss, fs)-                    Continue n ps2 ->-                        let newP =-                              Just' $ Parser pstep (return $ IPartial ps2) pextr-                        in return $ Continue n (newP, ss, fs)-                    Done n b -> do-                        fres <- fstep fs b-                        case fres of-                            FL.Partial fs1 ->-                                return $ Partial n (Nothing', ss, fs1)-                            FL.Done c -> return $ Done n c-                    Error msg -> return $ Error msg-            IDone b -> do-                fres <- fstep fs b-                case fres of-                    FL.Partial fs1 ->-                        return $ Partial 1 (Nothing', ss, fs1)-                    FL.Done c -> return $ Done 1 c-            IError err -> return $ Error err--    extract (Nothing', _, fs) = fmap (Done 0) $ fextract fs-    extract (Just' (Parser pstep pinit pextr), ss, fs) = do-        ps <- pinit-        case ps of-            IPartial ps1 ->  do-                r <- pextr ps1-                case r of-                    Done n b -> do-                        res <- fstep fs b-                        case res of-                            FL.Partial fs1 -> fmap (Done n) $ fextract fs1-                            FL.Done c -> return (Done n c)-                    Error err -> return $ Error err-                    Continue n s -> return $ Continue n (Just' (Parser pstep (return (IPartial s)) pextr), ss, fs)-                    Partial _ _ -> error "Partial in extract"-            IDone b -> do-                fres <- fstep fs b-                case fres of-                    FL.Partial fs1 -> fmap (Done 0) $ fextract fs1-                    FL.Done c -> return (Done 0 c)-            IError err -> return $ Error err------------------------------------------------------------------------------------ Alternative Collection----------------------------------------------------------------------------------{---- | @choice parsers@ applies the @parsers@ in order and returns the first--- successful parse.------ This is same as 'asum' but more efficient.------ /Broken/----{-# INLINE choice #-}-choice :: (MonadCatch m, Foldable t) => t (Parser a m b) -> Parser a m b-choice = foldl1 shortest--}------------------------------------------------------------------------------------ Sequential Repetition------------------------------------------------------------------------------------ | Like 'many' but uses a 'Parser' instead of a 'Fold' to collect the--- results. Parsing stops or fails if the collecting parser stops or fails.------ /Unimplemented/----{-# INLINE manyP #-}-manyP :: -- MonadCatch m =>-    Parser a m b -> Parser b m c -> Parser a m c-manyP _p _f = undefined---- | Collect zero or more parses. Apply the supplied parser repeatedly on the--- input stream and push the parse results to a downstream fold.------  Stops: when the downstream fold stops or the parser fails.---  Fails: never, produces zero or more results.------ >>> many = Parser.countBetween 0 maxBound------ Compare with 'Control.Applicative.many'.----{-# INLINE many #-}-many :: Monad m => Parser a m b -> Fold m b c -> Parser a m c-many = splitMany--- many = countBetween 0 maxBound---- Note: many1 would perhaps be a better name for this and consistent with--- other names like takeWhile1. But we retain the name "some" for--- compatibility.---- | Collect one or more parses. Apply the supplied parser repeatedly on the--- input stream and push the parse results to a downstream fold.------  Stops: when the downstream fold stops or the parser fails.---  Fails: if it stops without producing a single result.------ >>> some p f = Parser.manyP p (Parser.takeGE 1 f)--- >>> some = Parser.countBetween 1 maxBound------ Compare with 'Control.Applicative.some'.----{-# INLINE some #-}-some :: Monad m => Parser a m b -> Fold m b c -> Parser a m c-some = splitSome--- some p f = manyP p (takeGE 1 f)--- some = countBetween 1 maxBound---- | @countBetween m n f p@ collects between @m@ and @n@ sequential parses of--- parser @p@ using the fold @f@. Stop after collecting @n@ results. Fails if--- the input ends or the parser fails before @m@ results are collected.------ >>> countBetween m n p f = Parser.manyP p (Parser.takeBetween m n f)------ /Unimplemented/----{-# INLINE countBetween #-}-countBetween :: -- MonadCatch m =>-    Int -> Int -> Parser a m b -> Fold m b c -> Parser a m c-countBetween _m _n _p = undefined--- countBetween m n p f = manyP p (takeBetween m n f)---- | @count n f p@ collects exactly @n@ sequential parses of parser @p@ using--- the fold @f@.  Fails if the input ends or the parser fails before @n@--- results are collected.------ >>> count n = Parser.countBetween n n--- >>> count n p f = Parser.manyP p (Parser.takeEQ n f)------ /Unimplemented/----{-# INLINE count #-}-count :: -- MonadCatch m =>-    Int -> Parser a m b -> Fold m b c -> Parser a m c-count n = countBetween n n--- count n p f = manyP p (takeEQ n f)---- | Like 'manyTill' but uses a 'Parser' to collect the results instead of a--- 'Fold'.  Parsing stops or fails if the collecting parser stops or fails.------ We can implemnent parsers like the following using 'manyTillP':------ @--- countBetweenTill m n f p = manyTillP (takeBetween m n f) p--- @------ /Unimplemented/----{-# INLINE manyTillP #-}-manyTillP :: -- Monad m =>-    Parser a m b -> Parser a m x -> Parser b m c -> Parser a m c-manyTillP _p1 _p2 _f = undefined-    -- D.toParserK $ D.manyTillP (D.fromParserK p1) (D.fromParserK p2) f--{-# ANN type ManyTillState Fuse #-}-data ManyTillState fs sr sl-    = ManyTillR !Int !fs !sr-    | ManyTillL !fs !sl---- | @manyTill chunking test f@ tries the parser @test@ on the input, if @test@--- fails it backtracks and tries @chunking@, after @chunking@ succeeds @test@ is--- tried again and so on. The parser stops when @test@ succeeds.  The output of--- @test@ is discarded and the output of @chunking@ is accumulated by the--- supplied fold. The parser fails if @chunking@ fails.------ Stops when the fold @f@ stops.----{-# INLINE manyTill #-}-manyTill :: Monad m-    => Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c-manyTill (Parser stepL initialL extractL)-         (Parser stepR initialR _)-         (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    -- Caution: Mutual recursion--    scrutL fs p c d e = do-        resL <- initialL-        case resL of-            IPartial sl -> return $ c (ManyTillL fs sl)-            IDone bl -> do-                fr <- fstep fs bl-                case fr of-                    FL.Partial fs1 -> scrutR fs1 p c d e-                    FL.Done fb -> return $ d fb-            IError err -> return $ e err--    scrutR fs p c d e = do-        resR <- initialR-        case resR of-            IPartial sr -> return $ p (ManyTillR 0 fs sr)-            IDone _ -> d <$> fextract fs-            IError _ -> scrutL fs p c d e--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> scrutR fs IPartial IPartial IDone IError-            FL.Done b -> return $ IDone b--    step (ManyTillR cnt fs st) a = do-        r <- stepR st a-        case r of-            Partial n s -> return $ Partial n (ManyTillR 0 fs s)-            Continue n s -> do-                assertM(cnt + 1 - n >= 0)-                return $ Continue n (ManyTillR (cnt + 1 - n) fs s)-            Done n _ -> do-                b <- fextract fs-                return $ Done n b-            Error _ -> do-                resL <- initialL-                case resL of-                    IPartial sl ->-                        return $ Continue (cnt + 1) (ManyTillL fs sl)-                    IDone bl -> do-                        fr <- fstep fs bl-                        let cnt1 = cnt + 1-                        case fr of-                            FL.Partial fs1 ->-                                scrutR-                                    fs1-                                    (Partial cnt1)-                                    (Continue cnt1)-                                    (Done cnt1)-                                    Error-                            FL.Done fb -> return $ Done cnt1 fb-                    IError err -> return $ Error err-    step (ManyTillL fs st) a = do-        r <- stepL st a-        case r of-            Partial n s -> return $ Partial n (ManyTillL fs s)-            Continue n s -> return $ Continue n (ManyTillL fs s)-            Done n b -> do-                fs1 <- fstep fs b-                case fs1 of-                    FL.Partial s ->-                        scrutR s (Partial n) (Continue n) (Done n) Error-                    FL.Done b1 -> return $ Done n b1-            Error err -> return $ Error err--    extract (ManyTillL fs sR) = do-        res <- extractL sR-        case res of-            Done n b -> do-                r <- fstep fs b-                case r of-                    FL.Partial fs1 -> fmap (Done n) $ fextract fs1-                    FL.Done c -> return (Done n c)-            Error err -> return $ Error err-            Continue n s -> return $ Continue n (ManyTillL fs s)-            Partial _ _ -> error "Partial in extract"-    extract (ManyTillR _ fs _) = fmap (Done 0) $ fextract fs---- | @manyThen f collect recover@ repeats the parser @collect@ on the input and--- collects the output in the supplied fold. If the the parser @collect@ fails,--- parser @recover@ is run until it stops and then we start repeating the--- parser @collect@ again. The parser fails if the recovery parser fails.------ For example, this can be used to find a key frame in a video stream after an--- error.------ /Unimplemented/----{-# INLINE manyThen #-}-manyThen :: -- (Foldable t, Monad m) =>-    Parser a m b -> Parser a m x -> Fold m b c -> Parser a m c-manyThen _parser _recover _f = undefined------------------------------------------------------------------------------------ Repeated Alternatives------------------------------------------------------------------------------------ | Keep trying a parser up to a maximum of @n@ failures.  When the parser--- fails the input consumed till now is dropped and the new instance is tried--- on the fresh input.------ /Unimplemented/----{-# INLINE retryMaxTotal #-}-retryMaxTotal :: -- (Monad m) =>-    Int -> Parser a m b -> Fold m b c -> Parser a m c-retryMaxTotal _n _p _f  = undefined---- | Like 'retryMaxTotal' but aborts after @n@ successive failures.------ /Unimplemented/----{-# INLINE retryMaxSuccessive #-}-retryMaxSuccessive :: -- (Monad m) =>-    Int -> Parser a m b -> Fold m b c -> Parser a m c-retryMaxSuccessive _n _p _f = undefined---- | Keep trying a parser until it succeeds.  When the parser fails the input--- consumed till now is dropped and the new instance is tried on the fresh--- input.------ /Unimplemented/----{-# INLINE retry #-}-retry :: -- (Monad m) =>-    Parser a m b -> Parser a m b-retry _p = undefined
− src/Streamly/Internal/Data/Parser/ParserD/Tee.hs
@@ -1,617 +0,0 @@-{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}--#include "inline.hs"---- |--- Module      : Streamly.Internal.Data.Parser.ParserD.Tee--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Parallel parsers. Distributing the input to multiple parsers at the same--- time.------ For simplicity, we are using code where a particular state is unreachable--- but it is not prevented by types.  Somehow uni-pattern match using "let"--- produces better optimized code compared to using @case@ match and using--- explicit error messages in unreachable cases.------ There seem to be no way to silence individual warnings so we use a global--- incomplete uni-pattern match warning suppression option for the file.--- Disabling the warning for other code as well  has the potential to mask off--- some legit warnings, therefore, we have segregated only the code that uses--- uni-pattern matches in this module.--module Streamly.Internal.Data.Parser.ParserD.Tee-    (-    {--    -- Parallel zipped-      teeWith-    , teeWithFst-    , teeWithMin--    -- Parallel alternatives-    , shortest-    , longest-    -}-    )-where--{--import Control.Exception (assert)-import Control.Monad.Catch (MonadCatch, try)-import Prelude-       hiding (any, all, takeWhile)--import Fusion.Plugin.Types (Fuse(..))-import Streamly.Internal.Data.Parser.ParserD.Type-       (Initial(..), Parser(..), Step(..), ParseError)------------------------------------------------------------------------------------ Distribute input to two parsers and collect both results------------------------------------------------------------------------------------ When the input stream is distributed to two parsers, both the parsers can--- backtrack independently. Therefore, we need separate buffer state for each--- parser.------ ParserK------ We can keep the state of each parser in the zipper and pass around that--- zipper to the parsers. Each parser can consume from the zipper and then pass--- around the zipper to the other parser.------ ParserD------ In the approach we have taken here, the driver pushes one element at a time--- to the tee and each of the parsers in the tee may buffer it independently--- for backtracking. So they do not need to depend on the original stream--- source for individual parser backtracking. Problem arises when both the--- parsers backtrack and they do not need any input from the driver rather they--- must consume from their buffers. For such situation we may need a--- "Continue" style driver command from the tee so that the driver runs--- the tee without providing it any input. Or we may need a local driver loop--- until new input is to be demanded from the input stream.------ When the tee errors out or stops, the tee driver may have to backtrack by--- the specified amount (or the tee must return the leftover input). Therefore,--- the tee driver also has to buffer, this leads to triple buffering.------ When the tee stops we need to determine the backtracking amount from the--- leftover of both the parsers. Since both the parsers may have consumed--- different lengths of the stream we consider the maximum of the two as--- consumed.----  -- XXX We can use Initial instead of StepState-{-# ANN type StepState Fuse #-}-data StepState s a = StepState s | StepResult a---- | State of the pair of parsers in a tee composition--- Note: strictness annotation is important for fusing the constructors-{-# ANN type TeeState Fuse #-}-data TeeState sL sR x a b =--- @TeePair (past buffer, parser state, future-buffer1, future-buffer2) ...@-    TeePair !([x], StepState sL a, [x], [x]) !([x], StepState sR b, [x], [x])--{-# ANN type Res Fuse #-}-data Res = Yld Int | Stp Int | Skp | Err String---- | See 'Streamly.Internal.Data.Parser.teeWith'.------ /Broken/----{-# INLINE teeWith #-}-teeWith :: Monad m-    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c-teeWith zf (Parser stepL initialL extractL) (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    {-# INLINE_LATE initial #-}-    initial = do-        resL <- initialL-        resR <- initialR-        return $ case resL of-            IPartial sl ->-                case resR of-                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])-                                                       ([], StepState sr, [], [])-                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])-                                                    ([], StepResult br, [], [])-                     IError err -> IError err-            IDone bl ->-                case resR of-                     IPartial sr ->-                         IPartial $ TeePair ([], StepResult bl, [], [])-                                            ([], StepState sr, [], [])-                     IDone br -> IDone $ zf bl br-                     IError err -> IError err-            IError err -> IError err--    {-# INLINE consume #-}-    consume buf inp1 inp2 stp st y = do-        let (x, inp11, inp21) =-                case inp1 of-                    [] -> (y, [], [])-                    z : [] -> (z, reverse (x:inp2), [])-                    z : zs -> (z, zs, x:inp2)-        r <- stp st x-        let buf1 = x:buf-        return (buf1, r, inp11, inp21)--    -- XXX This is currently broken, even though both the parsers need to-    -- consume from their buffers after backtracking the driver would still be-    -- pushing more input to the buffers.-    ---    -- consume one input item and return the next state of the fold-    {-# INLINE useStream #-}-    useStream buf inp1 inp2 stp st y = do-        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y-        case r of-            Partial 0 s ->-                let state = ([], StepState s, inp11, inp21)-                 in return (state, Yld 0)-            Partial n s ->-                let src0 = Prelude.take n buf1-                    src  = Prelude.reverse src0-                    state = ([], StepState s, src ++ inp11, inp21)-                 in assert (n <= length buf1) (return (state, Yld n))-            Done n b ->-                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)-                 in assert (n <= length buf1) (return (state, Stp n))-            -- Continue 0 s -> (buf1, Right s, inp11, inp21)-            Continue n s ->-                let (src0, buf2) = splitAt n buf1-                    src  = Prelude.reverse src0-                    state = (buf2, StepState s, src ++ inp11, inp21)-                 in assert (n <= length buf1) (return (state, Skp))-            Error err -> return (undefined, Err err)--    {-# INLINE_LATE step #-}-    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                  (bufR, StepState sR, inpR1, inpR2)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        return $ case (stL,stR) of-            (Yld n1, Yld n2) -> Partial (min n1 n2) next-            (Yld n1, Stp n2) -> Partial (min n1 n2) next-            (Stp n1, Yld n2) -> Partial (min n1 n2) next-            (Stp n1, Stp n2) ->-                -- Uni-pattern match results in better optimized code compared-                -- to a case match.-                let (_, StepResult rL, _, _) = l-                    (_, StepResult rR, _, _) = r-                 in Done (min n1 n2) (zf rL rR)-            (Err err, _) -> Error err-            (_, Err err) -> Error err-            _ -> Continue 0 next--    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                r@(_, StepResult rR, _, _)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        let next = TeePair l r-        -- XXX If the unused count of this stream is lower than the unused-        -- count of the stopped stream, only then this will be correct. We need-        -- to fix the other case. We need to keep incrementing the unused count-        -- of the stopped stream and take the min of the two.-        return $ case stL of-            Yld n -> Partial n next-            Stp n ->-                let (_, StepResult rL, _, _) = l-                 in Done n (zf rL rR)-            Skp -> Continue 0 next-            Err err -> Error err--    step (TeePair l@(_, StepResult rL, _, _)-                    (bufR, StepState sR, inpR1, inpR2)) x = do-        (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        -- XXX If the unused count of this stream is lower than the unused-        -- count of the stopped stream, only then this will be correct. We need-        -- to fix the other case. We need to keep incrementing the unused count-        -- of the stopped stream and take the min of the two.-        return $ case stR of-            Yld n -> Partial n next-            Stp n ->-                let (_, StepResult rR, _, _) = r-                 in Done n (zf rL rR)-            Skp -> Continue 0 next-            Err err -> Error err--    step _ _ = undefined--    {-# INLINE_LATE extract #-}-    extract st =-        case st of-            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do-                rL <- extractL sL-                rR <- extractR sR-                return $ zf rL rR-            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do-                rL <- extractL sL-                return $ zf rL rR-            TeePair (_, StepResult  rL, _, _) (_, StepState sR, _, _) -> do-                rR <- extractR sR-                return $ zf rL rR-            TeePair (_, StepResult rL, _, _) (_, StepResult rR, _, _) ->-                return $ zf rL rR---- | See 'Streamly.Internal.Data.Parser.teeWithFst'.------ /Broken/----{-# INLINE teeWithFst #-}-teeWithFst :: Monad m-    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c-teeWithFst zf (Parser stepL initialL extractL)-              (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    {-# INLINE_LATE initial #-}-    initial = do-        resL <- initialL-        resR <- initialR-        case resL of-            IPartial sl ->-                return $ case resR of-                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])-                                                       ([], StepState sr, [], [])-                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])-                                                    ([], StepResult br, [], [])-                     IError err -> IError err-            IDone bl ->-                case resR of-                     IPartial sr -> IDone . zf bl <$> extractR sr-                     IDone br -> return $ IDone $ zf bl br-                     IError err -> return $ IError err-            IError err -> return $ IError err--    {-# INLINE consume #-}-    consume buf inp1 inp2 stp st y = do-        let (x, inp11, inp21) =-                case inp1 of-                    [] -> (y, [], [])-                    z : [] -> (z, reverse (x:inp2), [])-                    z : zs -> (z, zs, x:inp2)-        r <- stp st x-        let buf1 = x:buf-        return (buf1, r, inp11, inp21)--    -- consume one input item and return the next state of the fold-    {-# INLINE useStream #-}-    useStream buf inp1 inp2 stp st y = do-        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y-        case r of-            Partial 0 s ->-                let state = ([], StepState s, inp11, inp21)-                 in return (state, Yld 0)-            Partial n _ -> return (undefined, Yld n) -- Not implemented-            Done n b ->-                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)-                 in assert (n <= length buf1) (return (state, Stp n))-            -- Continue 0 s -> (buf1, Right s, inp11, inp21)-            Continue n s ->-                let (src0, buf2) = splitAt n buf1-                    src  = Prelude.reverse src0-                    state = (buf2, StepState s, src ++ inp11, inp21)-                 in assert (n <= length buf1) (return (state, Skp))-            Error err -> return (undefined, Err err)--    {-# INLINE_LATE step #-}-    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                  (bufR, StepState sR, inpR1, inpR2)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        case (stL,stR) of-            -- XXX what if the first parser returns an unused count which is-            -- more than the second parser's unused count? It does not make-            -- sense for the second parser to consume more than the first-            -- parser. We reset the input cursor based on the first parser.-            -- Error out if the second one has consumed more then the first?-            (Stp n1, Stp _) ->-                -- Uni-pattern match results in better optimized code compared-                -- to a case match.-                let (_, StepResult rL, _, _) = l-                    (_, StepResult rR, _, _) = r-                 in return $ Done n1 (zf rL rR)-            (Stp n1, Yld _) ->-                let (_, StepResult rL, _, _) = l-                    (_, StepState  ssR, _, _) = r-                 in do-                    rR <- extractR ssR-                    return $ Done n1 (zf rL rR)-            (Yld n1, Yld n2) -> return $ Partial (min n1 n2) next-            (Yld n1, Stp n2) -> return $ Partial (min n1 n2) next-            (Err err, _) -> return $ Error err-            (_, Err err) -> return $ Error err-            _ -> return $ Continue 0 next--    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                r@(_, StepResult rR, _, _)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        let next = TeePair l r-        -- XXX If the unused count of this stream is lower than the unused-        -- count of the stopped stream, only then this will be correct. We need-        -- to fix the other case. We need to keep incrementing the unused count-        -- of the stopped stream and take the min of the two.-        return $ case stL of-            Yld n -> Partial n next-            Stp n ->-                let (_, StepResult rL, _, _) = l-                 in Done n (zf rL rR)-            Skp -> Continue 0 next-            Err err -> Error err--    step _ _ = undefined--    {-# INLINE_LATE extract #-}-    extract st =-        case st of-            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do-                rL <- extractL sL-                rR <- extractR sR-                return $ zf rL rR-            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do-                rL <- extractL sL-                return $ zf rL rR-            _ -> error "unreachable"---- | See 'Streamly.Internal.Data.Parser.teeWithMin'.------ /Unimplemented/----{-# INLINE teeWithMin #-}-teeWithMin ::-    -- Monad m =>-    (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c-teeWithMin = undefined------------------------------------------------------------------------------------ Distribute input to two parsers and choose one result------------------------------------------------------------------------------------ | See 'Streamly.Internal.Data.Parser.shortest'.------ /Broken/----{-# INLINE shortest #-}-shortest :: Monad m => Parser x m a -> Parser x m a -> Parser x m a-shortest (Parser stepL initialL extractL) (Parser stepR initialR _) =-    Parser step initial extract--    where--    {-# INLINE_LATE initial #-}-    initial = do-        resL <- initialL-        resR <- initialR-        return $ case resL of-            IPartial sl ->-                case resR of-                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])-                                                       ([], StepState sr, [], [])-                     IDone br -> IDone br-                     IError err -> IError err-            IDone bl -> IDone bl-            IError errL ->-                case resR of-                     IPartial _ -> IError errL-                     IDone br -> IDone br-                     IError errR -> IError errR--    {-# INLINE consume #-}-    consume buf inp1 inp2 stp st y = do-        let (x, inp11, inp21) =-                case inp1 of-                    [] -> (y, [], [])-                    z : [] -> (z, reverse (x:inp2), [])-                    z : zs -> (z, zs, x:inp2)-        r <- stp st x-        let buf1 = x:buf-        return (buf1, r, inp11, inp21)--    -- consume one input item and return the next state of the fold-    {-# INLINE useStream #-}-    useStream buf inp1 inp2 stp st y = do-        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y-        case r of-            Partial 0 s ->-                let state = ([], StepState s, inp11, inp21)-                 in return (state, Yld 0)-            Partial n _ -> return (undefined, Yld n) -- Not implemented-            Done n b ->-                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)-                 in assert (n <= length buf1) (return (state, Stp n))-            -- Continue 0 s -> (buf1, Right s, inp11, inp21)-            Continue n s ->-                let (src0, buf2) = splitAt n buf1-                    src  = Prelude.reverse src0-                    state = (buf2, StepState s, src ++ inp11, inp21)-                 in assert (n <= length buf1) (return (state, Skp))-            Error err -> return (undefined, Err err)--    -- XXX Even if a parse finished earlier it may not be shortest if the other-    -- parser finishes later but returns a lot of unconsumed input. Our current-    -- criterion of shortest is whichever parse decided to stop earlier.-    {-# INLINE_LATE step #-}-    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                  (bufR, StepState sR, inpR1, inpR2)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        return $ case (stL,stR) of-            (Stp n1, _) ->-                let (_, StepResult rL, _, _) = l-                 in Done n1 rL-            (_, Stp n2) ->-                let (_, StepResult rR, _, _) = r-                 in Done n2 rR-            (Yld n1, Yld n2) -> Partial (min n1 n2) next-            (Err err, _) -> Error err-            (_, Err err) -> Error err-            _ -> Continue 0 next--    step _ _ = undefined--    {-# INLINE_LATE extract #-}-    extract st =-        case st of-            TeePair (_, StepState sL, _, _) _ -> extractL sL-            _ -> error "unreachable"---- | See 'Streamly.Internal.Data.Parser.longest'.------ /Broken/----{-# INLINE longest #-}-longest :: MonadCatch m => Parser x m a -> Parser x m a -> Parser x m a-longest (Parser stepL initialL extractL) (Parser stepR initialR extractR) =-    Parser step initial extract--    where---    {-# INLINE_LATE initial #-}-    initial = do-        resL <- initialL-        resR <- initialR-        return $ case resL of-            IPartial sl ->-                case resR of-                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])-                                                       ([], StepState sr, [], [])-                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])-                                                    ([], StepResult br, [], [])-                     IError _ ->-                         IPartial $ TeePair ([], StepState sl, [], [])-                                            ([], StepResult undefined, [], [])-            IDone bl ->-                case resR of-                     IPartial sr ->-                         IPartial $ TeePair ([], StepResult bl, [], [])-                                            ([], StepState sr, [], [])-                     IDone _ -> IDone bl-                     IError _ -> IDone bl-            IError _ ->-                case resR of-                     IPartial sr ->-                         IPartial $ TeePair ([], StepResult undefined, [], [])-                                            ([], StepState sr, [], [])-                     IDone br -> IDone br-                     IError err -> IError err--    {-# INLINE consume #-}-    consume buf inp1 inp2 stp st y = do-        let (x, inp11, inp21) =-                case inp1 of-                    [] -> (y, [], [])-                    z : [] -> (z, reverse (x:inp2), [])-                    z : zs -> (z, zs, x:inp2)-        r <- stp st x-        let buf1 = x:buf-        return (buf1, r, inp11, inp21)--    -- consume one input item and return the next state of the fold-    {-# INLINE useStream #-}-    useStream buf inp1 inp2 stp st y = do-        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y-        case r of-            Partial 0 s ->-                let state = ([], StepState s, inp11, inp21)-                 in return (state, Yld 0)-            Partial n _ -> return (undefined, Yld n) -- Not implemented-            Done n b ->-                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)-                 in assert (n <= length buf1) (return (state, Stp n))-            -- Continue 0 s -> (buf1, Right s, inp11, inp21)-            Continue n s ->-                let (src0, buf2) = splitAt n buf1-                    src  = Prelude.reverse src0-                    state = (buf2, StepState s, src ++ inp11, inp21)-                 in assert (n <= length buf1) (return (state, Skp))-            Error err -> return (undefined, Err err)--    {-# INLINE_LATE step #-}-    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                  (bufR, StepState sR, inpR1, inpR2)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        return $ case (stL,stR) of-            (Yld n1, Yld n2) -> Partial (min n1 n2) next-            (Yld n1, Stp n2) -> Partial (min n1 n2) next-            (Stp n1, Yld n2) -> Partial (min n1 n2) next-            (Stp n1, Stp n2) ->-                let (_, StepResult rL, _, _) = l-                    (_, StepResult rR, _, _) = r-                 in Done (max n1 n2) (if n1 >= n2 then rL else rR)-            (Err err, _) -> Error err-            (_, Err err) -> Error err-            _ -> Continue 0 next--    -- XXX the parser that finishes last may not be the longest because it may-    -- return a lot of unused input which makes it shorter. Our current-    -- criterion of deciding longest is based on whoever decides to finish-    -- last and not whoever consumed more input.-    ---    -- To actually know who made more progress we need to keep an account of-    -- how many items are unconsumed since the last yield.-    ---    step (TeePair (bufL, StepState sL, inpL1, inpL2)-                r@(_, StepResult _, _, _)) x = do-        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x-        let next = TeePair l r-        return $ case stL of-            Yld n -> Partial n next-            Stp n ->-                let (_, StepResult rL, _, _) = l-                 in Done n rL-            Skp -> Continue 0 next-            Err err -> Error err--    step (TeePair l@(_, StepResult _, _, _)-                    (bufR, StepState sR, inpR1, inpR2)) x = do-        (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x-        let next = TeePair l r-        return $ case stR of-            Yld n -> Partial n next-            Stp n ->-                let (_, StepResult rR, _, _) = r-                 in Done n rR-            Skp -> Continue 0 next-            Err err -> Error err--    step _ _ = undefined--    {-# INLINE_LATE extract #-}-    extract st =-        -- XXX When results are partial we may not be able to precisely compare-        -- which parser has made more progress till now.  One way to do that is-        -- to figure out the actually consumed input up to the last yield.-        ---        case st of-            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do-                r <- try $ extractL sL-                case r of-                    Left (_ :: ParseError) -> extractR sR-                    Right b -> return b-            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do-                r <- try $ extractL sL-                case r of-                    Left (_ :: ParseError) -> return rR-                    Right b -> return b-            TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do-                r <- try $ extractR sR-                case r of-                    Left (_ :: ParseError) -> return rL-                    Right b -> return b-            TeePair (_, StepResult _, _, _) (_, StepResult _, _, _) ->-                error "unreachable"--}
− src/Streamly/Internal/Data/Parser/ParserD/Type.hs
@@ -1,1429 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Parser.ParserD.Type--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Streaming and backtracking parsers.------ Parsers just extend folds.  Please read the 'Fold' design notes in--- "Streamly.Internal.Data.Fold.Type" for background on the design.------ = Parser Design------ The 'Parser' type or a parsing fold is a generalization of the 'Fold' type.--- The 'Fold' type /always/ succeeds on each input. Therefore, it does not need--- to buffer the input. In contrast, a 'Parser' may fail and backtrack to--- replay the input again to explore another branch of the parser. Therefore,--- it needs to buffer the input. Therefore, a 'Parser' is a fold with some--- additional requirements.  To summarize, unlike a 'Fold', a 'Parser':------ 1. may not generate a new value of the accumulator on every input, it may--- generate a new accumulator only after consuming multiple input elements--- (e.g. takeEQ).--- 2. on success may return some unconsumed input (e.g. takeWhile)--- 3. may fail and return all input without consuming it (e.g. satisfy)--- 4. backtrack and start inspecting the past input again (e.g. alt)------ These use cases require buffering and replaying of input.  To facilitate--- this, the step function of the 'Fold' is augmented to return the next state--- of the fold along with a command tag using a 'Step' functor, the tag tells--- the fold driver to manipulate the future input as the parser wishes. The--- 'Step' functor provides the following commands to the fold driver--- corresponding to the use cases outlined in the previous para:------ 1. 'Continue': buffer the current input and optionally go back to a previous---    position in the stream--- 2. 'Partial': buffer the current input and optionally go back to a previous---    position in the stream, drop the buffer before that position.--- 3. 'Done': parser succeeded, returns how much input was leftover--- 4. 'Error': indicates that the parser has failed without a result------ = How a Parser Works?------ A parser is just like a fold, it keeps consuming inputs from the stream and--- accumulating them in an accumulator. The accumulator of the parser could be--- a singleton value or it could be a collection of values e.g. a list.------ The parser may build a new output value from multiple input items. When it--- consumes an input item but needs more input to build a complete output item--- it uses @Continue 0 s@, yielding the intermediate state @s@ and asking the--- driver to provide more input.  When the parser determines that a new output--- value is complete it can use a @Done n b@ to terminate the parser with @n@--- items of input unused and the final value of the accumulator returned as--- @b@. If at any time the parser determines that the parse has failed it can--- return @Error err@.------ A parser building a collection of values (e.g. a list) can use the @Partial@--- constructor whenever a new item in the output collection is generated. If a--- parser building a collection of values has yielded at least one value then--- it is considered successful and cannot fail after that. In the current--- implementation, this is not automatically enforced, there is a rule that the--- parser MUST use only @Done@ for termination after the first @Partial@, it--- cannot use @Error@. It may be possible to change the implementation so that--- this rule is not required, but there may be some performance cost to it.------ 'Streamly.Internal.Data.Parser.takeWhile' and--- 'Streamly.Internal.Data.Parser.some' combinators are good examples of--- efficient implementations using all features of this representation.  It is--- possible to idiomatically build a collection of parsed items using a--- singleton parser and @Alternative@ instance instead of using a--- multi-yield parser.  However, this implementation is amenable to stream--- fusion and can therefore be much faster.------ = Error Handling------ When a parser's @step@ function is invoked it may terminate by either a--- 'Done' or an 'Error' return value. In an 'Alternative' composition an error--- return can make the composed parser backtrack and try another parser.------ If the stream stops before a parser could terminate then we use the--- @extract@ function of the parser to retrieve the last yielded value of the--- parser. If the parser has yielded at least one value then @extract@ MUST--- return a value without throwing an error, otherwise it uses the 'ParseError'--- exception to throw an error.------ We chose the exception throwing mechanism for @extract@ instead of using an--- explicit error return via an 'Either' type for keeping the interface simple--- as most of the time we do not need to catch the error in intermediate--- layers. Note that we cannot use exception throwing mechanism in @step@--- function because of performance reasons. 'Error' constructor in that case--- allows loop fusion and better performance.------ = Optimizing backtracking------ == Applicative Composition------ If a parser once returned 'Partial' it can never fail after that. This is--- used to reduce the buffering. A 'Partial' results in dropping the buffer and--- we cannot backtrack before that point.------ Parsers can be composed using an Alternative, if we are in an alternative--- composition we may have to backtrack to try the other branch.  When we--- compose two parsers using applicative @f <$> p1 <*> p2@ we can return a--- 'Partial' result only after both the parsers have succeeded. While running--- @p1@ we have to ensure that the input is not dropped until we have run @p2@,--- therefore we have to return a Continue instead of a Partial.------ However, if we know they both cannot fail then we know that the composed--- parser can never fail.  For this reason we should have "backtracking folds"--- as a separate type so that we can compose them in an efficient manner. In p1--- itself we can drop the buffer as soon as a 'Partial' result arrives. In--- fact, there is no Alternative composition for folds because they cannot--- fail.------ == Alternative Composition------ In @p1 <|> p2@ as soon as the parser p1 returns 'Partial' we know that it--- will not fail and we can immediately drop the buffer.------ If we are not using the parser in an alternative composition we can--- downgrade the parser to a backtracking fold and use the "backtracking--- fold"'s applicative for more efficient implementation. To downgrade we can--- translate the "Error" of parser to an exception.  This gives us best of both--- worlds, the applicative as well as alternative would have optimal--- backtracking buffer.------ The "many" for parsers would be different than "many" for folds. In case of--- folds an error would be propagated. In case of parsers the error would be--- ignored.------ = Implementation Approach------ Backtracking folds have an issue with tee style composition because each--- fold can backtrack independently, we will need independent buffers. Though--- this may be possible to implement it may not be efficient especially for--- folds that do not backtrack at all. Three types are possible, optimized for--- different use cases:------ * Non-backtracking folds: efficient Tee--- * Backtracking folds: efficient applicative--- * Parsers: alternative------ Downgrade parsers to backtracking folds for applicative used without--- alternative.  Upgrade backtracking folds to parsers when we have to use them--- as the last alternative.------ = Future Work------ It may make sense to move "takeWhile" type of parsers, which cannot fail but--- need some lookahead, to splitting folds.  This will allow such combinators--- to be accepted where we need an unfailing "Fold" type.------ Based on application requirements it should be possible to design even a--- richer interface to manipulate the input stream/buffer. For example, we--- could randomly seek into the stream in the forward or reverse directions or--- we can even seek to the end or from the end or seek from the beginning.------ We can distribute and scan/parse a stream using both folds and parsers and--- merge the resulting streams using different merge strategies (e.g.--- interleaving or serial).------ == Naming------ As far as possible, try that the names of the combinators in this module are--- consistent with:------ * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>--- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>--- * <http://hackage.haskell.org/package/megaparsec megaparsec>--- * <http://hackage.haskell.org/package/attoparsec attoparsec>--- * <http://hackage.haskell.org/package/parsec parsec>--module Streamly.Internal.Data.Parser.ParserD.Type-    (-    -- * Setup-    -- $setup--    -- * Types-      Initial (..)-    , Step (..)-    , extractStep-    , bimapOverrideCount-    , Parser (..)-    , ParseError (..)-    , rmapM--    -- * Constructors--    , fromPure-    , fromEffect-    , splitWith-    , split_--    , die-    , dieM-    , splitSome -- parseSome?-    , splitMany -- parseMany?-    , splitManyPost-    , alt-    , concatMap--    -- * Input transformation-    , lmap-    , lmapM-    , filter--    , noErrorUnsafeSplitWith-    , noErrorUnsafeSplit_-    , noErrorUnsafeConcatMap-    )-where--#include "inline.hs"-#include "assert.hs"--import Control.Applicative (Alternative(..), liftA2)-import Control.Exception (Exception(..))--- import Control.Monad (MonadPlus(..), (>=>))-import Control.Monad ((>=>))-import Control.Monad.IO.Class (MonadIO, liftIO)-import Data.Bifunctor (Bifunctor(..))-import Fusion.Plugin.Types (Fuse(..))-import Streamly.Internal.Data.Fold.Type (Fold(..), toList)--import qualified Control.Monad.Fail as Fail-import qualified Streamly.Internal.Data.Fold.Type as FL--import Prelude hiding (concatMap, filter)--#include "DocTestDataParser.hs"---- XXX The only differences between Initial and Step types are:------ * There are no backtracking counts in Initial--- * Continue and Partial are the same. Ideally Partial should mean that an--- empty result is valid and can be extracted; and Continue should mean that--- empty would result in an error on extraction. We can possibly distinguish--- the two cases.------ If we ignore the backtracking counts we can represent the Initial type using--- Step itself. That will also simplify the implementation of various parsers--- where the processing in intiial is just a sepcial case of step, see--- takeBetween for example.---- | The type of a 'Parser''s initial action.------ /Internal/----{-# ANN type Initial Fuse #-}-data Initial s b-    = IPartial !s   -- ^ Wait for step function to be called with state @s@.-    | IDone !b      -- ^ Return a result right away without an input.-    | IError !String -- ^ Return an error right away without an input.---- | @first@ maps on 'IPartial' and @second@ maps on 'IDone'.------ /Internal/----instance Bifunctor Initial where-    {-# INLINE bimap #-}-    bimap f _ (IPartial a) = IPartial (f a)-    bimap _ g (IDone b) = IDone (g b)-    bimap _ _ (IError err) = IError err---- | Maps a function over the result held by 'IDone'.------ >>> fmap = second------ /Internal/----instance Functor (Initial s) where-    {-# INLINE fmap #-}-    fmap = second---- We can simplify the Step type as follows:------ Partial Int (Either s (s, b)) -- Left continue, right partial result--- Done Int (Either String b)------ In this case Error may also have a "leftover" return. This means that after--- several successful partial results the last segment parsing failed and we--- are returning the leftover of that. The driver may choose to restart from--- the last segment where this parser failed or from the beginning.------ Folds can only return the right values. Parsers can also return lefts.---- | The return type of a 'Parser' step.------ The parse operation feeds the input stream to the parser one element at a--- time, representing a parse 'Step'. The parser may or may not consume the--- item and returns a result. If the result is 'Partial' we can either extract--- the result or feed more input to the parser. If the result is 'Continue', we--- must feed more input in order to get a result. If the parser returns 'Done'--- then the parser can no longer take any more input.------ If the result is 'Continue', the parse operation retains the input in a--- backtracking buffer, in case the parser may ask to backtrack in future.--- Whenever a 'Partial n' result is returned we first backtrack by @n@ elements--- in the input and then release any remaining backtracking buffer. Similarly,--- 'Continue n' backtracks to @n@ elements before the current position and--- starts feeding the input from that point for future invocations of the--- parser.------ If parser is not yet done, we can use the @extract@ operation on the @state@--- of the parser to extract a result. If the parser has not yet yielded a--- result, the operation fails with a 'ParseError' exception. If the parser--- yielded a 'Partial' result in the past the last partial result is returned.--- Therefore, if a parser yields a partial result once it cannot fail later on.------ The parser can never backtrack beyond the position where the last partial--- result left it at. The parser must ensure that the backtrack position is--- always after that.------ /Pre-release/----{-# ANN type Step Fuse #-}-data Step s b =-        Partial !Int !s-    -- ^ @Partial count state@. The following hold on Partial result:-    ---    -- 1. @extract@ on @state@ would succeed and give a result.-    -- 2. Input stream position is reset to @current position - count@.-    -- 3. All input before the new position is dropped. The parser can-    -- never backtrack beyond this position.--    | Continue !Int !s-    -- ^ @Continue count state@. The following hold on a Continue result:-    ---    -- 1. If there was a 'Partial' result in past, @extract@ on @state@ would-    -- give that result as 'Done' otherwise it may return 'Error' or-    -- 'Continue'.-    -- 2. Input stream position is reset to @current position - count@.-    -- 3. the input is retained in a backtrack buffer.--    | Done !Int !b-    -- ^ Done with leftover input count and result.-    ---    -- @Done count result@ means the parser has finished, it will accept no-    -- more input, last @count@ elements from the input are unused and the-    -- result of the parser is in @result@.--    | Error !String-    -- ^ Parser failed without generating any output.-    ---    -- The parsing operation may backtrack to the beginning and try another-    -- alternative.---- | Map first function over the state and second over the result.-instance Bifunctor Step where-    {-# INLINE bimap #-}-    bimap f g step =-        case step of-            Partial n s -> Partial n (f s)-            Continue n s -> Continue n (f s)-            Done n b -> Done n (g b)-            Error err -> Error err---- | Bimap discarding the count, and using the supplied count instead.-bimapOverrideCount :: Int -> (s -> s1) -> (b -> b1) -> Step s b -> Step s1 b1-bimapOverrideCount n f g step =-    case step of-        Partial _ s -> Partial n (f s)-        Continue _ s -> Continue n (f s)-        Done _ b -> Done n (g b)-        Error err -> Error err---- | fmap = second-instance Functor (Step s) where-    {-# INLINE fmap #-}-    fmap = second--{-# INLINE assertStepCount #-}-assertStepCount :: Int -> Step s b -> Step s b-assertStepCount i step =-    case step of-        Partial n _ -> assert (i == n) step-        Continue n _ -> assert (i == n) step-        Done n _ -> assert (i == n) step-        Error _ -> step---- | Map an extract function over the state of Step----{-# INLINE extractStep #-}-extractStep :: Monad m => (s -> m (Step s1 b)) -> Step s b -> m (Step s1 b)-extractStep f res =-    case res of-        Partial n s1 -> assertStepCount n <$> f s1-        Done n b -> return $ Done n b-        Continue n s1 -> assertStepCount n <$> f s1-        Error err -> return $ Error err---- | Map a monadic function over the result @b@ in @Step s b@.------ /Internal/-{-# INLINE mapMStep #-}-mapMStep :: Applicative m => (a -> m b) -> Step s a -> m (Step s b)-mapMStep f res =-    case res of-        Partial n s -> pure $ Partial n s-        Done n b -> Done n <$> f b-        Continue n s -> pure $ Continue n s-        Error err -> pure $ Error err---- | A parser is a fold that can fail and is represented as @Parser step--- initial extract@. Before we drive a parser we call the @initial@ action to--- retrieve the initial state of the fold. The parser driver invokes @step@--- with the state returned by the previous step and the next input element. It--- results into a new state and a command to the driver represented by 'Step'--- type. The driver keeps invoking the step function until it stops or fails.--- At any point of time the driver can call @extract@ to inspect the result of--- the fold. If the parser hits the end of input 'extract' is called.--- It may result in an error or an output value.------ /Pre-release/----data Parser a m b =-    forall s. Parser-        (s -> a -> m (Step s b))-        -- Initial cannot return "Partial/Done n" or "Continue". Continue 0 is-        -- same as Partial 0. In other words it cannot backtrack.-        (m (Initial s b))-        -- Extract can only return Partial or Continue n. In other words it can-        -- only backtrack or return partial result/error. But we do not return-        -- result in Partial, therefore, we have to use Done instead of Partial.-        (s -> m (Step s b))---- | This exception is used when a parser ultimately fails, the user of the--- parser is intimated via this exception.------ /Pre-release/----newtype ParseError = ParseError String-    deriving Show--instance Exception ParseError where-    displayException (ParseError err) = err--instance Functor m => Functor (Parser a m) where-    {-# INLINE fmap #-}-    fmap f (Parser step1 initial1 extract) =-        Parser step initial (fmap3 f extract)--        where--        initial = fmap2 f initial1-        step s b = fmap2 f (step1 s b)-        fmap2 g = fmap (fmap g)-        fmap3 g = fmap2 (fmap g)----------------------------------------------------------------------------------- Mapping on the output----------------------------------------------------------------------------------- | @rmapM f parser@ maps the monadic function @f@ on the output of the parser.------ >>> rmap = fmap-{-# INLINE rmapM #-}-rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c-rmapM f (Parser step initial extract) =-    Parser step1 initial1 (extract >=> mapMStep f)--    where--    initial1 = do-        res <- initial-        -- this is mapM f over result-        case res of-            IPartial x -> return $ IPartial x-            IDone a -> IDone <$> f a-            IError err -> return $ IError err-    step1 s a = step s a >>= mapMStep f---- | A parser that always yields a pure value without consuming any input.----{-# INLINE_NORMAL fromPure #-}-fromPure :: Monad m => b -> Parser a m b-fromPure b = Parser undefined (pure $ IDone b) undefined---- | A parser that always yields the result of an effectful action without--- consuming any input.----{-# INLINE fromEffect #-}-fromEffect :: Monad m => m b -> Parser a m b-fromEffect b = Parser undefined (IDone <$> b) undefined------------------------------------------------------------------------------------ Sequential applicative----------------------------------------------------------------------------------{-# ANN type SeqParseState Fuse #-}-data SeqParseState sl f sr = SeqParseL !sl | SeqParseR !f !sr---- Note: this implementation of splitWith is fast because of stream fusion but--- has quadratic time complexity, because each composition adds a new branch--- that each subsequent parse's input element has to go through, therefore, it--- cannot scale to a large number of compositions. After around 100--- compositions the performance starts dipping rapidly beyond a CPS style--- unfused implementation.------ Note: This is a parsing dual of appending streams using--- 'Streamly.Data.Stream.append', it splits the streams using two parsers and--- zips the results.---- | Sequential parser application.------ Apply two parsers sequentially to an input stream. The first parser runs and--- processes the input, the remaining input is then passed to the second--- parser. If both parsers succeed, their outputs are combined using the--- supplied function. If either parser fails, the operation fails.------ This implementation is strict in the second argument, therefore, the--- following will fail:------ >>> Stream.parse (Parser.splitWith const (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]--- *** Exception: Prelude.undefined--- ...------ Although this implementation allows stream fusion, it has quadratic--- complexity, making it suitable only for a small number of compositions.--- As a thumb rule use it for less than 8 compositions, use ParserK otherwise.------ Below are some common idioms that can be expressed using 'splitWith' and--- other parser primitives:------ >>> span p f1 f2 = Parser.splitWith (,) (Parser.takeWhile p f1) (Parser.fromFold f2)--- >>> spanBy eq f1 f2 = Parser.splitWith (,) (Parser.groupBy eq f1) (Parser.fromFold f2)------ /Pre-release/----{-# INLINE splitWith #-}-splitWith :: Monad m-    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c-splitWith func (Parser stepL initialL extractL)-               (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    initial = do-        -- XXX We can use bimap here if we make this a Step type-        resL <- initialL-        case resL of-            IPartial sl -> return $ IPartial $ SeqParseL sl-            IDone bl -> do-                resR <- initialR-                -- XXX We can use bimap here if we make this a Step type-                return $ case resR of-                    IPartial sr -> IPartial $ SeqParseR (func bl) sr-                    IDone br -> IDone (func bl br)-                    IError err -> IError err-            IError err -> return $ IError err--    -- Note: For the composed parse to terminate, the left parser has to be-    -- a terminating parser returning a Done at some point.-    step (SeqParseL st) a = do-        -- Important: Please do not use Applicative here. See-        -- https://github.com/composewell/streamly/issues/1033 and the problem-        -- defined in split_ for more info.-        -- XXX Use bimap-        resL <- stepL st a-        case resL of-            -- Note: We need to buffer the input for a possible Alternative-            -- e.g. in ((,) <$> p1 <*> p2) <|> p3, if p2 fails we have to-            -- backtrack and start running p3. So we need to keep the input-            -- buffered until we know that the applicative cannot fail.-            Partial n s -> return $ Continue n (SeqParseL s)-            Continue n s -> return $ Continue n (SeqParseL s)-            Done n b -> do-                -- XXX Use bimap if we make this a Step type-                -- fmap (bimap (SeqParseR (func b)) (func b)) initialR-                initR <- initialR-                return $ case initR of-                   IPartial sr -> Continue n $ SeqParseR (func b) sr-                   IDone br -> Done n (func b br)-                   IError err -> Error err-            Error err -> return $ Error err--    step (SeqParseR f st) a = fmap (bimap (SeqParseR f) f) (stepR st a)--    extract (SeqParseR f sR) = fmap (bimap (SeqParseR f) f) (extractR sR)-    extract (SeqParseL sL) = do-        -- XXX Use bimap here-        rL <- extractL sL-        case rL of-            Done n bL -> do-                -- XXX Use bimap here if we use Step type in Initial-                iR <- initialR-                case iR of-                    IPartial sR -> do-                        fmap-                            (bimap (SeqParseR (func bL)) (func bL))-                            (extractR sR)-                    IDone bR -> return $ Done n $ func bL bR-                    IError err -> return $ Error err-            Error err -> return $ Error err-            Partial _ _ -> error "Bug: splitWith extract 'Partial'"-            Continue n s -> return $ Continue n (SeqParseL s)------------------------------------------------------------------------------------ Sequential applicative for backtracking folds------------------------------------------------------------------------------------ XXX Create a newtype for nonfailing parsers and downgrade the parser to that--- type before this operation and then upgrade.------ We can do an inspection testing to reject unwanted constructors at compile--- time.------ We can use the compiler to automatically annotate accumulators, terminating--- folds, non-failing parsers and failing parsers.---- | Works correctly only if both the parsers are guaranteed to never fail.-{-# INLINE noErrorUnsafeSplitWith #-}-noErrorUnsafeSplitWith :: Monad m-    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c-noErrorUnsafeSplitWith func (Parser stepL initialL extractL)-               (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    errMsg e = error $ "noErrorUnsafeSplitWith: unreachable: " ++ e--    initial = do-        resL <- initialL-        case resL of-            IPartial sl -> return $ IPartial $ SeqParseL sl-            IDone bl -> do-                resR <- initialR-                return $ bimap (SeqParseR (func bl)) (func bl) resR-            IError err -> errMsg err--    -- Note: For the composed parse to terminate, the left parser has to be-    -- a terminating parser returning a Done at some point.-    step (SeqParseL st) a = do-        r <- stepL st a-        case r of-            -- Assume that the parser can never fail, therefore, we do not-            -- need to keep the input for backtracking.-            Partial n s -> return $ Partial n (SeqParseL s)-            Continue n s -> return $ Continue n (SeqParseL s)-            Done n b -> do-                res <- initialR-                return-                    $ case res of-                          IPartial sr -> Partial n $ SeqParseR (func b) sr-                          IDone br -> Done n (func b br)-                          IError err -> errMsg err-            Error err -> errMsg err--    step (SeqParseR f st) a = fmap (bimap (SeqParseR f) f) (stepR st a)--    extract (SeqParseR f sR) = fmap (bimap (SeqParseR f) f) (extractR sR)--    extract (SeqParseL sL) = do-        rL <- extractL sL-        case rL of-            Done n bL -> do-                iR <- initialR-                case iR of-                    IPartial sR -> do-                        rR <- extractR sR-                        return-                            $ bimapOverrideCount-                                n (SeqParseR (func bL)) (func bL) rR-                    IDone bR -> return $ Done n $ func bL bR-                    IError err -> errMsg err-            Error err -> errMsg err-            Partial _ _ -> errMsg "Partial"-            Continue n s -> return $ Continue n (SeqParseL s)--{-# ANN type SeqAState Fuse #-}-data SeqAState sl sr = SeqAL !sl | SeqAR !sr---- This turns out to be slightly faster than splitWith---- | Sequential parser application ignoring the output of the first parser.--- Apply two parsers sequentially to an input stream.  The input is provided to--- the first parser, when it is done the remaining input is provided to the--- second parser. The output of the parser is the output of the second parser.--- The operation fails if any of the parsers fail.------ This implementation is strict in the second argument, therefore, the--- following will fail:------ >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]--- *** Exception: Prelude.undefined--- ...------ Although this implementation allows stream fusion, it has quadratic--- complexity, making it suitable only for a small number of compositions.--- As a thumb rule use it for less than 8 compositions, use ParserK otherwise.------ /Pre-release/----{-# INLINE split_ #-}-split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b-split_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    initial = do-        resL <- initialL-        case resL of-            IPartial sl -> return $ IPartial $ SeqAL sl-            IDone _ -> do-                resR <- initialR-                return $ first SeqAR resR-            IError err -> return $ IError err--    -- Note: For the composed parse to terminate, the left parser has to be-    -- a terminating parser returning a Done at some point.-    step (SeqAL st) a = do-        -- Important: Do not use Applicative here. Applicative somehow caused-        -- the right action to run many times, not sure why though.-        resL <- stepL st a-        case resL of-            -- Note: this leads to buffering even if we are not in an-            -- Alternative composition.-            Partial n s -> return $ Continue n (SeqAL s)-            Continue n s -> return $ Continue n (SeqAL s)-            Done n _ -> do-                initR <- initialR-                return $ case initR of-                    IPartial s -> Continue n (SeqAR s)-                    IDone b -> Done n b-                    IError err -> Error err-            Error err -> return $ Error err--    step (SeqAR st) a = first SeqAR <$> stepR st a--    extract (SeqAR sR) = fmap (first SeqAR) (extractR sR)-    extract (SeqAL sL) = do-        rL <- extractL sL-        case rL of-            Done n _ -> do-                iR <- initialR-                -- XXX For initial we can have a bimap with leftover.-                case iR of-                    IPartial sR ->-                        fmap (bimapOverrideCount n SeqAR id) (extractR sR)-                    IDone bR -> return $ Done n bR-                    IError err -> return $ Error err-            Error err -> return $ Error err-            Partial _ _ -> error "split_: Partial"-            Continue n s -> return $ Continue n (SeqAL s)---- For backtracking folds-{-# INLINE noErrorUnsafeSplit_ #-}-noErrorUnsafeSplit_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b-noErrorUnsafeSplit_-    (Parser stepL initialL extractL) (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    errMsg e = error $ "noErrorUnsafeSplit_: unreachable: " ++ e--    initial = do-        resL <- initialL-        case resL of-            IPartial sl -> return $ IPartial $ SeqAL sl-            IDone _ -> do-                resR <- initialR-                return $ first SeqAR resR-            IError err -> errMsg err--    -- Note: For the composed parse to terminate, the left parser has to be-    -- a terminating parser returning a Done at some point.-    step (SeqAL st) a = do-        -- Important: Please do not use Applicative here. Applicative somehow-        -- caused the next action to run many times in the "tar" parsing code,-        -- not sure why though.-        resL <- stepL st a-        case resL of-            Partial n s -> return $ Partial n (SeqAL s)-            Continue n s -> return $ Continue n (SeqAL s)-            Done n _ -> do-                initR <- initialR-                return $ case initR of-                    IPartial s -> Partial n (SeqAR s)-                    IDone b -> Done n b-                    IError err -> errMsg err-            Error err -> errMsg err--    step (SeqAR st) a = first SeqAR <$> stepR st a--    extract (SeqAR sR) = fmap (first SeqAR) (extractR sR)-    extract (SeqAL sL) = do-        rL <- extractL sL-        case rL of-            Done n _ -> do-                iR <- initialR-                case iR of-                    IPartial sR -> do-                        fmap (bimapOverrideCount n SeqAR id) (extractR sR)-                    IDone bR -> return $ Done n bR-                    IError err -> errMsg err-            Error err -> errMsg err-            Partial _ _ -> error "split_: Partial"-            Continue n s -> return $ Continue n (SeqAL s)---- | 'Applicative' form of 'splitWith'.-instance Monad m => Applicative (Parser a m) where-    {-# INLINE pure #-}-    pure = fromPure--    {-# INLINE (<*>) #-}-    (<*>) = splitWith id--    {-# INLINE (*>) #-}-    (*>) = split_--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)------------------------------------------------------------------------------------ Sequential Alternative----------------------------------------------------------------------------------{-# ANN type AltParseState Fuse #-}-data AltParseState sl sr = AltParseL !Int !sl | AltParseR !sr---- Note: this implementation of alt is fast because of stream fusion but has--- quadratic time complexity, because each composition adds a new branch that--- each subsequent alternative's input element has to go through, therefore, it--- cannot scale to a large number of compositions---- | Sequential alternative. The input is first passed to the first parser, and--- if it succeeds, the result is returned. However, if the first parser fails,--- the parser driver backtracks and tries the same input on the second parser,--- returning the result if it succeeds.------ Note: This implementation is not lazy in the second argument. The following--- will fail:------ >> Stream.parse (Parser.satisfy (> 0) `Parser.alt` undefined) $ Stream.fromList [1..10]--- *** Exception: Prelude.undefined------ Although this implementation allows stream fusion, it has quadratic--- complexity, making it suitable only for a small number of compositions.--- As a thumb rule use it for less than 8 compositions, use ParserK otherwise.------ /Time Complexity:/ O(n^2) where n is the number of compositions.------ /Pre-release/----{-# INLINE alt #-}-alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a-alt (Parser stepL initialL extractL) (Parser stepR initialR extractR) =-    Parser step initial extract--    where--    initial = do-        resL <- initialL-        case resL of-            IPartial sl -> return $ IPartial $ AltParseL 0 sl-            IDone bl -> return $ IDone bl-            IError _ -> do-                resR <- initialR-                return $ case resR of-                    IPartial sr -> IPartial $ AltParseR sr-                    IDone br -> IDone br-                    IError err -> IError err--    -- Once a parser yields at least one value it cannot fail.  This-    -- restriction helps us make backtracking more efficient, as we do not need-    -- to keep the consumed items buffered after a yield. Note that we do not-    -- enforce this and if a misbehaving parser does not honor this then we can-    -- get unexpected results. XXX Can we detect and flag this?-    step (AltParseL cnt st) a = do-        r <- stepL st a-        case r of-            Partial n s -> return $ Partial n (AltParseL 0 s)-            Continue n s -> do-                assertM(cnt + 1 - n >= 0)-                return $ Continue n (AltParseL (cnt + 1 - n) s)-            Done n b -> return $ Done n b-            Error _ -> do-                res <- initialR-                return-                    $ case res of-                          IPartial rR -> Continue (cnt + 1) (AltParseR rR)-                          IDone b -> Done (cnt + 1) b-                          IError err -> Error err--    step (AltParseR st) a = do-        r <- stepR st a-        return $ case r of-            Partial n s -> Partial n (AltParseR s)-            Continue n s -> Continue n (AltParseR s)-            Done n b -> Done n b-            Error err -> Error err--    extract (AltParseR sR) = fmap (first AltParseR) (extractR sR)--    extract (AltParseL cnt sL) = do-        rL <- extractL sL-        case rL of-            Done n b -> return $ Done n b-            Error _ -> do-                res <- initialR-                return-                    $ case res of-                          IPartial rR -> Continue cnt (AltParseR rR)-                          IDone b -> Done cnt b-                          IError err -> Error err-            Partial _ _ -> error "Bug: alt: extractL 'Partial'"-            Continue n s -> do-                assertM(n == cnt)-                return $ Continue n (AltParseL 0 s)--{-# ANN type Fused3 Fuse #-}-data Fused3 a b c = Fused3 !a !b !c---- | See documentation of 'Streamly.Internal.Data.Parser.many'.------ /Pre-release/----{-# INLINE splitMany #-}-splitMany :: Monad m => Parser a m b -> Fold m b c -> Parser a m c-splitMany (Parser step1 initial1 extract1) (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    -- Caution! There is mutual recursion here, inlining the right functions is-    -- important.--    handleCollect partial done fres =-        case fres of-            FL.Partial fs -> do-                pres <- initial1-                case pres of-                    IPartial ps -> return $ partial $ Fused3 ps 0 fs-                    IDone pb ->-                        runCollectorWith (handleCollect partial done) fs pb-                    IError _ -> done <$> fextract fs-            FL.Done fb -> return $ done fb--    runCollectorWith cont fs pb = fstep fs pb >>= cont--    -- See notes in Fold.many for the reason why the parser must be initialized-    -- right away instead of on first input.-    initial = finitial >>= handleCollect IPartial IDone--    {-# INLINE step #-}-    step (Fused3 st cnt fs) a = do-        r <- step1 st a-        let cnt1 = cnt + 1-        case r of-            Partial n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) fs)-            Continue n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) fs)-            Done n b -> do-                assertM(cnt1 - n >= 0)-                fstep fs b >>= handleCollect (Partial n) (Done n)-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt xs--    extract (Fused3 _ 0 fs) = fmap (Done 0) (fextract fs)-    extract (Fused3 s cnt fs) = do-        r <- extract1 s-        case r of-            Error _ -> fmap (Done cnt) (fextract fs)-            Done n b -> do-                assertM(n <= cnt)-                fs1 <- fstep fs b-                case fs1 of-                    FL.Partial s1 -> fmap (Done n) (fextract s1)-                    FL.Done b1 -> return (Done n b1)-            Partial _ _ -> error "splitMany: Partial in extract"-            Continue n s1 -> do-                assertM(n == cnt)-                return (Continue n (Fused3 s1 0 fs))---- | Like splitMany, but inner fold emits an output at the end even if no input--- is received.------ /Internal/----{-# INLINE splitManyPost #-}-splitManyPost :: Monad m =>  Parser a m b -> Fold m b c -> Parser a m c-splitManyPost (Parser step1 initial1 extract1) (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    -- Caution! There is mutual recursion here, inlining the right functions is-    -- important.--    handleCollect partial done fres =-        case fres of-            FL.Partial fs -> do-                pres <- initial1-                case pres of-                    IPartial ps -> return $ partial $ Fused3 ps 0 fs-                    IDone pb ->-                        runCollectorWith (handleCollect partial done) fs pb-                    IError _ -> done <$> fextract fs-            FL.Done fb -> return $ done fb--    runCollectorWith cont fs pb = fstep fs pb >>= cont--    initial = finitial >>= handleCollect IPartial IDone--    {-# INLINE step #-}-    step (Fused3 st cnt fs) a = do-        r <- step1 st a-        let cnt1 = cnt + 1-        case r of-            Partial n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) fs)-            Continue n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) fs)-            Done n b -> do-                assertM(cnt1 - n >= 0)-                fstep fs b >>= handleCollect (Partial n) (Done n)-            Error _ -> do-                xs <- fextract fs-                return $ Done cnt1 xs--    extract (Fused3 s cnt fs) = do-        r <- extract1 s-        case r of-            Error _ -> fmap (Done cnt) (fextract fs)-            Done n b -> do-                assertM(n <= cnt)-                fs1 <- fstep fs b-                case fs1 of-                    FL.Partial s1 -> fmap (Done n) (fextract s1)-                    FL.Done b1 -> return (Done n b1)-            Partial _ _ -> error "splitMany: Partial in extract"-            Continue n s1 -> do-                assertM(n == cnt)-                return (Continue n (Fused3 s1 0 fs))---- | See documentation of 'Streamly.Internal.Data.Parser.some'.------ /Pre-release/----{-# INLINE splitSome #-}-splitSome :: Monad m => Parser a m b -> Fold m b c -> Parser a m c-splitSome (Parser step1 initial1 extract1) (Fold fstep finitial fextract) =-    Parser step initial extract--    where--    -- Caution! There is mutual recursion here, inlining the right functions is-    -- important.--    handleCollect partial done fres =-        case fres of-            FL.Partial fs -> do-                pres <- initial1-                case pres of-                    IPartial ps -> return $ partial $ Fused3 ps 0 $ Right fs-                    IDone pb ->-                        runCollectorWith (handleCollect partial done) fs pb-                    IError _ -> done <$> fextract fs-            FL.Done fb -> return $ done fb--    runCollectorWith cont fs pb = fstep fs pb >>= cont--    initial = do-        fres <- finitial-        case fres of-            FL.Partial fs -> do-                pres <- initial1-                case pres of-                    IPartial ps -> return $ IPartial $ Fused3 ps 0 $ Left fs-                    IDone pb ->-                        runCollectorWith (handleCollect IPartial IDone) fs pb-                    IError err -> return $ IError err-            FL.Done _ ->-                return-                    $ IError-                    $ "splitSome: The collecting fold terminated without"-                          ++ " consuming any elements."--    {-# INLINE step #-}-    step (Fused3 st cnt (Left fs)) a = do-        r <- step1 st a-        -- In the Left state, count is used only for the assert-        let cnt1 = cnt + 1-        case r of-            Partial n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) (Left fs))-            Continue n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) (Left fs))-            Done n b -> do-                assertM(cnt1 - n >= 0)-                fstep fs b >>= handleCollect (Partial n) (Done n)-            Error err -> return $ Error err-    step (Fused3 st cnt (Right fs)) a = do-        r <- step1 st a-        let cnt1 = cnt + 1-        case r of-            Partial n s -> do-                assertM(cnt1 - n >= 0)-                return $ Partial n (Fused3 s (cnt1 - n) (Right fs))-            Continue n s -> do-                assertM(cnt1 - n >= 0)-                return $ Continue n (Fused3 s (cnt1 - n) (Right fs))-            Done n b -> do-                assertM(cnt1 - n >= 0)-                fstep fs b >>= handleCollect (Partial n) (Done n)-            Error _ -> Done cnt1 <$> fextract fs--    extract (Fused3 s cnt (Left fs)) = do-        r <- extract1 s-        case r of-            Error err -> return (Error err)-            Done n b -> do-                assertM(n <= cnt)-                fs1 <- fstep fs b-                case fs1 of-                    FL.Partial s1 -> fmap (Done n) (fextract s1)-                    FL.Done b1 -> return (Done n b1)-            Partial _ _ -> error "splitSome: Partial in extract"-            Continue n s1 -> do-                assertM(n == cnt)-                return (Continue n (Fused3 s1 0 (Left fs)))-    extract (Fused3 s cnt (Right fs)) = do-        r <- extract1 s-        case r of-            Error _ -> fmap (Done cnt) (fextract fs)-            Done n b -> do-                assertM(n <= cnt)-                fs1 <- fstep fs b-                case fs1 of-                    FL.Partial s1 -> fmap (Done n) (fextract s1)-                    FL.Done b1 -> return (Done n b1)-            Partial _ _ -> error "splitSome: Partial in extract"-            Continue n s1 -> do-                assertM(n == cnt)-                return (Continue n (Fused3 s1 0 (Right fs)))---- | A parser that always fails with an error message without consuming--- any input.----{-# INLINE_NORMAL die #-}-die :: Monad m => String -> Parser a m b-die err = Parser undefined (pure (IError err)) undefined---- | A parser that always fails with an effectful error message and without--- consuming any input.------ /Pre-release/----{-# INLINE dieM #-}-dieM :: Monad m => m String -> Parser a m b-dieM err = Parser undefined (IError <$> err) undefined---- Note: The default implementations of "some" and "many" loop infinitely--- because of the strict pattern match on both the arguments in applicative and--- alternative. With the direct style parser type we cannot use the mutually--- recursive definitions of "some" and "many".------ Note: With the direct style parser type, the list in "some" and "many" is--- accumulated strictly, it cannot be consumed lazily.---- | Sequential alternative. The input is first passed to the first parser, and--- if it succeeds, the result is returned. However, if the first parser fails,--- the parser driver backtracks and tries the same input on the second parser,--- returning the result if it succeeds.------ Note: The implementation of '<|>' is not lazy in the second--- argument. The following code will fail:------ >>> Stream.parse (Parser.satisfy (> 0) <|> undefined) $ Stream.fromList [1..10]--- *** Exception: Prelude.undefined--- ...------ WARNING! this is not suitable for large scale use. As a thumb rule stream--- fusion works well for less than 8 compositions of this operation, otherwise--- consider using 'ParserK'. Do not use recursive parser implementations based--- on this Alternative instance.--instance Monad m => Alternative (Parser a m) where-    {-# INLINE empty #-}-    empty = die "empty"--    {-# INLINE (<|>) #-}-    (<|>) = alt--    {-# INLINE many #-}-    many = flip splitMany toList--    {-# INLINE some #-}-    some = flip splitSome toList--{-# ANN type ConcatParseState Fuse #-}-data ConcatParseState sl m a b =-      ConcatParseL !sl-    | forall s. ConcatParseR (s -> a -> m (Step s b)) s (s -> m (Step s b))---- | Map a 'Parser' returning function on the result of a 'Parser'.------ /Pre-release/----{-# INLINE concatMap #-}-concatMap :: Monad m =>-    (b -> Parser a m c) -> Parser a m b -> Parser a m c-concatMap func (Parser stepL initialL extractL) = Parser step initial extract--    where--    {-# INLINE initializeR #-}-    initializeR (Parser stepR initialR extractR) = do-        resR <- initialR-        return $ case resR of-            IPartial sr -> IPartial $ ConcatParseR stepR sr extractR-            IDone br -> IDone br-            IError err -> IError err--    initial = do-        res <- initialL-        case res of-            IPartial s -> return $ IPartial $ ConcatParseL s-            IDone b -> initializeR (func b)-            IError err -> return $ IError err--    {-# INLINE initializeRL #-}-    initializeRL n (Parser stepR initialR extractR) = do-        resR <- initialR-        return $ case resR of-            IPartial sr -> Continue n $ ConcatParseR stepR sr extractR-            IDone br -> Done n br-            IError err -> Error err--    step (ConcatParseL st) a = do-        r <- stepL st a-        case r of-            Partial n s -> return $ Continue n (ConcatParseL s)-            Continue n s -> return $ Continue n (ConcatParseL s)-            Done n b -> initializeRL n (func b)-            Error err -> return $ Error err--    step (ConcatParseR stepR st extractR) a = do-        r <- stepR st a-        return $ case r of-            Partial n s -> Partial n $ ConcatParseR stepR s extractR-            Continue n s -> Continue n $ ConcatParseR stepR s extractR-            Done n b -> Done n b-            Error err -> Error err--    {-# INLINE extractP #-}-    extractP n (Parser stepR initialR extractR) = do-        res <- initialR-        case res of-            IPartial s ->-                fmap-                    (first (\s1 -> ConcatParseR stepR s1 extractR))-                    (extractR s)-            IDone b -> return (Done n b)-            IError err -> return $ Error err--    extract (ConcatParseR stepR s extractR) =-        fmap (first (\s1 -> ConcatParseR stepR s1 extractR)) (extractR s)-    extract (ConcatParseL sL) = do-        rL <- extractL sL-        case rL of-            Error err -> return $ Error err-            Done n b -> extractP n $ func b-            Partial _ _ -> error "concatMap: extract Partial"-            Continue n s -> return $ Continue n (ConcatParseL s)--{-# INLINE noErrorUnsafeConcatMap #-}-noErrorUnsafeConcatMap :: Monad m =>-    (b -> Parser a m c) -> Parser a m b -> Parser a m c-noErrorUnsafeConcatMap func (Parser stepL initialL extractL) =-    Parser step initial extract--    where--    {-# INLINE initializeR #-}-    initializeR (Parser stepR initialR extractR) = do-        resR <- initialR-        return $ case resR of-            IPartial sr -> IPartial $ ConcatParseR stepR sr extractR-            IDone br -> IDone br-            IError err -> IError err--    initial = do-        res <- initialL-        case res of-            IPartial s -> return $ IPartial $ ConcatParseL s-            IDone b -> initializeR (func b)-            IError err -> return $ IError err--    {-# INLINE initializeRL #-}-    initializeRL n (Parser stepR initialR extractR) = do-        resR <- initialR-        return $ case resR of-            IPartial sr -> Partial n $ ConcatParseR stepR sr extractR-            IDone br -> Done n br-            IError err -> Error err--    step (ConcatParseL st) a = do-        r <- stepL st a-        case r of-            Partial n s -> return $ Partial n (ConcatParseL s)-            Continue n s -> return $ Continue n (ConcatParseL s)-            Done n b -> initializeRL n (func b)-            Error err -> return $ Error err--    step (ConcatParseR stepR st extractR) a = do-        r <- stepR st a-        return $ case r of-            Partial n s -> Partial n $ ConcatParseR stepR s extractR-            Continue n s -> Continue n $ ConcatParseR stepR s extractR-            Done n b -> Done n b-            Error err -> Error err--    {-# INLINE extractP #-}-    extractP n (Parser stepR initialR extractR) = do-        res <- initialR-        case res of-            IPartial s ->-                fmap-                    (first (\s1 -> ConcatParseR stepR s1 extractR))-                    (extractR s)-            IDone b -> return (Done n b)-            IError err -> return $ Error err--    extract (ConcatParseR stepR s extractR) =-        fmap (first (\s1 -> ConcatParseR stepR s1 extractR)) (extractR s)-    extract (ConcatParseL sL) = do-        rL <- extractL sL-        case rL of-            Error err -> return $ Error err-            Done n b -> extractP n $ func b-            Partial _ _ -> error "concatMap: extract Partial"-            Continue n s -> return $ Continue n (ConcatParseL s)---- Note: The monad instance has quadratic performance complexity. It works fine--- for small number of compositions but for a scalable implementation we need a--- CPS version.---- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.------ Although this implementation allows stream fusion, it has quadratic--- complexity, making it suitable only for a small number of compositions. As a--- thumb rule use it for less than 8 compositions, use 'ParserK' otherwise.----instance Monad m => Monad (Parser a m) where-    {-# INLINE return #-}-    return = pure--    {-# INLINE (>>=) #-}-    (>>=) = flip concatMap--    {-# INLINE (>>) #-}-    (>>) = (*>)--instance Monad m => Fail.MonadFail (Parser a m) where-    {-# INLINE fail #-}-    fail = die--{---- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.----instance Monad m => MonadPlus (Parser a m) where-    {-# INLINE mzero #-}-    mzero = die "mzero"--    {-# INLINE mplus #-}-    mplus = alt--}--instance (Monad m, MonadIO m) => MonadIO (Parser a m) where-    {-# INLINE liftIO #-}-    liftIO = fromEffect . liftIO----------------------------------------------------------------------------------- Mapping on input----------------------------------------------------------------------------------- | @lmap f parser@ maps the function @f@ on the input of the parser.------ >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)--- Right 338350------ > lmap = Parser.lmapM return----{-# INLINE lmap #-}-lmap :: (a -> b) -> Parser b m r -> Parser a m r-lmap f (Parser step begin done) = Parser step1 begin done--    where--    step1 x a = step x (f a)---- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.----{-# INLINE lmapM #-}-lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r-lmapM f (Parser step begin done) = Parser step1 begin done--    where--    step1 x a = f a >>= step x---- | Include only those elements that pass a predicate.------ >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]--- Right 40----{-# INLINE filter #-}-filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b-filter f (Parser step initial extract) = Parser step1 initial extract--    where--    step1 x a = if f a then step x a else return $ Partial 0 x
− src/Streamly/Internal/Data/Parser/ParserK/Type.hs
@@ -1,545 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Parser.ParserK.Type--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ CPS style implementation of parsers.------ The CPS representation allows linear performance for Applicative, sequence,--- Monad, Alternative, and choice operations compared to the quadratic--- complexity of the corresponding direct style operations. However, direct--- style operations allow fusion with ~10x better performance than CPS.------ The direct style representation does not allow for recursive definitions of--- "some" and "many" whereas CPS allows that.------ 'Applicative' and 'Control.Applicative.Alternative' type class based--- combinators from the--- <http://hackage.haskell.org/package/parser-combinators parser-combinators>--- package can also be used with the 'ParserK' type.--module Streamly.Internal.Data.Parser.ParserK.Type-    (-      Step (..)-    , Input (..)-    , ParseResult (..)-    , ParserK (..)-    , fromParser-    -- , toParser-    , fromPure-    , fromEffect-    , die-    )-where--#include "ArrayMacros.h"-#include "assert.hs"-#include "inline.hs"--import Control.Applicative (Alternative(..), liftA2)-import Control.Monad (MonadPlus(..), ap)-import Control.Monad.IO.Class (MonadIO, liftIO)--- import Control.Monad.Trans.Class (MonadTrans(lift))-import Data.Proxy (Proxy(..))-import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Unboxed (peekWith, sizeOf, Unbox)-import Streamly.Internal.System.IO (unsafeInlineIO)--import qualified Control.Monad.Fail as Fail-import qualified Streamly.Internal.Data.Array.Type as Array-import qualified Streamly.Internal.Data.Parser.ParserD.Type as ParserD--data Input a = None | Chunk {-# UNPACK #-} !(Array a)---- | The intermediate result of running a parser step. The parser driver may--- stop with a final result, pause with a continuation to resume, or fail with--- an error.------ See ParserD docs. This is the same as the ParserD Step except that it uses a--- continuation in Partial and Continue constructors instead of a state in case--- of ParserD.------ /Pre-release/----data Step a m r =-    -- The Int is the current stream position index wrt to the start of the-    -- array.-      Done !Int r-      -- XXX we can use a "resume" and a "stop" continuations instead of Maybe.-      -- measure if that works any better.-      -- Array a -> m (Step a m r), m (Step a m r)-    | Partial !Int (Input a -> m (Step a m r))-    | Continue !Int (Input a -> m (Step a m r))-    | Error !Int String--instance Functor m => Functor (Step a m) where-    fmap f (Done n r) = Done n (f r)-    fmap f (Partial n k) = Partial n (fmap (fmap f) . k)-    fmap f (Continue n k) = Continue n (fmap (fmap f) . k)-    fmap _ (Error n e) = Error n e---- Note: Passing position index separately instead of passing it with the--- result causes huge regression in expression parsing becnhmarks.---- | The parser's result.------ Int is the position index into the current input array. Could be negative.--- Cannot be beyond the input array max bound.------ /Pre-release/----data ParseResult b =-      Success !Int !b      -- Position index, result-    | Failure !Int !String -- Position index, error---- | Map a function over 'Success'.-instance Functor ParseResult where-    fmap f (Success n b) = Success n (f b)-    fmap _ (Failure n e) = Failure n e---- XXX Change the type to the shape (a -> m r -> m r) -> (m r -> m r) -> m r------ The parse continuation would be: Array a -> m (Step a m r) -> m (Step a m r)--- The extract continuation would be: m (Step a m r) -> m (Step a m r)------ Use Step itself in place of ParseResult.---- | A continuation passing style parser representation. A continuation of--- 'Step's, each step passes a state and a parse result to the next 'Step'. The--- resulting 'Step' may carry a continuation that consumes input 'a' and--- results in another 'Step'. Essentially, the continuation may either consume--- input without a result or return a result with no further input to be--- consumed.----newtype ParserK a m b = MkParser-    { runParser :: forall r.-           -- Using "Input" in runParser is not necessary but it avoids making-           -- one more function call to get the input. This could be helpful-           -- for cases where we process just one element per call.-           ---           -- Do not eta reduce the applications of this continuation.-           ---           (ParseResult b -> Int -> Input a -> m (Step a m r))-           -- XXX Maintain and pass the original position in the stream. that-           -- way we can also report better errors. Use a Context structure for-           -- passing the state.--           -- Stream position index wrt to the current input array start. If-           -- negative then backtracking is required before using the array.-           -- The parser should use "Continue -n" in this case if it needs to-           -- consume input. Negative value cannot be beyond the current-           -- backtrack buffer. Positive value cannot be beyond array length.-           -- If the parser needs to advance beyond the array length it should-           -- use "Continue +n".-        -> Int-           -- used elem count, a count of elements consumed by the parser. If-           -- an Alternative fails we need to backtrack by this amount.-        -> Int-           -- The second argument is the used count as described above. The-           -- current input position is carried as part of 'Success'-           -- constructor of 'ParseResult'.-           -- XXX Use Array a, determine eof by using a nil array-        -> Input a-        -> m (Step a m r)-    }------------------------------------------------------------------------------------ Functor------------------------------------------------------------------------------------ XXX rewrite this using ParserD, expose rmapM from ParserD.--- | Maps a function over the output of the parser.----instance Functor m => Functor (ParserK a m) where-    {-# INLINE fmap #-}-    fmap f parser = MkParser $ \k n st arr ->-        let k1 res = k (fmap f res)-         in runParser parser k1 n st arr------------------------------------------------------------------------------------ Sequential applicative------------------------------------------------------------------------------------ This is the dual of stream "fromPure".------ | A parser that always yields a pure value without consuming any input.------ /Pre-release/----{-# INLINE fromPure #-}-fromPure :: b -> ParserK a m b-fromPure b = MkParser $ \k n st arr -> k (Success n b) st arr---- | See 'Streamly.Internal.Data.Parser.fromEffect'.------ /Pre-release/----{-# INLINE fromEffect #-}-fromEffect :: Monad m => m b -> ParserK a m b-fromEffect eff =-    MkParser $ \k n st arr -> eff >>= \b -> k (Success n b) st arr---- | 'Applicative' form of 'Streamly.Internal.Data.Parser.splitWith'. Note that--- this operation does not fuse, use 'Streamly.Internal.Data.Parser.splitWith'--- when fusion is important.----instance Monad m => Applicative (ParserK a m) where-    {-# INLINE pure #-}-    pure = fromPure--    {-# INLINE (<*>) #-}-    (<*>) = ap--    {-# INLINE (*>) #-}-    p1 *> p2 = MkParser $ \k n st arr ->-        let k1 (Success n1 _) s input = runParser p2 k n1 s input-            k1 (Failure n1 e) s input = k (Failure n1 e) s input-        in runParser p1 k1 n st arr--    {-# INLINE (<*) #-}-    p1 <* p2 = MkParser $ \k n st arr ->-        let k1 (Success n1 b) s1 input =-                let k2 (Success n2 _) s2 input2  = k (Success n2 b) s2 input2-                    k2 (Failure n2 e) s2 input2  = k (Failure n2 e) s2 input2-                in runParser p2 k2 n1 s1 input-            k1 (Failure n1 e) s1 input = k (Failure n1 e) s1 input-        in runParser p1 k1 n st arr--    {-# INLINE liftA2 #-}-    liftA2 f p = (<*>) (fmap f p)------------------------------------------------------------------------------------ Monad------------------------------------------------------------------------------------ This is the dual of "nil".------ | A parser that always fails with an error message without consuming--- any input.------ /Pre-release/----{-# INLINE die #-}-die :: String -> ParserK a m b-die err = MkParser (\k n st arr -> k (Failure n err) st arr)---- | Monad composition can be used for lookbehind parsers, we can make the--- future parses depend on the previously parsed values.------ If we have to parse "a9" or "9a" but not "99" or "aa" we can use the--- following parser:------ @--- backtracking :: MonadCatch m => PR.Parser Char m String--- backtracking =---     sequence [PR.satisfy isDigit, PR.satisfy isAlpha]---     '<|>'---     sequence [PR.satisfy isAlpha, PR.satisfy isDigit]--- @------ We know that if the first parse resulted in a digit at the first place then--- the second parse is going to fail.  However, we waste that information and--- parse the first character again in the second parse only to know that it is--- not an alphabetic char.  By using lookbehind in a 'Monad' composition we can--- avoid redundant work:------ @--- data DigitOrAlpha = Digit Char | Alpha Char------ lookbehind :: MonadCatch m => PR.Parser Char m String--- lookbehind = do---     x1 \<-    Digit '<$>' PR.satisfy isDigit---          '<|>' Alpha '<$>' PR.satisfy isAlpha------     -- Note: the parse depends on what we parsed already---     x2 <- case x1 of---         Digit _ -> PR.satisfy isAlpha---         Alpha _ -> PR.satisfy isDigit------     return $ case x1 of---         Digit x -> [x,x2]---         Alpha x -> [x,x2]--- @------ See also 'Streamly.Internal.Data.Parser.concatMap'. This monad instance--- does not fuse, use 'Streamly.Internal.Data.Parser.concatMap' when you need--- fusion.----instance Monad m => Monad (ParserK a m) where-    {-# INLINE return #-}-    return = pure--    {-# INLINE (>>=) #-}-    p >>= f = MkParser $ \k n st arr ->-        let k1 (Success n1 b) s1 inp = runParser (f b) k n1 s1 inp-            k1 (Failure n1 e) s1 inp = k (Failure n1 e) s1 inp-         in runParser p k1 n st arr--    {-# INLINE (>>) #-}-    (>>) = (*>)--#if !(MIN_VERSION_base(4,13,0))-    -- This is redefined instead of just being Fail.fail to be-    -- compatible with base 4.8.-    {-# INLINE fail #-}-    fail = die-#endif-instance Monad m => Fail.MonadFail (ParserK a m) where-    {-# INLINE fail #-}-    fail = die--instance MonadIO m => MonadIO (ParserK a m) where-    {-# INLINE liftIO #-}-    liftIO = fromEffect . liftIO------------------------------------------------------------------------------------ Alternative------------------------------------------------------------------------------------ | 'Alternative' form of 'Streamly.Internal.Data.Parser.alt'. Backtrack and--- run the second parser if the first one fails.------ The "some" and "many" operations of alternative accumulate results in a pure--- list which is not scalable and streaming. Instead use--- 'Streamly.Internal.Data.Parser.some' and--- 'Streamly.Internal.Data.Parser.many' for fusible operations with composable--- accumulation of results.------ See also 'Streamly.Internal.Data.Parser.alt'. This 'Alternative' instance--- does not fuse, use 'Streamly.Internal.Data.Parser.alt' when you need--- fusion.----instance Monad m => Alternative (ParserK a m) where-    {-# INLINE empty #-}-    empty = die "empty"--    {-# INLINE (<|>) #-}-    p1 <|> p2 = MkParser $ \k n _ arr ->-        let-            k1 (Failure pos _) used input = runParser p2 k (pos - used) 0 input-            k1 success _ input = k success 0 input-        in runParser p1 k1 n 0 arr--    -- some and many are implemented here instead of using default definitions-    -- so that we can use INLINE on them. It gives 50% performance improvement.--    {-# INLINE many #-}-    many v = many_v--        where--        many_v = some_v <|> pure []-        some_v = (:) <$> v <*> many_v--    {-# INLINE some #-}-    some v = some_v--        where--        many_v = some_v <|> pure []-        some_v = (:) <$> v <*> many_v---- | 'mzero' is same as 'empty', it aborts the parser. 'mplus' is same as--- '<|>', it selects the first succeeding parser.----instance Monad m => MonadPlus (ParserK a m) where-    {-# INLINE mzero #-}-    mzero = die "mzero"--    {-# INLINE mplus #-}-    mplus = (<|>)--{--instance MonadTrans (ParserK a) where-    {-# INLINE lift #-}-    lift = fromEffect--}------------------------------------------------------------------------------------ Convert ParserD to ParserK----------------------------------------------------------------------------------{-# INLINE parseDToK #-}-parseDToK-    :: forall m a s b r. (Monad m, Unbox a)-    => (s -> a -> m (ParserD.Step s b))-    -> m (ParserD.Initial s b)-    -> (s -> m (ParserD.Step s b))-    -> (ParseResult b -> Int -> Input a -> m (Step a m r))-    -> Int-    -> Int-    -> Input a-    -> m (Step a m r)-parseDToK pstep initial extract cont !offset0 !usedCount !input = do-    res <- initial-    case res of-        ParserD.IPartial pst -> do-            case input of-                Chunk arr -> parseContChunk usedCount offset0 pst arr-                None -> parseContNothing usedCount pst-        ParserD.IDone b -> cont (Success offset0 b) usedCount input-        ParserD.IError err -> cont (Failure offset0 err) usedCount input--    where--    -- XXX We can maintain an absolute position instead of relative that will-    -- help in reporting of error location in the stream.-    {-# NOINLINE parseContChunk #-}-    parseContChunk !count !offset !state arr@(Array contents start end) = do-         if offset >= 0-         then go SPEC (start + offset * SIZE_OF(a)) state-         else return $ Continue offset (parseCont count state)--        where--        {-# INLINE onDone #-}-        onDone n b =-            assert (n <= Array.length arr)-                (cont (Success n b) (count + n - offset) (Chunk arr))--        {-# INLINE callParseCont #-}-        callParseCont constr n pst1 =-            assert (n < 0 || n >= Array.length arr)-                (return $ constr n (parseCont (count + n - offset) pst1))--        {-# INLINE onPartial #-}-        onPartial = callParseCont Partial--        {-# INLINE onContinue #-}-        onContinue = callParseCont Continue--        {-# INLINE onError #-}-        onError n err =-            cont (Failure n err) (count + n - offset) (Chunk arr)--        {-# INLINE onBack #-}-        onBack offset1 elemSize constr pst = do-            let pos = offset1 - start-             in if pos >= 0-                then go SPEC offset1 pst-                else constr (pos `div` elemSize) pst--        -- Note: div may be expensive but the alternative is to maintain an element-        -- offset in addition to a byte offset or just the element offset and use-        -- multiplication to get the byte offset every time, both these options-        -- turned out to be more expensive than using div.-        go !_ !cur !pst | cur >= end =-            onContinue ((end - start) `div` SIZE_OF(a))  pst-        go !_ !cur !pst = do-            let !x = unsafeInlineIO $ peekWith contents cur-            pRes <- pstep pst x-            let elemSize = SIZE_OF(a)-                next = INDEX_NEXT(cur,a)-                back n = next - n * elemSize-                curOff = (cur - start) `div` elemSize-                nextOff = (next - start) `div` elemSize-            -- The "n" here is stream position index wrt the array start, and-            -- not the backtrack count as returned by byte stream parsers.-            case pRes of-                ParserD.Done 0 b ->-                    onDone nextOff b-                ParserD.Done 1 b ->-                    onDone curOff b-                ParserD.Done n b ->-                    onDone ((back n - start) `div` elemSize) b-                ParserD.Partial 0 pst1 ->-                    go SPEC next pst1-                ParserD.Partial 1 pst1 ->-                    go SPEC cur pst1-                ParserD.Partial n pst1 ->-                    onBack (back n) elemSize onPartial pst1-                ParserD.Continue 0 pst1 ->-                    go SPEC next pst1-                ParserD.Continue 1 pst1 ->-                    go SPEC cur pst1-                ParserD.Continue n pst1 ->-                    onBack (back n) elemSize onContinue pst1-                ParserD.Error err ->-                    onError curOff err--    {-# NOINLINE parseContNothing #-}-    parseContNothing !count !pst = do-        r <- extract pst-        case r of-            -- IMPORTANT: the n here is from the byte stream parser, that means-            -- it is the backtrack element count and not the stream position-            -- index into the current input array.-            ParserD.Done n b ->-                assert (n >= 0)-                    (cont (Success (- n) b) (count - n) None)-            ParserD.Continue n pst1 ->-                assert (n >= 0)-                    (return $ Continue (- n) (parseCont (count - n) pst1))-            ParserD.Error err ->-                -- XXX It is called only when there is no input arr. So using 0-                -- as the position is correct?-                cont (Failure 0 err) count None-            ParserD.Partial _ _ -> error "Bug: parseDToK Partial unreachable"--    -- XXX Maybe we can use two separate continuations instead of using-    -- Just/Nothing cases here. That may help in avoiding the parseContJust-    -- function call.-    {-# INLINE parseCont #-}-    parseCont !cnt !pst (Chunk arr) = parseContChunk cnt 0 pst arr-    parseCont !cnt !pst None = parseContNothing cnt pst---- | Convert a raw byte 'Parser' to a chunked 'ParserK'.------ /Pre-release/----{-# INLINE_LATE fromParser #-}-fromParser :: (Monad m, Unbox a) => ParserD.Parser a m b -> ParserK a m b-fromParser (ParserD.Parser step initial extract) =-    MkParser $ parseDToK step initial extract--{------------------------------------------------------------------------------------ Convert CPS style 'Parser' to direct style 'D.Parser'------------------------------------------------------------------------------------ | A continuation to extract the result when a CPS parser is done.-{-# INLINE parserDone #-}-parserDone :: Monad m => ParseResult b -> Int -> Input a -> m (Step a m b)-parserDone (Success n b) _ None = return $ Done n b-parserDone (Failure n e) _ None = return $ Error n e-parserDone _ _ _ = error "Bug: toParser: called with input"---- | Convert a CPS style 'ParserK' to a direct style 'ParserD.Parser'.------ /Pre-release/----{-# INLINE_LATE toParser #-}-toParser :: Monad m => ParserK a m b -> ParserD.Parser a m b-toParser parser = ParserD.Parser step initial extract--    where--    initial = pure (ParserD.IPartial (\x -> runParser parser 0 0 x parserDone))--    step cont a = do-        r <- cont (Single a)-        return $ case r of-            Done n b -> ParserD.Done n b-            Error _ e -> ParserD.Error e-            Partial n cont1 -> ParserD.Partial n cont1-            Continue n cont1 -> ParserD.Continue n cont1--    extract cont = do-        r <- cont None-        case r of-            Done n b -> return $ ParserD.Done n b-            Error _ e -> return $ ParserD.Error e-            Partial _ cont1 -> extract cont1-            Continue n cont1 -> return $ ParserD.Continue n cont1--#ifndef DISABLE_FUSION-{-# RULES "fromParser/toParser fusion" [2]-    forall s. toParser (fromParser s) = s #-}-{-# RULES "toParser/fromParser fusion" [2]-    forall s. fromParser (toParser s) = s #-}-#endif--}
+ src/Streamly/Internal/Data/Parser/Tee.hs view
@@ -0,0 +1,617 @@+{-# OPTIONS_GHC -Wno-incomplete-uni-patterns #-}++#include "inline.hs"++-- |+-- Module      : Streamly.Internal.Data.Parser.ParserD.Tee+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Parallel parsers. Distributing the input to multiple parsers at the same+-- time.+--+-- For simplicity, we are using code where a particular state is unreachable+-- but it is not prevented by types.  Somehow uni-pattern match using "let"+-- produces better optimized code compared to using @case@ match and using+-- explicit error messages in unreachable cases.+--+-- There seem to be no way to silence individual warnings so we use a global+-- incomplete uni-pattern match warning suppression option for the file.+-- Disabling the warning for other code as well  has the potential to mask off+-- some legit warnings, therefore, we have segregated only the code that uses+-- uni-pattern matches in this module.++module Streamly.Internal.Data.Parser.Tee+    (+    {-+    -- Parallel zipped+      teeWith+    , teeWithFst+    , teeWithMin++    -- Parallel alternatives+    , shortest+    , longest+    -}+    )+where++{-+import Control.Exception (assert)+import Control.Monad.Catch (MonadCatch, try)+import Prelude+       hiding (any, all, takeWhile)++import Fusion.Plugin.Types (Fuse(..))+import Streamly.Internal.Data.Parser.ParserD.Type+       (Initial(..), Parser(..), Step(..), ParseError)++-------------------------------------------------------------------------------+-- Distribute input to two parsers and collect both results+-------------------------------------------------------------------------------++-- When the input stream is distributed to two parsers, both the parsers can+-- backtrack independently. Therefore, we need separate buffer state for each+-- parser.+--+-- ParserK+--+-- We can keep the state of each parser in the zipper and pass around that+-- zipper to the parsers. Each parser can consume from the zipper and then pass+-- around the zipper to the other parser.+--+-- ParserD+--+-- In the approach we have taken here, the driver pushes one element at a time+-- to the tee and each of the parsers in the tee may buffer it independently+-- for backtracking. So they do not need to depend on the original stream+-- source for individual parser backtracking. Problem arises when both the+-- parsers backtrack and they do not need any input from the driver rather they+-- must consume from their buffers. For such situation we may need a+-- "Continue" style driver command from the tee so that the driver runs+-- the tee without providing it any input. Or we may need a local driver loop+-- until new input is to be demanded from the input stream.+--+-- When the tee errors out or stops, the tee driver may have to backtrack by+-- the specified amount (or the tee must return the leftover input). Therefore,+-- the tee driver also has to buffer, this leads to triple buffering.+--+-- When the tee stops we need to determine the backtracking amount from the+-- leftover of both the parsers. Since both the parsers may have consumed+-- different lengths of the stream we consider the maximum of the two as+-- consumed.+--+  -- XXX We can use Initial instead of StepState+{-# ANN type StepState Fuse #-}+data StepState s a = StepState s | StepResult a++-- | State of the pair of parsers in a tee composition+-- Note: strictness annotation is important for fusing the constructors+{-# ANN type TeeState Fuse #-}+data TeeState sL sR x a b =+-- @TeePair (past buffer, parser state, future-buffer1, future-buffer2) ...@+    TeePair !([x], StepState sL a, [x], [x]) !([x], StepState sR b, [x], [x])++{-# ANN type Res Fuse #-}+data Res = Yld Int | Stp Int | Skp | Err String++-- | See 'Streamly.Internal.Data.Parser.teeWith'.+--+-- /Broken/+--+{-# INLINE teeWith #-}+teeWith :: Monad m+    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+teeWith zf (Parser stepL initialL extractL) (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    {-# INLINE_LATE initial #-}+    initial = do+        resL <- initialL+        resR <- initialR+        return $ case resL of+            IPartial sl ->+                case resR of+                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])+                                                       ([], StepState sr, [], [])+                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])+                                                    ([], StepResult br, [], [])+                     IError err -> IError err+            IDone bl ->+                case resR of+                     IPartial sr ->+                         IPartial $ TeePair ([], StepResult bl, [], [])+                                            ([], StepState sr, [], [])+                     IDone br -> IDone $ zf bl br+                     IError err -> IError err+            IError err -> IError err++    {-# INLINE consume #-}+    consume buf inp1 inp2 stp st y = do+        let (x, inp11, inp21) =+                case inp1 of+                    [] -> (y, [], [])+                    z : [] -> (z, reverse (x:inp2), [])+                    z : zs -> (z, zs, x:inp2)+        r <- stp st x+        let buf1 = x:buf+        return (buf1, r, inp11, inp21)++    -- XXX This is currently broken, even though both the parsers need to+    -- consume from their buffers after backtracking the driver would still be+    -- pushing more input to the buffers.+    --+    -- consume one input item and return the next state of the fold+    {-# INLINE useStream #-}+    useStream buf inp1 inp2 stp st y = do+        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y+        case r of+            Partial 0 s ->+                let state = ([], StepState s, inp11, inp21)+                 in return (state, Yld 0)+            Partial n s ->+                let src0 = Prelude.take n buf1+                    src  = Prelude.reverse src0+                    state = ([], StepState s, src ++ inp11, inp21)+                 in assert (n <= length buf1) (return (state, Yld n))+            Done n b ->+                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)+                 in assert (n <= length buf1) (return (state, Stp n))+            -- Continue 0 s -> (buf1, Right s, inp11, inp21)+            Continue n s ->+                let (src0, buf2) = splitAt n buf1+                    src  = Prelude.reverse src0+                    state = (buf2, StepState s, src ++ inp11, inp21)+                 in assert (n <= length buf1) (return (state, Skp))+            Error err -> return (undefined, Err err)++    {-# INLINE_LATE step #-}+    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                  (bufR, StepState sR, inpR1, inpR2)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        return $ case (stL,stR) of+            (Yld n1, Yld n2) -> Partial (min n1 n2) next+            (Yld n1, Stp n2) -> Partial (min n1 n2) next+            (Stp n1, Yld n2) -> Partial (min n1 n2) next+            (Stp n1, Stp n2) ->+                -- Uni-pattern match results in better optimized code compared+                -- to a case match.+                let (_, StepResult rL, _, _) = l+                    (_, StepResult rR, _, _) = r+                 in Done (min n1 n2) (zf rL rR)+            (Err err, _) -> Error err+            (_, Err err) -> Error err+            _ -> Continue 0 next++    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                r@(_, StepResult rR, _, _)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        let next = TeePair l r+        -- XXX If the unused count of this stream is lower than the unused+        -- count of the stopped stream, only then this will be correct. We need+        -- to fix the other case. We need to keep incrementing the unused count+        -- of the stopped stream and take the min of the two.+        return $ case stL of+            Yld n -> Partial n next+            Stp n ->+                let (_, StepResult rL, _, _) = l+                 in Done n (zf rL rR)+            Skp -> Continue 0 next+            Err err -> Error err++    step (TeePair l@(_, StepResult rL, _, _)+                    (bufR, StepState sR, inpR1, inpR2)) x = do+        (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        -- XXX If the unused count of this stream is lower than the unused+        -- count of the stopped stream, only then this will be correct. We need+        -- to fix the other case. We need to keep incrementing the unused count+        -- of the stopped stream and take the min of the two.+        return $ case stR of+            Yld n -> Partial n next+            Stp n ->+                let (_, StepResult rR, _, _) = r+                 in Done n (zf rL rR)+            Skp -> Continue 0 next+            Err err -> Error err++    step _ _ = undefined++    {-# INLINE_LATE extract #-}+    extract st =+        case st of+            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do+                rL <- extractL sL+                rR <- extractR sR+                return $ zf rL rR+            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do+                rL <- extractL sL+                return $ zf rL rR+            TeePair (_, StepResult  rL, _, _) (_, StepState sR, _, _) -> do+                rR <- extractR sR+                return $ zf rL rR+            TeePair (_, StepResult rL, _, _) (_, StepResult rR, _, _) ->+                return $ zf rL rR++-- | See 'Streamly.Internal.Data.Parser.teeWithFst'.+--+-- /Broken/+--+{-# INLINE teeWithFst #-}+teeWithFst :: Monad m+    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+teeWithFst zf (Parser stepL initialL extractL)+              (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    {-# INLINE_LATE initial #-}+    initial = do+        resL <- initialL+        resR <- initialR+        case resL of+            IPartial sl ->+                return $ case resR of+                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])+                                                       ([], StepState sr, [], [])+                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])+                                                    ([], StepResult br, [], [])+                     IError err -> IError err+            IDone bl ->+                case resR of+                     IPartial sr -> IDone . zf bl <$> extractR sr+                     IDone br -> return $ IDone $ zf bl br+                     IError err -> return $ IError err+            IError err -> return $ IError err++    {-# INLINE consume #-}+    consume buf inp1 inp2 stp st y = do+        let (x, inp11, inp21) =+                case inp1 of+                    [] -> (y, [], [])+                    z : [] -> (z, reverse (x:inp2), [])+                    z : zs -> (z, zs, x:inp2)+        r <- stp st x+        let buf1 = x:buf+        return (buf1, r, inp11, inp21)++    -- consume one input item and return the next state of the fold+    {-# INLINE useStream #-}+    useStream buf inp1 inp2 stp st y = do+        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y+        case r of+            Partial 0 s ->+                let state = ([], StepState s, inp11, inp21)+                 in return (state, Yld 0)+            Partial n _ -> return (undefined, Yld n) -- Not implemented+            Done n b ->+                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)+                 in assert (n <= length buf1) (return (state, Stp n))+            -- Continue 0 s -> (buf1, Right s, inp11, inp21)+            Continue n s ->+                let (src0, buf2) = splitAt n buf1+                    src  = Prelude.reverse src0+                    state = (buf2, StepState s, src ++ inp11, inp21)+                 in assert (n <= length buf1) (return (state, Skp))+            Error err -> return (undefined, Err err)++    {-# INLINE_LATE step #-}+    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                  (bufR, StepState sR, inpR1, inpR2)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        case (stL,stR) of+            -- XXX what if the first parser returns an unused count which is+            -- more than the second parser's unused count? It does not make+            -- sense for the second parser to consume more than the first+            -- parser. We reset the input cursor based on the first parser.+            -- Error out if the second one has consumed more then the first?+            (Stp n1, Stp _) ->+                -- Uni-pattern match results in better optimized code compared+                -- to a case match.+                let (_, StepResult rL, _, _) = l+                    (_, StepResult rR, _, _) = r+                 in return $ Done n1 (zf rL rR)+            (Stp n1, Yld _) ->+                let (_, StepResult rL, _, _) = l+                    (_, StepState  ssR, _, _) = r+                 in do+                    rR <- extractR ssR+                    return $ Done n1 (zf rL rR)+            (Yld n1, Yld n2) -> return $ Partial (min n1 n2) next+            (Yld n1, Stp n2) -> return $ Partial (min n1 n2) next+            (Err err, _) -> return $ Error err+            (_, Err err) -> return $ Error err+            _ -> return $ Continue 0 next++    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                r@(_, StepResult rR, _, _)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        let next = TeePair l r+        -- XXX If the unused count of this stream is lower than the unused+        -- count of the stopped stream, only then this will be correct. We need+        -- to fix the other case. We need to keep incrementing the unused count+        -- of the stopped stream and take the min of the two.+        return $ case stL of+            Yld n -> Partial n next+            Stp n ->+                let (_, StepResult rL, _, _) = l+                 in Done n (zf rL rR)+            Skp -> Continue 0 next+            Err err -> Error err++    step _ _ = undefined++    {-# INLINE_LATE extract #-}+    extract st =+        case st of+            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do+                rL <- extractL sL+                rR <- extractR sR+                return $ zf rL rR+            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do+                rL <- extractL sL+                return $ zf rL rR+            _ -> error "unreachable"++-- | See 'Streamly.Internal.Data.Parser.teeWithMin'.+--+-- /Unimplemented/+--+{-# INLINE teeWithMin #-}+teeWithMin ::+    -- Monad m =>+    (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+teeWithMin = undefined++-------------------------------------------------------------------------------+-- Distribute input to two parsers and choose one result+-------------------------------------------------------------------------------++-- | See 'Streamly.Internal.Data.Parser.shortest'.+--+-- /Broken/+--+{-# INLINE shortest #-}+shortest :: Monad m => Parser x m a -> Parser x m a -> Parser x m a+shortest (Parser stepL initialL extractL) (Parser stepR initialR _) =+    Parser step initial extract++    where++    {-# INLINE_LATE initial #-}+    initial = do+        resL <- initialL+        resR <- initialR+        return $ case resL of+            IPartial sl ->+                case resR of+                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])+                                                       ([], StepState sr, [], [])+                     IDone br -> IDone br+                     IError err -> IError err+            IDone bl -> IDone bl+            IError errL ->+                case resR of+                     IPartial _ -> IError errL+                     IDone br -> IDone br+                     IError errR -> IError errR++    {-# INLINE consume #-}+    consume buf inp1 inp2 stp st y = do+        let (x, inp11, inp21) =+                case inp1 of+                    [] -> (y, [], [])+                    z : [] -> (z, reverse (x:inp2), [])+                    z : zs -> (z, zs, x:inp2)+        r <- stp st x+        let buf1 = x:buf+        return (buf1, r, inp11, inp21)++    -- consume one input item and return the next state of the fold+    {-# INLINE useStream #-}+    useStream buf inp1 inp2 stp st y = do+        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y+        case r of+            Partial 0 s ->+                let state = ([], StepState s, inp11, inp21)+                 in return (state, Yld 0)+            Partial n _ -> return (undefined, Yld n) -- Not implemented+            Done n b ->+                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)+                 in assert (n <= length buf1) (return (state, Stp n))+            -- Continue 0 s -> (buf1, Right s, inp11, inp21)+            Continue n s ->+                let (src0, buf2) = splitAt n buf1+                    src  = Prelude.reverse src0+                    state = (buf2, StepState s, src ++ inp11, inp21)+                 in assert (n <= length buf1) (return (state, Skp))+            Error err -> return (undefined, Err err)++    -- XXX Even if a parse finished earlier it may not be shortest if the other+    -- parser finishes later but returns a lot of unconsumed input. Our current+    -- criterion of shortest is whichever parse decided to stop earlier.+    {-# INLINE_LATE step #-}+    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                  (bufR, StepState sR, inpR1, inpR2)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        return $ case (stL,stR) of+            (Stp n1, _) ->+                let (_, StepResult rL, _, _) = l+                 in Done n1 rL+            (_, Stp n2) ->+                let (_, StepResult rR, _, _) = r+                 in Done n2 rR+            (Yld n1, Yld n2) -> Partial (min n1 n2) next+            (Err err, _) -> Error err+            (_, Err err) -> Error err+            _ -> Continue 0 next++    step _ _ = undefined++    {-# INLINE_LATE extract #-}+    extract st =+        case st of+            TeePair (_, StepState sL, _, _) _ -> extractL sL+            _ -> error "unreachable"++-- | See 'Streamly.Internal.Data.Parser.longest'.+--+-- /Broken/+--+{-# INLINE longest #-}+longest :: MonadCatch m => Parser x m a -> Parser x m a -> Parser x m a+longest (Parser stepL initialL extractL) (Parser stepR initialR extractR) =+    Parser step initial extract++    where+++    {-# INLINE_LATE initial #-}+    initial = do+        resL <- initialL+        resR <- initialR+        return $ case resL of+            IPartial sl ->+                case resR of+                     IPartial sr -> IPartial $ TeePair ([], StepState sl, [], [])+                                                       ([], StepState sr, [], [])+                     IDone br -> IPartial $ TeePair ([], StepState sl, [], [])+                                                    ([], StepResult br, [], [])+                     IError _ ->+                         IPartial $ TeePair ([], StepState sl, [], [])+                                            ([], StepResult undefined, [], [])+            IDone bl ->+                case resR of+                     IPartial sr ->+                         IPartial $ TeePair ([], StepResult bl, [], [])+                                            ([], StepState sr, [], [])+                     IDone _ -> IDone bl+                     IError _ -> IDone bl+            IError _ ->+                case resR of+                     IPartial sr ->+                         IPartial $ TeePair ([], StepResult undefined, [], [])+                                            ([], StepState sr, [], [])+                     IDone br -> IDone br+                     IError err -> IError err++    {-# INLINE consume #-}+    consume buf inp1 inp2 stp st y = do+        let (x, inp11, inp21) =+                case inp1 of+                    [] -> (y, [], [])+                    z : [] -> (z, reverse (x:inp2), [])+                    z : zs -> (z, zs, x:inp2)+        r <- stp st x+        let buf1 = x:buf+        return (buf1, r, inp11, inp21)++    -- consume one input item and return the next state of the fold+    {-# INLINE useStream #-}+    useStream buf inp1 inp2 stp st y = do+        (buf1, r, inp11, inp21) <- consume buf inp1 inp2 stp st y+        case r of+            Partial 0 s ->+                let state = ([], StepState s, inp11, inp21)+                 in return (state, Yld 0)+            Partial n _ -> return (undefined, Yld n) -- Not implemented+            Done n b ->+                let state = (Prelude.take n buf1, StepResult b, inp11, inp21)+                 in assert (n <= length buf1) (return (state, Stp n))+            -- Continue 0 s -> (buf1, Right s, inp11, inp21)+            Continue n s ->+                let (src0, buf2) = splitAt n buf1+                    src  = Prelude.reverse src0+                    state = (buf2, StepState s, src ++ inp11, inp21)+                 in assert (n <= length buf1) (return (state, Skp))+            Error err -> return (undefined, Err err)++    {-# INLINE_LATE step #-}+    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                  (bufR, StepState sR, inpR1, inpR2)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        (r,stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        return $ case (stL,stR) of+            (Yld n1, Yld n2) -> Partial (min n1 n2) next+            (Yld n1, Stp n2) -> Partial (min n1 n2) next+            (Stp n1, Yld n2) -> Partial (min n1 n2) next+            (Stp n1, Stp n2) ->+                let (_, StepResult rL, _, _) = l+                    (_, StepResult rR, _, _) = r+                 in Done (max n1 n2) (if n1 >= n2 then rL else rR)+            (Err err, _) -> Error err+            (_, Err err) -> Error err+            _ -> Continue 0 next++    -- XXX the parser that finishes last may not be the longest because it may+    -- return a lot of unused input which makes it shorter. Our current+    -- criterion of deciding longest is based on whoever decides to finish+    -- last and not whoever consumed more input.+    --+    -- To actually know who made more progress we need to keep an account of+    -- how many items are unconsumed since the last yield.+    --+    step (TeePair (bufL, StepState sL, inpL1, inpL2)+                r@(_, StepResult _, _, _)) x = do+        (l,stL) <- useStream bufL inpL1 inpL2 stepL sL x+        let next = TeePair l r+        return $ case stL of+            Yld n -> Partial n next+            Stp n ->+                let (_, StepResult rL, _, _) = l+                 in Done n rL+            Skp -> Continue 0 next+            Err err -> Error err++    step (TeePair l@(_, StepResult _, _, _)+                    (bufR, StepState sR, inpR1, inpR2)) x = do+        (r, stR) <- useStream bufR inpR1 inpR2 stepR sR x+        let next = TeePair l r+        return $ case stR of+            Yld n -> Partial n next+            Stp n ->+                let (_, StepResult rR, _, _) = r+                 in Done n rR+            Skp -> Continue 0 next+            Err err -> Error err++    step _ _ = undefined++    {-# INLINE_LATE extract #-}+    extract st =+        -- XXX When results are partial we may not be able to precisely compare+        -- which parser has made more progress till now.  One way to do that is+        -- to figure out the actually consumed input up to the last yield.+        --+        case st of+            TeePair (_, StepState sL, _, _) (_, StepState sR, _, _) -> do+                r <- try $ extractL sL+                case r of+                    Left (_ :: ParseError) -> extractR sR+                    Right b -> return b+            TeePair (_, StepState sL, _, _) (_, StepResult rR, _, _) -> do+                r <- try $ extractL sL+                case r of+                    Left (_ :: ParseError) -> return rR+                    Right b -> return b+            TeePair (_, StepResult rL, _, _) (_, StepState sR, _, _) -> do+                r <- try $ extractR sR+                case r of+                    Left (_ :: ParseError) -> return rL+                    Right b -> return b+            TeePair (_, StepResult _, _, _) (_, StepResult _, _, _) ->+                error "unreachable"+-}
+ src/Streamly/Internal/Data/Parser/Type.hs view
@@ -0,0 +1,1452 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Parser.ParserD.Type+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Streaming and backtracking parsers.+--+-- Parsers just extend folds.  Please read the 'Fold' design notes in+-- "Streamly.Internal.Data.Fold.Type" for background on the design.+--+-- = Parser Design+--+-- The 'Parser' type or a parsing fold is a generalization of the 'Fold' type.+-- The 'Fold' type /always/ succeeds on each input. Therefore, it does not need+-- to buffer the input. In contrast, a 'Parser' may fail and backtrack to+-- replay the input again to explore another branch of the parser. Therefore,+-- it needs to buffer the input. Therefore, a 'Parser' is a fold with some+-- additional requirements.  To summarize, unlike a 'Fold', a 'Parser':+--+-- 1. may not generate a new value of the accumulator on every input, it may+-- generate a new accumulator only after consuming multiple input elements+-- (e.g. takeEQ).+-- 2. on success may return some unconsumed input (e.g. takeWhile)+-- 3. may fail and return all input without consuming it (e.g. satisfy)+-- 4. backtrack and start inspecting the past input again (e.g. alt)+--+-- These use cases require buffering and replaying of input.  To facilitate+-- this, the step function of the 'Fold' is augmented to return the next state+-- of the fold along with a command tag using a 'Step' functor, the tag tells+-- the fold driver to manipulate the future input as the parser wishes. The+-- 'Step' functor provides the following commands to the fold driver+-- corresponding to the use cases outlined in the previous para:+--+-- 1. 'Continue': buffer the current input and optionally go back to a previous+--    position in the stream+-- 2. 'Partial': buffer the current input and optionally go back to a previous+--    position in the stream, drop the buffer before that position.+-- 3. 'Done': parser succeeded, returns how much input was leftover+-- 4. 'Error': indicates that the parser has failed without a result+--+-- = How a Parser Works?+--+-- A parser is just like a fold, it keeps consuming inputs from the stream and+-- accumulating them in an accumulator. The accumulator of the parser could be+-- a singleton value or it could be a collection of values e.g. a list.+--+-- The parser may build a new output value from multiple input items. When it+-- consumes an input item but needs more input to build a complete output item+-- it uses @Continue 0 s@, yielding the intermediate state @s@ and asking the+-- driver to provide more input.  When the parser determines that a new output+-- value is complete it can use a @Done n b@ to terminate the parser with @n@+-- items of input unused and the final value of the accumulator returned as+-- @b@. If at any time the parser determines that the parse has failed it can+-- return @Error err@.+--+-- A parser building a collection of values (e.g. a list) can use the @Partial@+-- constructor whenever a new item in the output collection is generated. If a+-- parser building a collection of values has yielded at least one value then+-- it is considered successful and cannot fail after that. In the current+-- implementation, this is not automatically enforced, there is a rule that the+-- parser MUST use only @Done@ for termination after the first @Partial@, it+-- cannot use @Error@. It may be possible to change the implementation so that+-- this rule is not required, but there may be some performance cost to it.+--+-- 'Streamly.Internal.Data.Parser.takeWhile' and+-- 'Streamly.Internal.Data.Parser.some' combinators are good examples of+-- efficient implementations using all features of this representation.  It is+-- possible to idiomatically build a collection of parsed items using a+-- singleton parser and @Alternative@ instance instead of using a+-- multi-yield parser.  However, this implementation is amenable to stream+-- fusion and can therefore be much faster.+--+-- = Error Handling+--+-- When a parser's @step@ function is invoked it may terminate by either a+-- 'Done' or an 'Error' return value. In an 'Alternative' composition an error+-- return can make the composed parser backtrack and try another parser.+--+-- If the stream stops before a parser could terminate then we use the+-- @extract@ function of the parser to retrieve the last yielded value of the+-- parser. If the parser has yielded at least one value then @extract@ MUST+-- return a value without throwing an error, otherwise it uses the 'ParseError'+-- exception to throw an error.+--+-- We chose the exception throwing mechanism for @extract@ instead of using an+-- explicit error return via an 'Either' type for keeping the interface simple+-- as most of the time we do not need to catch the error in intermediate+-- layers. Note that we cannot use exception throwing mechanism in @step@+-- function because of performance reasons. 'Error' constructor in that case+-- allows loop fusion and better performance.+--+-- = Optimizing backtracking+--+-- == Applicative Composition+--+-- If a parser once returned 'Partial' it can never fail after that. This is+-- used to reduce the buffering. A 'Partial' results in dropping the buffer and+-- we cannot backtrack before that point.+--+-- Parsers can be composed using an Alternative, if we are in an alternative+-- composition we may have to backtrack to try the other branch.  When we+-- compose two parsers using applicative @f <$> p1 <*> p2@ we can return a+-- 'Partial' result only after both the parsers have succeeded. While running+-- @p1@ we have to ensure that the input is not dropped until we have run @p2@,+-- therefore we have to return a Continue instead of a Partial.+--+-- However, if we know they both cannot fail then we know that the composed+-- parser can never fail.  For this reason we should have "backtracking folds"+-- as a separate type so that we can compose them in an efficient manner. In p1+-- itself we can drop the buffer as soon as a 'Partial' result arrives. In+-- fact, there is no Alternative composition for folds because they cannot+-- fail.+--+-- == Alternative Composition+--+-- In @p1 <|> p2@ as soon as the parser p1 returns 'Partial' we know that it+-- will not fail and we can immediately drop the buffer.+--+-- If we are not using the parser in an alternative composition we can+-- downgrade the parser to a backtracking fold and use the "backtracking+-- fold"'s applicative for more efficient implementation. To downgrade we can+-- translate the "Error" of parser to an exception.  This gives us best of both+-- worlds, the applicative as well as alternative would have optimal+-- backtracking buffer.+--+-- The "many" for parsers would be different than "many" for folds. In case of+-- folds an error would be propagated. In case of parsers the error would be+-- ignored.+--+-- = Implementation Approach+--+-- Backtracking folds have an issue with tee style composition because each+-- fold can backtrack independently, we will need independent buffers. Though+-- this may be possible to implement it may not be efficient especially for+-- folds that do not backtrack at all. Three types are possible, optimized for+-- different use cases:+--+-- * Non-backtracking folds: efficient Tee+-- * Backtracking folds: efficient applicative+-- * Parsers: alternative+--+-- Downgrade parsers to backtracking folds for applicative used without+-- alternative.  Upgrade backtracking folds to parsers when we have to use them+-- as the last alternative.+--+-- = Future Work+--+-- It may make sense to move "takeWhile" type of parsers, which cannot fail but+-- need some lookahead, to splitting folds.  This will allow such combinators+-- to be accepted where we need an unfailing "Fold" type.+--+-- Based on application requirements it should be possible to design even a+-- richer interface to manipulate the input stream/buffer. For example, we+-- could randomly seek into the stream in the forward or reverse directions or+-- we can even seek to the end or from the end or seek from the beginning.+--+-- We can distribute and scan/parse a stream using both folds and parsers and+-- merge the resulting streams using different merge strategies (e.g.+-- interleaving or serial).+--+-- == Naming+--+-- As far as possible, try that the names of the combinators in this module are+-- consistent with:+--+-- * <https://hackage.haskell.org/package/base/docs/Text-ParserCombinators-ReadP.html base/Text.ParserCombinators.ReadP>+-- * <http://hackage.haskell.org/package/parser-combinators parser-combinators>+-- * <http://hackage.haskell.org/package/megaparsec megaparsec>+-- * <http://hackage.haskell.org/package/attoparsec attoparsec>+-- * <http://hackage.haskell.org/package/parsec parsec>++module Streamly.Internal.Data.Parser.Type+    (+    -- * Types+      Initial (..)+    , Step (..)+    , extractStep+    , bimapOverrideCount+    , Parser (..)+    , ParseError (..)+    , rmapM++    -- * Constructors++    , fromPure+    , fromEffect+    , splitWith+    , split_++    , die+    , dieM+    , splitSome -- parseSome?+    , splitMany -- parseMany?+    , splitManyPost+    , alt+    , concatMap++    -- * Input transformation+    , lmap+    , lmapM+    , filter++    , noErrorUnsafeSplitWith+    , noErrorUnsafeSplit_+    , noErrorUnsafeConcatMap+    )+where++#include "inline.hs"+#include "assert.hs"++#if !MIN_VERSION_base(4,18,0)+import Control.Applicative (liftA2)+#endif+import Control.Applicative (Alternative(..))+import Control.Exception (Exception(..))+-- import Control.Monad (MonadPlus(..), (>=>))+import Control.Monad ((>=>))+import Control.Monad.IO.Class (MonadIO, liftIO)+import Data.Bifunctor (Bifunctor(..))+import Fusion.Plugin.Types (Fuse(..))+import Streamly.Internal.Data.Fold.Type (Fold(..), toList)++import qualified Control.Monad.Fail as Fail+import qualified Streamly.Internal.Data.Fold.Type as FL++import Prelude hiding (concatMap, filter)++#include "DocTestDataParser.hs"++-- XXX The only differences between Initial and Step types are:+--+-- * There are no backtracking counts in Initial+-- * Continue and Partial are the same. Ideally Partial should mean that an+-- empty result is valid and can be extracted; and Continue should mean that+-- empty would result in an error on extraction. We can possibly distinguish+-- the two cases.+--+-- If we ignore the backtracking counts we can represent the Initial type using+-- Step itself. That will also simplify the implementation of various parsers+-- where the processing in intiial is just a sepcial case of step, see+-- takeBetween for example.++-- | The type of a 'Parser''s initial action.+--+-- /Internal/+--+{-# ANN type Initial Fuse #-}+data Initial s b+    = IPartial !s   -- ^ Wait for step function to be called with state @s@.+    | IDone !b      -- ^ Return a result right away without an input.+    | IError !String -- ^ Return an error right away without an input.++-- | @first@ maps on 'IPartial' and @second@ maps on 'IDone'.+--+-- /Internal/+--+instance Bifunctor Initial where+    {-# INLINE bimap #-}+    bimap f _ (IPartial a) = IPartial (f a)+    bimap _ g (IDone b) = IDone (g b)+    bimap _ _ (IError err) = IError err++-- | Maps a function over the result held by 'IDone'.+--+-- >>> fmap = second+--+-- /Internal/+--+instance Functor (Initial s) where+    {-# INLINE fmap #-}+    fmap = second++-- We can simplify the Step type as follows:+--+-- Partial Int (Either s (s, b)) -- Left continue, right partial result+-- Done Int (Either String b)+--+-- In this case Error may also have a "leftover" return. This means that after+-- several successful partial results the last segment parsing failed and we+-- are returning the leftover of that. The driver may choose to restart from+-- the last segment where this parser failed or from the beginning.+--+-- Folds can only return the right values. Parsers can also return lefts.++-- | The return type of a 'Parser' step.+--+-- The parse operation feeds the input stream to the parser one element at a+-- time, representing a parse 'Step'. The parser may or may not consume the+-- item and returns a result. If the result is 'Partial' we can either extract+-- the result or feed more input to the parser. If the result is 'Continue', we+-- must feed more input in order to get a result. If the parser returns 'Done'+-- then the parser can no longer take any more input.+--+-- If the result is 'Continue', the parse operation retains the input in a+-- backtracking buffer, in case the parser may ask to backtrack in future.+-- Whenever a 'Partial n' result is returned we first backtrack by @n@ elements+-- in the input and then release any remaining backtracking buffer. Similarly,+-- 'Continue n' backtracks to @n@ elements before the current position and+-- starts feeding the input from that point for future invocations of the+-- parser.+--+-- If parser is not yet done, we can use the @extract@ operation on the @state@+-- of the parser to extract a result. If the parser has not yet yielded a+-- result, the operation fails with a 'ParseError' exception. If the parser+-- yielded a 'Partial' result in the past the last partial result is returned.+-- Therefore, if a parser yields a partial result once it cannot fail later on.+--+-- The parser can never backtrack beyond the position where the last partial+-- result left it at. The parser must ensure that the backtrack position is+-- always after that.+--+-- /Pre-release/+--+{-# ANN type Step Fuse #-}+data Step s b =+        Partial !Int !s+    -- ^ @Partial count state@. The following hold on Partial result:+    --+    -- 1. @extract@ on @state@ would succeed and give a result.+    -- 2. Input stream position is reset to @current position - count@.+    -- 3. All input before the new position is dropped. The parser can+    -- never backtrack beyond this position.++    | Continue !Int !s+    -- ^ @Continue count state@. The following hold on a Continue result:+    --+    -- 1. If there was a 'Partial' result in past, @extract@ on @state@ would+    -- give that result as 'Done' otherwise it may return 'Error' or+    -- 'Continue'.+    -- 2. Input stream position is reset to @current position - count@.+    -- 3. the input is retained in a backtrack buffer.++    | Done !Int !b+    -- ^ Done with leftover input count and result.+    --+    -- @Done count result@ means the parser has finished, it will accept no+    -- more input, last @count@ elements from the input are unused and the+    -- result of the parser is in @result@.++    | Error !String+    -- ^ Parser failed without generating any output.+    --+    -- The parsing operation may backtrack to the beginning and try another+    -- alternative.++-- | Map first function over the state and second over the result.+instance Bifunctor Step where+    {-# INLINE bimap #-}+    bimap f g step =+        case step of+            Partial n s -> Partial n (f s)+            Continue n s -> Continue n (f s)+            Done n b -> Done n (g b)+            Error err -> Error err++-- | Bimap discarding the count, and using the supplied count instead.+bimapOverrideCount :: Int -> (s -> s1) -> (b -> b1) -> Step s b -> Step s1 b1+bimapOverrideCount n f g step =+    case step of+        Partial _ s -> Partial n (f s)+        Continue _ s -> Continue n (f s)+        Done _ b -> Done n (g b)+        Error err -> Error err++-- | fmap = second+instance Functor (Step s) where+    {-# INLINE fmap #-}+    fmap = second++{-# INLINE assertStepCount #-}+assertStepCount :: Int -> Step s b -> Step s b+assertStepCount i step =+    case step of+        Partial n _ -> assert (i == n) step+        Continue n _ -> assert (i == n) step+        Done n _ -> assert (i == n) step+        Error _ -> step++-- | Map an extract function over the state of Step+--+{-# INLINE extractStep #-}+extractStep :: Monad m => (s -> m (Step s1 b)) -> Step s b -> m (Step s1 b)+extractStep f res =+    case res of+        Partial n s1 -> assertStepCount n <$> f s1+        Done n b -> return $ Done n b+        Continue n s1 -> assertStepCount n <$> f s1+        Error err -> return $ Error err++-- | Map a monadic function over the result @b@ in @Step s b@.+--+-- /Internal/+{-# INLINE mapMStep #-}+mapMStep :: Applicative m => (a -> m b) -> Step s a -> m (Step s b)+mapMStep f res =+    case res of+        Partial n s -> pure $ Partial n s+        Done n b -> Done n <$> f b+        Continue n s -> pure $ Continue n s+        Error err -> pure $ Error err++-- | A parser is a fold that can fail and is represented as @Parser step+-- initial extract@. Before we drive a parser we call the @initial@ action to+-- retrieve the initial state of the fold. The parser driver invokes @step@+-- with the state returned by the previous step and the next input element. It+-- results into a new state and a command to the driver represented by 'Step'+-- type. The driver keeps invoking the step function until it stops or fails.+-- At any point of time the driver can call @extract@ to inspect the result of+-- the fold. If the parser hits the end of input 'extract' is called.+-- It may result in an error or an output value.+--+-- /Pre-release/+--+data Parser a m b =+    forall s. Parser+        (s -> a -> m (Step s b))+        -- Initial cannot return "Partial/Done n" or "Continue". Continue 0 is+        -- same as Partial 0. In other words it cannot backtrack.+        (m (Initial s b))+        -- Extract can only return Partial or Continue n. In other words it can+        -- only backtrack or return partial result/error. But we do not return+        -- result in Partial, therefore, we have to use Done instead of Partial.+        (s -> m (Step s b))++-- | This exception is used when a parser ultimately fails, the user of the+-- parser is intimated via this exception.+--+-- /Pre-release/+--+newtype ParseError = ParseError String+    deriving Show++instance Exception ParseError where+    displayException (ParseError err) = err++-- | Map a function on the result i.e. on @b@ in @Parser a m b@.+instance Functor m => Functor (Parser a m) where+    {-# INLINE fmap #-}+    fmap f (Parser step1 initial1 extract) =+        Parser step initial (fmap3 f extract)++        where++        initial = fmap2 f initial1+        step s b = fmap2 f (step1 s b)+        fmap2 g = fmap (fmap g)+        fmap3 g = fmap2 (fmap g)++------------------------------------------------------------------------------+-- Mapping on the output+------------------------------------------------------------------------------++-- | @rmapM f parser@ maps the monadic function @f@ on the output of the parser.+--+-- >>> rmap = fmap+{-# INLINE rmapM #-}+rmapM :: Monad m => (b -> m c) -> Parser a m b -> Parser a m c+rmapM f (Parser step initial extract) =+    Parser step1 initial1 (extract >=> mapMStep f)++    where++    initial1 = do+        res <- initial+        -- this is mapM f over result+        case res of+            IPartial x -> return $ IPartial x+            IDone a -> IDone <$> f a+            IError err -> return $ IError err+    step1 s a = step s a >>= mapMStep f++-- | A parser that always yields a pure value without consuming any input.+--+{-# INLINE_NORMAL fromPure #-}+fromPure :: Monad m => b -> Parser a m b+fromPure b = Parser undefined (pure $ IDone b) undefined++-- | A parser that always yields the result of an effectful action without+-- consuming any input.+--+{-# INLINE fromEffect #-}+fromEffect :: Monad m => m b -> Parser a m b+fromEffect b = Parser undefined (IDone <$> b) undefined++-------------------------------------------------------------------------------+-- Sequential applicative+-------------------------------------------------------------------------------++{-# ANN type SeqParseState Fuse #-}+data SeqParseState sl f sr = SeqParseL !sl | SeqParseR !f !sr++-- Note: this implementation of splitWith is fast because of stream fusion but+-- has quadratic time complexity, because each composition adds a new branch+-- that each subsequent parse's input element has to go through, therefore, it+-- cannot scale to a large number of compositions. After around 100+-- compositions the performance starts dipping rapidly beyond a CPS style+-- unfused implementation.+--+-- Note: This is a parsing dual of appending streams using+-- 'Streamly.Data.Stream.append', it splits the streams using two parsers and+-- zips the results.++-- | Sequential parser application. Apply two parsers sequentially to an input+-- stream. The first parser runs and processes the input, the remaining input+-- is then passed to the second parser. If both parsers succeed, their outputs+-- are combined using the supplied function. If either parser fails, the+-- operation fails.+--+-- This combinator delivers high performance by stream fusion but it comes with+-- some limitations. For those cases use the 'Applicative' instance of+-- 'Streamly.Data.ParserK.ParserK'.+--+-- CAVEAT 1: NO RECURSION. This function is strict in both arguments. As a+-- result, if a parser is defined recursively using this, it may cause an+-- infintie loop. The following example checks the strictness:+--+-- >>> p = Parser.splitWith const (Parser.satisfy (> 0)) undefined+-- >>> Stream.parse p $ Stream.fromList [1]+-- *** Exception: Prelude.undefined+-- ...+--+-- CAVEAT 2: QUADRATIC TIME COMPLEXITY. Static composition is fast due to+-- stream fusion, but it works well only for limited (e.g. up to 8)+-- compositions, use "Streamly.Data.ParserK" for larger compositions.+--+-- Below are some common idioms that can be expressed using 'splitWith':+--+-- >>> span p f1 f2 = Parser.splitWith (,) (Parser.takeWhile p f1) (Parser.fromFold f2)+-- >>> spanBy eq f1 f2 = Parser.splitWith (,) (Parser.groupBy eq f1) (Parser.fromFold f2)+--+-- /Pre-release/+--+{-# INLINE splitWith #-}+splitWith :: Monad m+    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+splitWith func (Parser stepL initialL extractL)+               (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    initial = do+        -- XXX We can use bimap here if we make this a Step type+        resL <- initialL+        case resL of+            IPartial sl -> return $ IPartial $ SeqParseL sl+            IDone bl -> do+                resR <- initialR+                -- XXX We can use bimap here if we make this a Step type+                return $ case resR of+                    IPartial sr -> IPartial $ SeqParseR (func bl) sr+                    IDone br -> IDone (func bl br)+                    IError err -> IError err+            IError err -> return $ IError err++    -- Note: For the composed parse to terminate, the left parser has to be+    -- a terminating parser returning a Done at some point.+    step (SeqParseL st) a = do+        -- Important: Please do not use Applicative here. See+        -- https://github.com/composewell/streamly/issues/1033 and the problem+        -- defined in split_ for more info.+        -- XXX Use bimap+        resL <- stepL st a+        case resL of+            -- Note: We need to buffer the input for a possible Alternative+            -- e.g. in ((,) <$> p1 <*> p2) <|> p3, if p2 fails we have to+            -- backtrack and start running p3. So we need to keep the input+            -- buffered until we know that the applicative cannot fail.+            Partial n s -> return $ Continue n (SeqParseL s)+            Continue n s -> return $ Continue n (SeqParseL s)+            Done n b -> do+                -- XXX Use bimap if we make this a Step type+                -- fmap (bimap (SeqParseR (func b)) (func b)) initialR+                initR <- initialR+                return $ case initR of+                   IPartial sr -> Continue n $ SeqParseR (func b) sr+                   IDone br -> Done n (func b br)+                   IError err -> Error err+            Error err -> return $ Error err++    step (SeqParseR f st) a = fmap (bimap (SeqParseR f) f) (stepR st a)++    extract (SeqParseR f sR) = fmap (bimap (SeqParseR f) f) (extractR sR)+    extract (SeqParseL sL) = do+        -- XXX Use bimap here+        rL <- extractL sL+        case rL of+            Done n bL -> do+                -- XXX Use bimap here if we use Step type in Initial+                iR <- initialR+                case iR of+                    IPartial sR -> do+                        fmap+                            (bimap (SeqParseR (func bL)) (func bL))+                            (extractR sR)+                    IDone bR -> return $ Done n $ func bL bR+                    IError err -> return $ Error err+            Error err -> return $ Error err+            Partial _ _ -> error "Bug: splitWith extract 'Partial'"+            Continue n s -> return $ Continue n (SeqParseL s)++-------------------------------------------------------------------------------+-- Sequential applicative for backtracking folds+-------------------------------------------------------------------------------++-- XXX Create a newtype for nonfailing parsers and downgrade the parser to that+-- type before this operation and then upgrade.+--+-- We can do an inspection testing to reject unwanted constructors at compile+-- time.+--+-- We can use the compiler to automatically annotate accumulators, terminating+-- folds, non-failing parsers and failing parsers.++-- | Better performance 'splitWith' for non-failing parsers.+--+-- Does not work correctly for parsers that can fail.+--+-- ALL THE CAVEATS IN 'splitWith' APPLY HERE AS WELL.+--+{-# INLINE noErrorUnsafeSplitWith #-}+noErrorUnsafeSplitWith :: Monad m+    => (a -> b -> c) -> Parser x m a -> Parser x m b -> Parser x m c+noErrorUnsafeSplitWith func (Parser stepL initialL extractL)+               (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    errMsg e = error $ "noErrorUnsafeSplitWith: unreachable: " ++ e++    initial = do+        resL <- initialL+        case resL of+            IPartial sl -> return $ IPartial $ SeqParseL sl+            IDone bl -> do+                resR <- initialR+                return $ bimap (SeqParseR (func bl)) (func bl) resR+            IError err -> errMsg err++    -- Note: For the composed parse to terminate, the left parser has to be+    -- a terminating parser returning a Done at some point.+    step (SeqParseL st) a = do+        r <- stepL st a+        case r of+            -- Assume that the parser can never fail, therefore, we do not+            -- need to keep the input for backtracking.+            Partial n s -> return $ Partial n (SeqParseL s)+            Continue n s -> return $ Continue n (SeqParseL s)+            Done n b -> do+                res <- initialR+                return+                    $ case res of+                          IPartial sr -> Partial n $ SeqParseR (func b) sr+                          IDone br -> Done n (func b br)+                          IError err -> errMsg err+            Error err -> errMsg err++    step (SeqParseR f st) a = fmap (bimap (SeqParseR f) f) (stepR st a)++    extract (SeqParseR f sR) = fmap (bimap (SeqParseR f) f) (extractR sR)++    extract (SeqParseL sL) = do+        rL <- extractL sL+        case rL of+            Done n bL -> do+                iR <- initialR+                case iR of+                    IPartial sR -> do+                        rR <- extractR sR+                        return+                            $ bimapOverrideCount+                                n (SeqParseR (func bL)) (func bL) rR+                    IDone bR -> return $ Done n $ func bL bR+                    IError err -> errMsg err+            Error err -> errMsg err+            Partial _ _ -> errMsg "Partial"+            Continue n s -> return $ Continue n (SeqParseL s)++{-# ANN type SeqAState Fuse #-}+data SeqAState sl sr = SeqAL !sl | SeqAR !sr++-- This turns out to be slightly faster than splitWith++-- | Sequential parser application ignoring the output of the first parser.+-- Apply two parsers sequentially to an input stream.  The input is provided to+-- the first parser, when it is done the remaining input is provided to the+-- second parser. The output of the parser is the output of the second parser.+-- The operation fails if any of the parsers fail.+--+-- ALL THE CAVEATS IN 'splitWith' APPLY HERE AS WELL.+--+-- This implementation is strict in the second argument, therefore, the+-- following will fail:+--+-- >>> Stream.parse (Parser.split_ (Parser.satisfy (> 0)) undefined) $ Stream.fromList [1]+-- *** Exception: Prelude.undefined+-- ...+--+-- /Pre-release/+--+{-# INLINE split_ #-}+split_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b+split_ (Parser stepL initialL extractL) (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    initial = do+        resL <- initialL+        case resL of+            IPartial sl -> return $ IPartial $ SeqAL sl+            IDone _ -> do+                resR <- initialR+                return $ first SeqAR resR+            IError err -> return $ IError err++    -- Note: For the composed parse to terminate, the left parser has to be+    -- a terminating parser returning a Done at some point.+    step (SeqAL st) a = do+        -- Important: Do not use Applicative here. Applicative somehow caused+        -- the right action to run many times, not sure why though.+        resL <- stepL st a+        case resL of+            -- Note: this leads to buffering even if we are not in an+            -- Alternative composition.+            Partial n s -> return $ Continue n (SeqAL s)+            Continue n s -> return $ Continue n (SeqAL s)+            Done n _ -> do+                initR <- initialR+                return $ case initR of+                    IPartial s -> Continue n (SeqAR s)+                    IDone b -> Done n b+                    IError err -> Error err+            Error err -> return $ Error err++    step (SeqAR st) a = first SeqAR <$> stepR st a++    extract (SeqAR sR) = fmap (first SeqAR) (extractR sR)+    extract (SeqAL sL) = do+        rL <- extractL sL+        case rL of+            Done n _ -> do+                iR <- initialR+                -- XXX For initial we can have a bimap with leftover.+                case iR of+                    IPartial sR ->+                        fmap (bimapOverrideCount n SeqAR id) (extractR sR)+                    IDone bR -> return $ Done n bR+                    IError err -> return $ Error err+            Error err -> return $ Error err+            Partial _ _ -> error "split_: Partial"+            Continue n s -> return $ Continue n (SeqAL s)++-- | Better performance 'split_' for non-failing parsers.+--+-- Does not work correctly for parsers that can fail.+--+-- ALL THE CAVEATS IN 'splitWith' APPLY HERE AS WELL.+--+{-# INLINE noErrorUnsafeSplit_ #-}+noErrorUnsafeSplit_ :: Monad m => Parser x m a -> Parser x m b -> Parser x m b+noErrorUnsafeSplit_+    (Parser stepL initialL extractL) (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    errMsg e = error $ "noErrorUnsafeSplit_: unreachable: " ++ e++    initial = do+        resL <- initialL+        case resL of+            IPartial sl -> return $ IPartial $ SeqAL sl+            IDone _ -> do+                resR <- initialR+                return $ first SeqAR resR+            IError err -> errMsg err++    -- Note: For the composed parse to terminate, the left parser has to be+    -- a terminating parser returning a Done at some point.+    step (SeqAL st) a = do+        -- Important: Please do not use Applicative here. Applicative somehow+        -- caused the next action to run many times in the "tar" parsing code,+        -- not sure why though.+        resL <- stepL st a+        case resL of+            Partial n s -> return $ Partial n (SeqAL s)+            Continue n s -> return $ Continue n (SeqAL s)+            Done n _ -> do+                initR <- initialR+                return $ case initR of+                    IPartial s -> Partial n (SeqAR s)+                    IDone b -> Done n b+                    IError err -> errMsg err+            Error err -> errMsg err++    step (SeqAR st) a = first SeqAR <$> stepR st a++    extract (SeqAR sR) = fmap (first SeqAR) (extractR sR)+    extract (SeqAL sL) = do+        rL <- extractL sL+        case rL of+            Done n _ -> do+                iR <- initialR+                case iR of+                    IPartial sR -> do+                        fmap (bimapOverrideCount n SeqAR id) (extractR sR)+                    IDone bR -> return $ Done n bR+                    IError err -> errMsg err+            Error err -> errMsg err+            Partial _ _ -> error "split_: Partial"+            Continue n s -> return $ Continue n (SeqAL s)++-- | READ THE CAVEATS in 'splitWith' before using this instance.+--+-- >>> pure = Parser.fromPure+-- >>> (<*>) = Parser.splitWith id+-- >>> (*>) = Parser.split_+instance Monad m => Applicative (Parser a m) where+    {-# INLINE pure #-}+    pure = fromPure++    {-# INLINE (<*>) #-}+    (<*>) = splitWith id++    {-# INLINE (*>) #-}+    (*>) = split_++    {-# INLINE liftA2 #-}+    liftA2 f x = (<*>) (fmap f x)++-------------------------------------------------------------------------------+-- Sequential Alternative+-------------------------------------------------------------------------------++{-# ANN type AltParseState Fuse #-}+data AltParseState sl sr = AltParseL !Int !sl | AltParseR !sr++-- Note: this implementation of alt is fast because of stream fusion but has+-- quadratic time complexity, because each composition adds a new branch that+-- each subsequent alternative's input element has to go through, therefore, it+-- cannot scale to a large number of compositions++-- | Sequential alternative. The input is first passed to the first parser,+-- if it succeeds, the result is returned. However, if the first parser fails,+-- the parser driver backtracks and tries the same input on the second+-- (alternative) parser, returning the result if it succeeds.+--+-- This combinator delivers high performance by stream fusion but it comes with+-- some limitations. For those cases use the 'Alternative' instance of+-- 'Streamly.Data.ParserK.ParserK'.+--+-- CAVEAT 1: NO RECURSION. This function is strict in both arguments. As a+-- result, if a parser is defined recursively using this, it may cause an+-- infintie loop. The following example checks the strictness:+--+-- >>> p = Parser.satisfy (> 0) `Parser.alt` undefined+-- >>> Stream.parse p $ Stream.fromList [1..10]+-- *** Exception: Prelude.undefined+--+-- CAVEAT 2: QUADRATIC TIME COMPLEXITY. Static composition is fast due to+-- stream fusion, but it works well only for limited (e.g. up to 8)+-- compositions, use "Streamly.Data.ParserK" for larger compositions.+--+-- /Time Complexity:/ O(n^2) where n is the number of compositions.+--+-- /Pre-release/+--+{-# INLINE alt #-}+alt :: Monad m => Parser x m a -> Parser x m a -> Parser x m a+alt (Parser stepL initialL extractL) (Parser stepR initialR extractR) =+    Parser step initial extract++    where++    initial = do+        resL <- initialL+        case resL of+            IPartial sl -> return $ IPartial $ AltParseL 0 sl+            IDone bl -> return $ IDone bl+            IError _ -> do+                resR <- initialR+                return $ case resR of+                    IPartial sr -> IPartial $ AltParseR sr+                    IDone br -> IDone br+                    IError err -> IError err++    -- Once a parser yields at least one value it cannot fail.  This+    -- restriction helps us make backtracking more efficient, as we do not need+    -- to keep the consumed items buffered after a yield. Note that we do not+    -- enforce this and if a misbehaving parser does not honor this then we can+    -- get unexpected results. XXX Can we detect and flag this?+    step (AltParseL cnt st) a = do+        r <- stepL st a+        case r of+            Partial n s -> return $ Partial n (AltParseL 0 s)+            Continue n s -> do+                assertM(cnt + 1 - n >= 0)+                return $ Continue n (AltParseL (cnt + 1 - n) s)+            Done n b -> return $ Done n b+            Error _ -> do+                res <- initialR+                return+                    $ case res of+                          IPartial rR -> Continue (cnt + 1) (AltParseR rR)+                          IDone b -> Done (cnt + 1) b+                          IError err -> Error err++    step (AltParseR st) a = do+        r <- stepR st a+        return $ case r of+            Partial n s -> Partial n (AltParseR s)+            Continue n s -> Continue n (AltParseR s)+            Done n b -> Done n b+            Error err -> Error err++    extract (AltParseR sR) = fmap (first AltParseR) (extractR sR)++    extract (AltParseL cnt sL) = do+        rL <- extractL sL+        case rL of+            Done n b -> return $ Done n b+            Error _ -> do+                res <- initialR+                return+                    $ case res of+                          IPartial rR -> Continue cnt (AltParseR rR)+                          IDone b -> Done cnt b+                          IError err -> Error err+            Partial _ _ -> error "Bug: alt: extractL 'Partial'"+            Continue n s -> do+                assertM(n == cnt)+                return $ Continue n (AltParseL 0 s)++{-# ANN type Fused3 Fuse #-}+data Fused3 a b c = Fused3 !a !b !c++-- | See documentation of 'Streamly.Internal.Data.Parser.many'.+--+-- /Pre-release/+--+{-# INLINE splitMany #-}+splitMany :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+splitMany (Parser step1 initial1 extract1) (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    -- Caution! There is mutual recursion here, inlining the right functions is+    -- important.++    handleCollect partial done fres =+        case fres of+            FL.Partial fs -> do+                pres <- initial1+                case pres of+                    IPartial ps -> return $ partial $ Fused3 ps 0 fs+                    IDone pb ->+                        runCollectorWith (handleCollect partial done) fs pb+                    IError _ -> done <$> ffinal fs+            FL.Done fb -> return $ done fb++    runCollectorWith cont fs pb = fstep fs pb >>= cont++    -- See notes in Fold.many for the reason why the parser must be initialized+    -- right away instead of on first input.+    initial = finitial >>= handleCollect IPartial IDone++    {-# INLINE step #-}+    step (Fused3 st cnt fs) a = do+        r <- step1 st a+        let cnt1 = cnt + 1+        case r of+            Partial n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) fs)+            Continue n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) fs)+            Done n b -> do+                assertM(cnt1 - n >= 0)+                fstep fs b >>= handleCollect (Partial n) (Done n)+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt xs++    extract (Fused3 _ 0 fs) = fmap (Done 0) (ffinal fs)+    extract (Fused3 s cnt fs) = do+        r <- extract1 s+        case r of+            Error _ -> fmap (Done cnt) (ffinal fs)+            Done n b -> do+                assertM(n <= cnt)+                fs1 <- fstep fs b+                case fs1 of+                    FL.Partial s1 -> fmap (Done n) (ffinal s1)+                    FL.Done b1 -> return (Done n b1)+            Partial _ _ -> error "splitMany: Partial in extract"+            Continue n s1 -> do+                assertM(n == cnt)+                return (Continue n (Fused3 s1 0 fs))++-- | Like splitMany, but inner fold emits an output at the end even if no input+-- is received.+--+-- /Internal/+--+{-# INLINE splitManyPost #-}+splitManyPost :: Monad m =>  Parser a m b -> Fold m b c -> Parser a m c+splitManyPost (Parser step1 initial1 extract1) (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    -- Caution! There is mutual recursion here, inlining the right functions is+    -- important.++    handleCollect partial done fres =+        case fres of+            FL.Partial fs -> do+                pres <- initial1+                case pres of+                    IPartial ps -> return $ partial $ Fused3 ps 0 fs+                    IDone pb ->+                        runCollectorWith (handleCollect partial done) fs pb+                    IError _ -> done <$> ffinal fs+            FL.Done fb -> return $ done fb++    runCollectorWith cont fs pb = fstep fs pb >>= cont++    initial = finitial >>= handleCollect IPartial IDone++    {-# INLINE step #-}+    step (Fused3 st cnt fs) a = do+        r <- step1 st a+        let cnt1 = cnt + 1+        case r of+            Partial n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) fs)+            Continue n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) fs)+            Done n b -> do+                assertM(cnt1 - n >= 0)+                fstep fs b >>= handleCollect (Partial n) (Done n)+            Error _ -> do+                xs <- ffinal fs+                return $ Done cnt1 xs++    extract (Fused3 s cnt fs) = do+        r <- extract1 s+        case r of+            Error _ -> fmap (Done cnt) (ffinal fs)+            Done n b -> do+                assertM(n <= cnt)+                fs1 <- fstep fs b+                case fs1 of+                    FL.Partial s1 -> fmap (Done n) (ffinal s1)+                    FL.Done b1 -> return (Done n b1)+            Partial _ _ -> error "splitMany: Partial in extract"+            Continue n s1 -> do+                assertM(n == cnt)+                return (Continue n (Fused3 s1 0 fs))++-- | See documentation of 'Streamly.Internal.Data.Parser.some'.+--+-- /Pre-release/+--+{-# INLINE splitSome #-}+splitSome :: Monad m => Parser a m b -> Fold m b c -> Parser a m c+splitSome (Parser step1 initial1 extract1) (Fold fstep finitial _ ffinal) =+    Parser step initial extract++    where++    -- Caution! There is mutual recursion here, inlining the right functions is+    -- important.++    handleCollect partial done fres =+        case fres of+            FL.Partial fs -> do+                pres <- initial1+                case pres of+                    IPartial ps -> return $ partial $ Fused3 ps 0 $ Right fs+                    IDone pb ->+                        runCollectorWith (handleCollect partial done) fs pb+                    IError _ -> done <$> ffinal fs+            FL.Done fb -> return $ done fb++    runCollectorWith cont fs pb = fstep fs pb >>= cont++    initial = do+        fres <- finitial+        case fres of+            FL.Partial fs -> do+                pres <- initial1+                case pres of+                    IPartial ps -> return $ IPartial $ Fused3 ps 0 $ Left fs+                    IDone pb ->+                        runCollectorWith (handleCollect IPartial IDone) fs pb+                    IError err -> return $ IError err+            FL.Done _ ->+                return+                    $ IError+                    $ "splitSome: The collecting fold terminated without"+                          ++ " consuming any elements."++    {-# INLINE step #-}+    step (Fused3 st cnt (Left fs)) a = do+        r <- step1 st a+        -- In the Left state, count is used only for the assert+        let cnt1 = cnt + 1+        case r of+            Partial n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) (Left fs))+            Continue n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) (Left fs))+            Done n b -> do+                assertM(cnt1 - n >= 0)+                fstep fs b >>= handleCollect (Partial n) (Done n)+            Error err -> return $ Error err+    step (Fused3 st cnt (Right fs)) a = do+        r <- step1 st a+        let cnt1 = cnt + 1+        case r of+            Partial n s -> do+                assertM(cnt1 - n >= 0)+                return $ Partial n (Fused3 s (cnt1 - n) (Right fs))+            Continue n s -> do+                assertM(cnt1 - n >= 0)+                return $ Continue n (Fused3 s (cnt1 - n) (Right fs))+            Done n b -> do+                assertM(cnt1 - n >= 0)+                fstep fs b >>= handleCollect (Partial n) (Done n)+            Error _ -> Done cnt1 <$> ffinal fs++    extract (Fused3 s cnt (Left fs)) = do+        r <- extract1 s+        case r of+            Error err -> return (Error err)+            Done n b -> do+                assertM(n <= cnt)+                fs1 <- fstep fs b+                case fs1 of+                    FL.Partial s1 -> fmap (Done n) (ffinal s1)+                    FL.Done b1 -> return (Done n b1)+            Partial _ _ -> error "splitSome: Partial in extract"+            Continue n s1 -> do+                assertM(n == cnt)+                return (Continue n (Fused3 s1 0 (Left fs)))+    extract (Fused3 s cnt (Right fs)) = do+        r <- extract1 s+        case r of+            Error _ -> fmap (Done cnt) (ffinal fs)+            Done n b -> do+                assertM(n <= cnt)+                fs1 <- fstep fs b+                case fs1 of+                    FL.Partial s1 -> fmap (Done n) (ffinal s1)+                    FL.Done b1 -> return (Done n b1)+            Partial _ _ -> error "splitSome: Partial in extract"+            Continue n s1 -> do+                assertM(n == cnt)+                return (Continue n (Fused3 s1 0 (Right fs)))++-- | A parser that always fails with an error message without consuming+-- any input.+--+{-# INLINE_NORMAL die #-}+die :: Monad m => String -> Parser a m b+die err = Parser undefined (pure (IError err)) undefined++-- | A parser that always fails with an effectful error message and without+-- consuming any input.+--+-- /Pre-release/+--+{-# INLINE dieM #-}+dieM :: Monad m => m String -> Parser a m b+dieM err = Parser undefined (IError <$> err) undefined++-- Note: The default implementations of "some" and "many" loop infinitely+-- because of the strict pattern match on both the arguments in applicative and+-- alternative. With the direct style parser type we cannot use the mutually+-- recursive definitions of "some" and "many".+--+-- Note: With the direct style parser type, the list in "some" and "many" is+-- accumulated strictly, it cannot be consumed lazily.++-- | READ THE CAVEATS in 'alt' before using this instance.+--+-- >>> empty = Parser.die "empty"+-- >>> (<|>) = Parser.alt+-- >>> many = flip Parser.many Fold.toList+-- >>> some = flip Parser.some Fold.toList+instance Monad m => Alternative (Parser a m) where+    {-# INLINE empty #-}+    empty = die "empty"++    {-# INLINE (<|>) #-}+    (<|>) = alt++    {-# INLINE many #-}+    many = flip splitMany toList++    {-# INLINE some #-}+    some = flip splitSome toList++{-# ANN type ConcatParseState Fuse #-}+data ConcatParseState sl m a b =+      ConcatParseL !sl+    | forall s. ConcatParseR (s -> a -> m (Step s b)) s (s -> m (Step s b))++-- XXX Does it fuse completely? Check and update, it cannot fuse the+-- dynamically generated parser.++-- | Map a 'Parser' returning function on the result of a 'Parser'.+--+-- ALL THE CAVEATS IN 'splitWith' APPLY HERE AS WELL.+--+-- /Pre-release/+--+{-# INLINE concatMap #-}+concatMap :: Monad m =>+    (b -> Parser a m c) -> Parser a m b -> Parser a m c+concatMap func (Parser stepL initialL extractL) = Parser step initial extract++    where++    {-# INLINE initializeR #-}+    initializeR (Parser stepR initialR extractR) = do+        resR <- initialR+        return $ case resR of+            IPartial sr -> IPartial $ ConcatParseR stepR sr extractR+            IDone br -> IDone br+            IError err -> IError err++    initial = do+        res <- initialL+        case res of+            IPartial s -> return $ IPartial $ ConcatParseL s+            IDone b -> initializeR (func b)+            IError err -> return $ IError err++    {-# INLINE initializeRL #-}+    initializeRL n (Parser stepR initialR extractR) = do+        resR <- initialR+        return $ case resR of+            IPartial sr -> Continue n $ ConcatParseR stepR sr extractR+            IDone br -> Done n br+            IError err -> Error err++    step (ConcatParseL st) a = do+        r <- stepL st a+        case r of+            Partial n s -> return $ Continue n (ConcatParseL s)+            Continue n s -> return $ Continue n (ConcatParseL s)+            Done n b -> initializeRL n (func b)+            Error err -> return $ Error err++    step (ConcatParseR stepR st extractR) a = do+        r <- stepR st a+        return $ case r of+            Partial n s -> Partial n $ ConcatParseR stepR s extractR+            Continue n s -> Continue n $ ConcatParseR stepR s extractR+            Done n b -> Done n b+            Error err -> Error err++    {-# INLINE extractP #-}+    extractP n (Parser stepR initialR extractR) = do+        res <- initialR+        case res of+            IPartial s ->+                fmap+                    (first (\s1 -> ConcatParseR stepR s1 extractR))+                    (extractR s)+            IDone b -> return (Done n b)+            IError err -> return $ Error err++    extract (ConcatParseR stepR s extractR) =+        fmap (first (\s1 -> ConcatParseR stepR s1 extractR)) (extractR s)+    extract (ConcatParseL sL) = do+        rL <- extractL sL+        case rL of+            Error err -> return $ Error err+            Done n b -> extractP n $ func b+            Partial _ _ -> error "concatMap: extract Partial"+            Continue n s -> return $ Continue n (ConcatParseL s)++-- | Better performance 'concatMap' for non-failing parsers.+--+-- Does not work correctly for parsers that can fail.+--+-- ALL THE CAVEATS IN 'splitWith' APPLY HERE AS WELL.+--+{-# INLINE noErrorUnsafeConcatMap #-}+noErrorUnsafeConcatMap :: Monad m =>+    (b -> Parser a m c) -> Parser a m b -> Parser a m c+noErrorUnsafeConcatMap func (Parser stepL initialL extractL) =+    Parser step initial extract++    where++    {-# INLINE initializeR #-}+    initializeR (Parser stepR initialR extractR) = do+        resR <- initialR+        return $ case resR of+            IPartial sr -> IPartial $ ConcatParseR stepR sr extractR+            IDone br -> IDone br+            IError err -> IError err++    initial = do+        res <- initialL+        case res of+            IPartial s -> return $ IPartial $ ConcatParseL s+            IDone b -> initializeR (func b)+            IError err -> return $ IError err++    {-# INLINE initializeRL #-}+    initializeRL n (Parser stepR initialR extractR) = do+        resR <- initialR+        return $ case resR of+            IPartial sr -> Partial n $ ConcatParseR stepR sr extractR+            IDone br -> Done n br+            IError err -> Error err++    step (ConcatParseL st) a = do+        r <- stepL st a+        case r of+            Partial n s -> return $ Partial n (ConcatParseL s)+            Continue n s -> return $ Continue n (ConcatParseL s)+            Done n b -> initializeRL n (func b)+            Error err -> return $ Error err++    step (ConcatParseR stepR st extractR) a = do+        r <- stepR st a+        return $ case r of+            Partial n s -> Partial n $ ConcatParseR stepR s extractR+            Continue n s -> Continue n $ ConcatParseR stepR s extractR+            Done n b -> Done n b+            Error err -> Error err++    {-# INLINE extractP #-}+    extractP n (Parser stepR initialR extractR) = do+        res <- initialR+        case res of+            IPartial s ->+                fmap+                    (first (\s1 -> ConcatParseR stepR s1 extractR))+                    (extractR s)+            IDone b -> return (Done n b)+            IError err -> return $ Error err++    extract (ConcatParseR stepR s extractR) =+        fmap (first (\s1 -> ConcatParseR stepR s1 extractR)) (extractR s)+    extract (ConcatParseL sL) = do+        rL <- extractL sL+        case rL of+            Error err -> return $ Error err+            Done n b -> extractP n $ func b+            Partial _ _ -> error "concatMap: extract Partial"+            Continue n s -> return $ Continue n (ConcatParseL s)++-- Note: The monad instance has quadratic performance complexity. It works fine+-- for small number of compositions but for a scalable implementation we need a+-- CPS version.++-- | READ THE CAVEATS in 'concatMap' before using this instance.+--+-- >>> (>>=) = flip Parser.concatMap+--+instance Monad m => Monad (Parser a m) where+    {-# INLINE return #-}+    return = pure++    {-# INLINE (>>=) #-}+    (>>=) = flip concatMap++    {-# INLINE (>>) #-}+    (>>) = (*>)++-- | >>> fail = Parser.die+instance Monad m => Fail.MonadFail (Parser a m) where+    {-# INLINE fail #-}+    fail = die++{-+-- | See documentation of 'Streamly.Internal.Data.Parser.ParserK.Type.Parser'.+--+instance Monad m => MonadPlus (Parser a m) where+    {-# INLINE mzero #-}+    mzero = die "mzero"++    {-# INLINE mplus #-}+    mplus = alt+-}++-- | >>> liftIO = Parser.fromEffect . liftIO+instance (MonadIO m) => MonadIO (Parser a m) where+    {-# INLINE liftIO #-}+    liftIO = fromEffect . liftIO++------------------------------------------------------------------------------+-- Mapping on input+------------------------------------------------------------------------------++-- | @lmap f parser@ maps the function @f@ on the input of the parser.+--+-- >>> Stream.parse (Parser.lmap (\x -> x * x) (Parser.fromFold Fold.sum)) (Stream.enumerateFromTo 1 100)+-- Right 338350+--+-- > lmap = Parser.lmapM return+--+{-# INLINE lmap #-}+lmap :: (a -> b) -> Parser b m r -> Parser a m r+lmap f (Parser step begin done) = Parser step1 begin done++    where++    step1 x a = step x (f a)++-- | @lmapM f parser@ maps the monadic function @f@ on the input of the parser.+--+{-# INLINE lmapM #-}+lmapM :: Monad m => (a -> m b) -> Parser b m r -> Parser a m r+lmapM f (Parser step begin done) = Parser step1 begin done++    where++    step1 x a = f a >>= step x++-- | Include only those elements that pass a predicate.+--+-- >>> Stream.parse (Parser.filter (> 5) (Parser.fromFold Fold.sum)) $ Stream.fromList [1..10]+-- Right 40+--+{-# INLINE filter #-}+filter :: Monad m => (a -> Bool) -> Parser a m b -> Parser a m b+filter f (Parser step initial extract) = Parser step1 initial extract++    where++    step1 x a = if f a then step x a else return $ Partial 0 x
+ src/Streamly/Internal/Data/ParserK.hs view
@@ -0,0 +1,16 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.ParserK+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC++module Streamly.Internal.Data.ParserK+    (+      module Streamly.Internal.Data.ParserK.Type+    )+where++import Streamly.Internal.Data.ParserK.Type
+ src/Streamly/Internal/Data/ParserK/Type.hs view
@@ -0,0 +1,752 @@+-- |+-- Module      : Streamly.Internal.Data.Parser.ParserK.Type+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- CPS style implementation of parsers.+--+-- The CPS representation allows linear performance for Applicative, sequence,+-- Monad, Alternative, and choice operations compared to the quadratic+-- complexity of the corresponding direct style operations. However, direct+-- style operations allow fusion with ~10x better performance than CPS.+--+-- The direct style representation does not allow for recursive definitions of+-- "some" and "many" whereas CPS allows that.+--+-- 'Applicative' and 'Control.Applicative.Alternative' type class based+-- combinators from the+-- <http://hackage.haskell.org/package/parser-combinators parser-combinators>+-- package can also be used with the 'ParserK' type.++module Streamly.Internal.Data.ParserK.Type+    (+      Step (..)+    , Input (..)+    , ParseResult (..)+    , ParserK (..)+    , adaptC+    , adapt+    , adaptCG+    -- , toParser+    , fromPure+    , fromEffect+    , die+    )+where++#include "ArrayMacros.h"+#include "assert.hs"+#include "inline.hs"++#if !MIN_VERSION_base(4,18,0)+import Control.Applicative (liftA2)+#endif+import Control.Applicative (Alternative(..))+import Control.Monad (MonadPlus(..), ap)+import Control.Monad.IO.Class (MonadIO, liftIO)+-- import Control.Monad.Trans.Class (MonadTrans(lift))+import Data.Proxy (Proxy(..))+import GHC.Types (SPEC(..))+import Streamly.Internal.Data.Array.Type (Array(..))+import Streamly.Internal.Data.Unbox (Unbox(..))+import Streamly.Internal.System.IO (unsafeInlineIO)++import qualified Control.Monad.Fail as Fail+import qualified Streamly.Internal.Data.Array.Type as Array+import qualified Streamly.Internal.Data.MutArray.Generic as GenArr+    ( getIndexUnsafeWith+    )+import qualified Streamly.Internal.Data.Array.Generic as GenArr+import qualified Streamly.Internal.Data.Parser.Type as ParserD++-- Note: We cannot use an Array directly as input because we need to identify+-- the end of input case using None. We cannot do that using nil Array as nil+-- Arrays can be encountered in normal input as well.+--+-- We could specialize the ParserK type to use an Array directly, that provides+-- some performance improvement. The best advantage of that is when we consume+-- one element at a time from the array. If we really want that perf+-- improvement we can use a special ParserK type with the following Input.+--+-- data Input a = None | Chunk {-# UNPACK #-} !(Array a)+--+data Input a = None | Chunk a++-- | The intermediate result of running a parser step. The parser driver may+-- stop with a final result, pause with a continuation to resume, or fail with+-- an error.+--+-- See ParserD docs. This is the same as the ParserD Step except that it uses a+-- continuation in Partial and Continue constructors instead of a state in case+-- of ParserD.+--+-- /Pre-release/+--+data Step a m r =+    -- The Int is the current stream position index wrt to the start of the+    -- array.+      Done !Int r+    | Partial !Int (Input a -> m (Step a m r))+    | Continue !Int (Input a -> m (Step a m r))+    | Error !Int String++instance Functor m => Functor (Step a m) where+    fmap f (Done n r) = Done n (f r)+    fmap f (Partial n k) = Partial n (fmap (fmap f) . k)+    fmap f (Continue n k) = Continue n (fmap (fmap f) . k)+    fmap _ (Error n e) = Error n e++-- Note: Passing position index separately instead of passing it with the+-- result causes huge regression in expression parsing becnhmarks.++-- | The parser's result.+--+-- Int is the position index into the current input array. Could be negative.+-- Cannot be beyond the input array max bound.+--+-- /Pre-release/+--+data ParseResult b =+      Success !Int !b      -- Position index, result+    | Failure !Int !String -- Position index, error++-- | Map a function over 'Success'.+instance Functor ParseResult where+    fmap f (Success n b) = Success n (f b)+    fmap _ (Failure n e) = Failure n e++-- XXX Change the type to the shape (a -> m r -> m r) -> (m r -> m r) -> m r+--+-- The parse continuation would be: Array a -> m (Step a m r) -> m (Step a m r)+-- The extract continuation would be: m (Step a m r) -> m (Step a m r)+--+-- Use Step itself in place of ParseResult.++-- | A continuation passing style parser representation. A continuation of+-- 'Step's, each step passes a state and a parse result to the next 'Step'. The+-- resulting 'Step' may carry a continuation that consumes input 'a' and+-- results in another 'Step'. Essentially, the continuation may either consume+-- input without a result or return a result with no further input to be+-- consumed.+--+newtype ParserK a m b = MkParser+    { runParser :: forall r.+           -- Using "Input" in runParser is not necessary but it avoids making+           -- one more function call to get the input. This could be helpful+           -- for cases where we process just one element per call.+           --+           -- Do not eta reduce the applications of this continuation.+           --+           (ParseResult b -> Int -> Input a -> m (Step a m r))+           -- XXX Maintain and pass the original position in the stream. that+           -- way we can also report better errors. Use a Context structure for+           -- passing the state.++           -- Stream position index wrt to the current input array start. If+           -- negative then backtracking is required before using the array.+           -- The parser should use "Continue -n" in this case if it needs to+           -- consume input. Negative value cannot be beyond the current+           -- backtrack buffer. Positive value cannot be beyond array length.+           -- If the parser needs to advance beyond the array length it should+           -- use "Continue +n".+        -> Int+           -- used elem count, a count of elements consumed by the parser. If+           -- an Alternative fails we need to backtrack by this amount.+        -> Int+           -- The second argument is the used count as described above. The+           -- current input position is carried as part of 'Success'+           -- constructor of 'ParseResult'.+        -> Input a+        -> m (Step a m r)+    }++-------------------------------------------------------------------------------+-- Functor+-------------------------------------------------------------------------------++-- XXX rewrite this using ParserD, expose rmapM from ParserD.++-- | Map a function on the result i.e. on @b@ in @Parser a m b@.+instance Functor m => Functor (ParserK a m) where+    {-# INLINE fmap #-}+    fmap f parser = MkParser $ \k n st arr ->+        let k1 res = k (fmap f res)+         in runParser parser k1 n st arr++-------------------------------------------------------------------------------+-- Sequential applicative+-------------------------------------------------------------------------------++-- This is the dual of stream "fromPure".++-- | A parser that always yields a pure value without consuming any input.+--+-- /Pre-release/+--+{-# INLINE fromPure #-}+fromPure :: b -> ParserK a m b+fromPure b = MkParser $ \k n st arr -> k (Success n b) st arr++-- | See 'Streamly.Internal.Data.Parser.fromEffect'.+--+-- /Pre-release/+--+{-# INLINE fromEffect #-}+fromEffect :: Monad m => m b -> ParserK a m b+fromEffect eff =+    MkParser $ \k n st arr -> eff >>= \b -> k (Success n b) st arr++-- | @f \<$> p1 \<*> p2@ applies parsers p1 and p2 sequentially to an input+-- stream. The first parser runs and processes the input, the remaining input+-- is then passed to the second parser. If both parsers succeed, their outputs+-- are applied to the function @f@. If either parser fails, the operation+-- fails.+--+instance Monad m => Applicative (ParserK a m) where+    {-# INLINE pure #-}+    pure = fromPure++    {-# INLINE (<*>) #-}+    (<*>) = ap++    {-# INLINE (*>) #-}+    p1 *> p2 = MkParser $ \k n st arr ->+        let k1 (Success n1 _) s input = runParser p2 k n1 s input+            k1 (Failure n1 e) s input = k (Failure n1 e) s input+        in runParser p1 k1 n st arr++    {-# INLINE (<*) #-}+    p1 <* p2 = MkParser $ \k n st arr ->+        let k1 (Success n1 b) s1 input =+                let k2 (Success n2 _) s2 input2  = k (Success n2 b) s2 input2+                    k2 (Failure n2 e) s2 input2  = k (Failure n2 e) s2 input2+                in runParser p2 k2 n1 s1 input+            k1 (Failure n1 e) s1 input = k (Failure n1 e) s1 input+        in runParser p1 k1 n st arr++    {-# INLINE liftA2 #-}+    liftA2 f p = (<*>) (fmap f p)++-------------------------------------------------------------------------------+-- Monad+-------------------------------------------------------------------------------++-- This is the dual of "nil".+--+-- | A parser that always fails with an error message without consuming+-- any input.+--+-- /Pre-release/+--+{-# INLINE die #-}+die :: String -> ParserK a m b+die err = MkParser (\k n st arr -> k (Failure n err) st arr)++-- | Monad composition can be used for lookbehind parsers, we can dynamically+-- compose new parsers based on the results of the previously parsed values.+instance Monad m => Monad (ParserK a m) where+    {-# INLINE return #-}+    return = pure++    {-# INLINE (>>=) #-}+    p >>= f = MkParser $ \k n st arr ->+        let k1 (Success n1 b) s1 inp = runParser (f b) k n1 s1 inp+            k1 (Failure n1 e) s1 inp = k (Failure n1 e) s1 inp+         in runParser p k1 n st arr++    {-# INLINE (>>) #-}+    (>>) = (*>)++#if !(MIN_VERSION_base(4,13,0))+    -- This is redefined instead of just being Fail.fail to be+    -- compatible with base 4.8.+    {-# INLINE fail #-}+    fail = die+#endif+instance Monad m => Fail.MonadFail (ParserK a m) where+    {-# INLINE fail #-}+    fail = die++instance MonadIO m => MonadIO (ParserK a m) where+    {-# INLINE liftIO #-}+    liftIO = fromEffect . liftIO++-------------------------------------------------------------------------------+-- Alternative+-------------------------------------------------------------------------------++-- | @p1 \<|> p2@ passes the input to parser p1, if it succeeds, the result is+-- returned. However, if p1 fails, the parser driver backtracks and tries the+-- same input on the alternative parser p2, returning the result if it+-- succeeds.+--+instance Monad m => Alternative (ParserK a m) where+    {-# INLINE empty #-}+    empty = die "empty"++    {-# INLINE (<|>) #-}+    p1 <|> p2 = MkParser $ \k n _ arr ->+        let+            k1 (Failure pos _) used input = runParser p2 k (pos - used) 0 input+            k1 success _ input = k success 0 input+        in runParser p1 k1 n 0 arr++    -- some and many are implemented here instead of using default definitions+    -- so that we can use INLINE on them. It gives 50% performance improvement.++    {-# INLINE many #-}+    many v = many_v++        where++        many_v = some_v <|> pure []+        some_v = (:) <$> v <*> many_v++    {-# INLINE some #-}+    some v = some_v++        where++        many_v = some_v <|> pure []+        some_v = (:) <$> v <*> many_v++-- | 'mzero' is same as 'empty', it aborts the parser. 'mplus' is same as+-- '<|>', it selects the first succeeding parser.+--+instance Monad m => MonadPlus (ParserK a m) where+    {-# INLINE mzero #-}+    mzero = die "mzero"++    {-# INLINE mplus #-}+    mplus = (<|>)++{-+instance MonadTrans (ParserK a) where+    {-# INLINE lift #-}+    lift = fromEffect+-}++-------------------------------------------------------------------------------+-- Convert ParserD to ParserK+-------------------------------------------------------------------------------++--------------------------------------------------------------------------------+-- Chunked+--------------------------------------------------------------------------------++{-# INLINE adaptCWith #-}+adaptCWith+    :: forall m a s b r. (Monad m, Unbox a)+    => (s -> a -> m (ParserD.Step s b))+    -> m (ParserD.Initial s b)+    -> (s -> m (ParserD.Step s b))+    -> (ParseResult b -> Int -> Input (Array a) -> m (Step (Array a) m r))+    -> Int+    -> Int+    -> Input (Array a)+    -> m (Step (Array a) m r)+adaptCWith pstep initial extract cont !offset0 !usedCount !input = do+    res <- initial+    case res of+        ParserD.IPartial pst -> do+            case input of+                Chunk arr -> parseContChunk usedCount offset0 pst arr+                None -> parseContNothing usedCount pst+        ParserD.IDone b -> cont (Success offset0 b) usedCount input+        ParserD.IError err -> cont (Failure offset0 err) usedCount input++    where++    -- XXX We can maintain an absolute position instead of relative that will+    -- help in reporting of error location in the stream.+    {-# NOINLINE parseContChunk #-}+    parseContChunk !count !offset !state arr@(Array contents start end) = do+         if offset >= 0+         then go SPEC (start + offset * SIZE_OF(a)) state+         else return $ Continue offset (parseCont count state)++        where++        {-# INLINE onDone #-}+        onDone n b =+            assert (n <= Array.length arr)+                (cont (Success n b) (count + n - offset) (Chunk arr))++        {-# INLINE callParseCont #-}+        callParseCont constr n pst1 =+            assert (n < 0 || n >= Array.length arr)+                (return $ constr n (parseCont (count + n - offset) pst1))++        {-# INLINE onPartial #-}+        onPartial = callParseCont Partial++        {-# INLINE onContinue #-}+        onContinue = callParseCont Continue++        {-# INLINE onError #-}+        onError n err =+            cont (Failure n err) (count + n - offset) (Chunk arr)++        {-# INLINE onBack #-}+        onBack offset1 elemSize constr pst = do+            let pos = offset1 - start+             in if pos >= 0+                then go SPEC offset1 pst+                else constr (pos `div` elemSize) pst++        -- Note: div may be expensive but the alternative is to maintain an element+        -- offset in addition to a byte offset or just the element offset and use+        -- multiplication to get the byte offset every time, both these options+        -- turned out to be more expensive than using div.+        go !_ !cur !pst | cur >= end =+            onContinue ((end - start) `div` SIZE_OF(a))  pst+        go !_ !cur !pst = do+            let !x = unsafeInlineIO $ peekAt cur contents+            pRes <- pstep pst x+            let elemSize = SIZE_OF(a)+                next = INDEX_NEXT(cur,a)+                back n = next - n * elemSize+                curOff = (cur - start) `div` elemSize+                nextOff = (next - start) `div` elemSize+            -- The "n" here is stream position index wrt the array start, and+            -- not the backtrack count as returned by byte stream parsers.+            case pRes of+                ParserD.Done 0 b ->+                    onDone nextOff b+                ParserD.Done 1 b ->+                    onDone curOff b+                ParserD.Done n b ->+                    onDone ((back n - start) `div` elemSize) b+                ParserD.Partial 0 pst1 ->+                    go SPEC next pst1+                ParserD.Partial 1 pst1 ->+                    go SPEC cur pst1+                ParserD.Partial n pst1 ->+                    onBack (back n) elemSize onPartial pst1+                ParserD.Continue 0 pst1 ->+                    go SPEC next pst1+                ParserD.Continue 1 pst1 ->+                    go SPEC cur pst1+                ParserD.Continue n pst1 ->+                    onBack (back n) elemSize onContinue pst1+                ParserD.Error err ->+                    onError curOff err++    {-# NOINLINE parseContNothing #-}+    parseContNothing !count !pst = do+        r <- extract pst+        case r of+            -- IMPORTANT: the n here is from the byte stream parser, that means+            -- it is the backtrack element count and not the stream position+            -- index into the current input array.+            ParserD.Done n b ->+                assert (n >= 0)+                    (cont (Success (- n) b) (count - n) None)+            ParserD.Continue n pst1 ->+                assert (n >= 0)+                    (return $ Continue (- n) (parseCont (count - n) pst1))+            ParserD.Error err ->+                -- XXX It is called only when there is no input arr. So using 0+                -- as the position is correct?+                cont (Failure 0 err) count None+            ParserD.Partial _ _ -> error "Bug: adaptCWith Partial unreachable"++    -- XXX Maybe we can use two separate continuations instead of using+    -- Just/Nothing cases here. That may help in avoiding the parseContJust+    -- function call.+    {-# INLINE parseCont #-}+    parseCont !cnt !pst (Chunk arr) = parseContChunk cnt 0 pst arr+    parseCont !cnt !pst None = parseContNothing cnt pst++-- | Convert an element 'Parser' to a chunked 'ParserK'. A chunked parser is+-- more efficient than an element parser.+--+-- /Pre-release/+--+{-# INLINE_LATE adaptC #-}+adaptC :: (Monad m, Unbox a) => ParserD.Parser a m b -> ParserK (Array a) m b+adaptC (ParserD.Parser step initial extract) =+    MkParser $ adaptCWith step initial extract++--------------------------------------------------------------------------------+-- Singular+--------------------------------------------------------------------------------++{-# INLINE adaptWith #-}+adaptWith+    :: forall m a s b r. (Monad m)+    => (s -> a -> m (ParserD.Step s b))+    -> m (ParserD.Initial s b)+    -> (s -> m (ParserD.Step s b))+    -> (ParseResult b -> Int -> Input a -> m (Step a m r))+    -> Int+    -> Int+    -> Input a+    -> m (Step a m r)+adaptWith pstep initial extract cont !relPos !usedCount !input = do+    res <- initial+    case res of+        ParserD.IPartial pst -> do+            -- XXX can we come here with relPos 1?+            if relPos == 0+            then+                case input of+                    Chunk arr -> parseContChunk usedCount pst arr+                    None -> parseContNothing usedCount pst+            -- XXX Previous code was using Continue in this case+            else pure $ Partial relPos (parseCont usedCount pst)+        ParserD.IDone b -> cont (Success relPos b) usedCount input+        ParserD.IError err -> cont (Failure relPos err) usedCount input++    where++    -- XXX We can maintain an absolute position instead of relative that will+    -- help in reporting of error location in the stream.+    {-# NOINLINE parseContChunk #-}+    parseContChunk !count !state x = do+         go SPEC state++        where++        go !_ !pst = do+            pRes <- pstep pst x+            case pRes of+                ParserD.Done 0 b ->+                    cont (Success 1 b) (count + 1) (Chunk x)+                ParserD.Done 1 b ->+                    cont (Success 0 b) count (Chunk x)+                ParserD.Done n b ->+                    cont (Success (1 - n) b) (count + 1 - n) (Chunk x)+                ParserD.Partial 0 pst1 ->+                    pure $ Partial 1 (parseCont (count + 1) pst1)+                ParserD.Partial 1 pst1 ->+                    -- XXX Since we got Partial, the driver should drop the+                    -- buffer, we should call the driver here?+                    go SPEC pst1+                ParserD.Partial n pst1 ->+                    pure $ Partial (1 - n) (parseCont (count + 1 - n) pst1)+                ParserD.Continue 0 pst1 ->+                    pure $ Continue 1 (parseCont (count + 1) pst1)+                ParserD.Continue 1 pst1 ->+                    go SPEC pst1+                ParserD.Continue n pst1 ->+                    pure $ Continue (1 - n) (parseCont (count + 1 - n) pst1)+                ParserD.Error err ->+                    -- XXX fix undefined+                    cont (Failure 0 err) count (Chunk x)++    {-# NOINLINE parseContNothing #-}+    parseContNothing !count !pst = do+        r <- extract pst+        case r of+            -- IMPORTANT: the n here is from the byte stream parser, that means+            -- it is the backtrack element count and not the stream position+            -- index into the current input array.+            ParserD.Done n b ->+                assert (n >= 0)+                    (cont (Success (- n) b) (count - n) None)+            ParserD.Continue n pst1 ->+                assert (n >= 0)+                    (return $ Continue (- n) (parseCont (count - n) pst1))+            ParserD.Error err ->+                -- XXX It is called only when there is no input arr. So using 0+                -- as the position is correct?+                cont (Failure 0 err) count None+            ParserD.Partial _ _ -> error "Bug: adaptCWith Partial unreachable"++    -- XXX Maybe we can use two separate continuations instead of using+    -- Just/Nothing cases here. That may help in avoiding the parseContJust+    -- function call.+    {-# INLINE parseCont #-}+    parseCont !cnt !pst (Chunk arr) = parseContChunk cnt pst arr+    parseCont !cnt !pst None = parseContNothing cnt pst++-- | Convert a 'Parser' to 'ParserK'.+--+-- /Pre-release/+--+{-# INLINE_LATE adapt #-}+adapt :: Monad m => ParserD.Parser a m b -> ParserK a m b+adapt (ParserD.Parser step initial extract) =+    MkParser $ adaptWith step initial extract++--------------------------------------------------------------------------------+-- Chunked Generic+--------------------------------------------------------------------------------++{-# INLINE adaptCGWith #-}+adaptCGWith+    :: forall m a s b r. (Monad m)+    => (s -> a -> m (ParserD.Step s b))+    -> m (ParserD.Initial s b)+    -> (s -> m (ParserD.Step s b))+    -> (ParseResult b -> Int -> Input (GenArr.Array a) -> m (Step (GenArr.Array a) m r))+    -> Int+    -> Int+    -> Input (GenArr.Array a)+    -> m (Step (GenArr.Array a) m r)+adaptCGWith pstep initial extract cont !offset0 !usedCount !input = do+    res <- initial+    case res of+        ParserD.IPartial pst -> do+            case input of+                Chunk arr -> parseContChunk usedCount offset0 pst arr+                None -> parseContNothing usedCount pst+        ParserD.IDone b -> cont (Success offset0 b) usedCount input+        ParserD.IError err -> cont (Failure offset0 err) usedCount input++    where++    {-# NOINLINE parseContChunk #-}+    parseContChunk !count !offset !state arr@(GenArr.Array contents start len) = do+         if offset >= 0+         then go SPEC (start + offset) state+         else return $ Continue offset (parseCont count state)++        where++        {-# INLINE end #-}+        end = start + len++        {-# INLINE onDone #-}+        onDone n b =+            assert (n <= GenArr.length arr)+                (cont (Success n b) (count + n - offset) (Chunk arr))++        {-# INLINE callParseCont #-}+        callParseCont constr n pst1 =+            assert (n < 0 || n >= GenArr.length arr)+                (return $ constr n (parseCont (count + n - offset) pst1))++        {-# INLINE onPartial #-}+        onPartial = callParseCont Partial++        {-# INLINE onContinue #-}+        onContinue = callParseCont Continue++        {-# INLINE onError #-}+        onError n err =+            cont (Failure n err) (count + n - offset) (Chunk arr)++        {-# INLINE onBack #-}+        onBack offset1 constr pst = do+            let pos = offset1 - start+             in if pos >= 0+                then go SPEC offset1 pst+                else constr pos pst++        go !_ !cur !pst | cur >= end =+            onContinue len  pst+        go !_ !cur !pst = do+            let !x = unsafeInlineIO $ GenArr.getIndexUnsafeWith contents cur+            pRes <- pstep pst x+            let next = cur + 1+                back n = next - n+                curOff = cur - start+                nextOff = next - start+            -- The "n" here is stream position index wrt the array start, and+            -- not the backtrack count as returned by byte stream parsers.+            case pRes of+                ParserD.Done 0 b ->+                    onDone nextOff b+                ParserD.Done 1 b ->+                    onDone curOff b+                ParserD.Done n b ->+                    onDone (back n - start) b+                ParserD.Partial 0 pst1 ->+                    go SPEC next pst1+                ParserD.Partial 1 pst1 ->+                    go SPEC cur pst1+                ParserD.Partial n pst1 ->+                    onBack (back n) onPartial pst1+                ParserD.Continue 0 pst1 ->+                    go SPEC next pst1+                ParserD.Continue 1 pst1 ->+                    go SPEC cur pst1+                ParserD.Continue n pst1 ->+                    onBack (back n) onContinue pst1+                ParserD.Error err ->+                    onError curOff err++    {-# NOINLINE parseContNothing #-}+    parseContNothing !count !pst = do+        r <- extract pst+        case r of+            -- IMPORTANT: the n here is from the byte stream parser, that means+            -- it is the backtrack element count and not the stream position+            -- index into the current input array.+            ParserD.Done n b ->+                assert (n >= 0)+                    (cont (Success (- n) b) (count - n) None)+            ParserD.Continue n pst1 ->+                assert (n >= 0)+                    (return $ Continue (- n) (parseCont (count - n) pst1))+            ParserD.Error err ->+                -- XXX It is called only when there is no input arr. So using 0+                -- as the position is correct?+                cont (Failure 0 err) count None+            ParserD.Partial _ _ -> error "Bug: adaptCGWith Partial unreachable"++    {-# INLINE parseCont #-}+    parseCont !cnt !pst (Chunk arr) = parseContChunk cnt 0 pst arr+    parseCont !cnt !pst None = parseContNothing cnt pst++-- | A generic 'adaptC'. Similar to 'adaptC' but is not constrained to 'Unbox'+-- types.+--+-- /Pre-release/+--+{-# INLINE_LATE adaptCG #-}+adaptCG ::+       Monad m => ParserD.Parser a m b -> ParserK (GenArr.Array a) m b+adaptCG (ParserD.Parser step initial extract) =+    MkParser $ adaptCGWith step initial extract++{-+-------------------------------------------------------------------------------+-- Convert CPS style 'Parser' to direct style 'D.Parser'+-------------------------------------------------------------------------------++-- | A continuation to extract the result when a CPS parser is done.+{-# INLINE parserDone #-}+parserDone :: Monad m => ParseResult b -> Int -> Input a -> m (Step a m b)+parserDone (Success n b) _ None = return $ Done n b+parserDone (Failure n e) _ None = return $ Error n e+parserDone _ _ _ = error "Bug: toParser: called with input"++-- | Convert a CPS style 'ParserK' to a direct style 'ParserD.Parser'.+--+-- /Pre-release/+--+{-# INLINE_LATE toParser #-}+toParser :: Monad m => ParserK a m b -> ParserD.Parser a m b+toParser parser = ParserD.Parser step initial extract++    where++    initial = pure (ParserD.IPartial (\x -> runParser parser 0 0 x parserDone))++    step cont a = do+        r <- cont (Single a)+        return $ case r of+            Done n b -> ParserD.Done n b+            Error _ e -> ParserD.Error e+            Partial n cont1 -> ParserD.Partial n cont1+            Continue n cont1 -> ParserD.Continue n cont1++    extract cont = do+        r <- cont None+        case r of+            Done n b -> return $ ParserD.Done n b+            Error _ e -> return $ ParserD.Error e+            Partial _ cont1 -> extract cont1+            Continue n cont1 -> return $ ParserD.Continue n cont1++{-# RULES "fromParser/toParser fusion" [2]+    forall s. toParser (fromParser s) = s #-}+{-# RULES "toParser/fromParser fusion" [2]+    forall s. fromParser (toParser s) = s #-}+-}
src/Streamly/Internal/Data/Pipe.hs view
@@ -24,11 +24,10 @@ module Streamly.Internal.Data.Pipe     (     -- * Pipe Type-      Pipe+      module Streamly.Internal.Data.Pipe.Type      -- * Pipes     -- ** Mapping-    , map     , mapM      {-@@ -112,11 +111,9 @@     , groupsRollingBy     -} -    -- * Composing Pipes-    , tee-    , zipWith-    , compose ++     {-     -- * Distributing     -- |@@ -244,9 +241,9 @@  -- import Streamly.Data.Fold.Types (Fold(..)) import Streamly.Internal.Data.Pipe.Type-       (Pipe(..), PipeState(..), Step(..), zipWith, tee, map, compose)+ -- import Streamly.Internal.Data.Array.Type (Array)--- import Streamly.Internal.Data.Ring.Unboxed (Ring)+-- import Streamly.Internal.Data.Ring (Ring) -- import Streamly.Internal.Data.Stream (Stream) -- import Streamly.Internal.Data.Time.Units -- (AbsTime, MilliSecond64(..), addToAbsTime, diffAbsTime, toRelTime,@@ -256,8 +253,8 @@  -- import qualified Streamly.Internal.Data.Array.Type as A -- import qualified Streamly.Data.Stream as S--- import qualified Streamly.Internal.Data.Stream.StreamD as D--- import qualified Streamly.Internal.Data.Stream.StreamK as K+-- import qualified Streamly.Internal.Data.Stream as D+-- import qualified Streamly.Internal.Data.StreamK as K -- import qualified Streamly.Internal.Data.Stream.Common as P  ------------------------------------------------------------------------------
src/Streamly/Internal/Data/Producer.hs view
@@ -24,31 +24,24 @@ -- unecessary function calls can be avoided.  module Streamly.Internal.Data.Producer-    ( Producer (..)+    (+      module Streamly.Internal.Data.Producer.Source+    , module Streamly.Internal.Data.Producer.Type      -- * Converting     , simplify--    -- * Producers-    , nil-    , nilM-    , unfoldrM     , fromStreamD-    , fromList--    -- * Combinators-    , NestedLoop (..)-    , concat     ) where  #include "inline.hs" -import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))-import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..))+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.Producer.Source import Streamly.Internal.Data.Producer.Type import Prelude hiding (concat) 
src/Streamly/Internal/Data/Producer/Source.hs view
@@ -36,11 +36,11 @@ import Control.Exception (assert) import GHC.Exts (SpecConstrAnnotation(..)) import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Parser.ParserD (ParseError(..), Step(..))+import Streamly.Internal.Data.Parser (ParseError(..), Step(..)) import Streamly.Internal.Data.Producer.Type (Producer(..))-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))+import Streamly.Internal.Data.Stream.Step (Step(..)) -import qualified Streamly.Internal.Data.Parser.ParserD as ParserD+import qualified Streamly.Internal.Data.Parser as ParserD -- import qualified Streamly.Internal.Data.Parser.ParserK.Type as ParserK  import Prelude hiding (read)@@ -160,7 +160,11 @@                         return (Right b, unread src s1)                     Error err -> do                         s1 <- uextract s-                        return (Left (ParseError err), unread [x] s1)+                        let src  = Prelude.reverse (getList buf)+                        return+                            ( Left (ParseError err)+                            , unread (src ++ [x]) s1+                            )             Skip s -> go SPEC s buf pst             Stop -> goStop buf pst @@ -190,7 +194,11 @@                 return (Right b, unread src s1)             Error err -> do                     s1 <- uextract s-                    return (Left (ParseError err), unread (x:xs) s1)+                    let src  = Prelude.reverse (getList buf)+                    return+                        ( Left (ParseError err)+                        , unread (src ++ (x:xs)) s1+                        )      -- This is a simplified gobuf     goExtract !_ buf (List []) !pst = goStop buf pst@@ -216,8 +224,12 @@                 let src0 = Prelude.take n (x:getList buf)                     src  = Prelude.reverse src0                 return (Right b, unread src (source Nothing))-            Error err ->-                    return (Left (ParseError err), unread (x:xs) (source Nothing))+            Error err -> do+                    let src  = Prelude.reverse (getList buf)+                    return+                        ( Left (ParseError err)+                        , unread (src ++ (x:xs)) (source Nothing)+                        )      -- This is a simplified goExtract     {-# INLINE goStop #-}@@ -238,8 +250,9 @@                 let src0 = Prelude.take n (getList buf)                     src  = Prelude.reverse src0                 return (Right b, unread src (source Nothing))-            Error err ->-                return (Left (ParseError err), source Nothing)+            Error err -> do+                let src  = Prelude.reverse (getList buf)+                return (Left (ParseError err), unread src (source Nothing))  {- -- | Parse a buffered source using a parser, returning the parsed value and the
src/Streamly/Internal/Data/Producer/Type.hs view
@@ -33,7 +33,7 @@ #include "inline.hs"  import Fusion.Plugin.Types (Fuse(..))-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))+import Streamly.Internal.Data.Stream.Step (Step(..)) import Prelude hiding (concat, map)  ------------------------------------------------------------------------------
src/Streamly/Internal/Data/Refold/Type.hs view
@@ -47,12 +47,12 @@ import Fusion.Plugin.Types (Fuse(..)) import Streamly.Internal.Data.Fold.Step (Step(..), mapMStep) -import Prelude hiding (take, iterate)+import Prelude hiding (Foldable(..), take, iterate)  -- $setup -- >>> :m -- >>> import qualified Streamly.Internal.Data.Refold.Type as Refold--- >>> import qualified Streamly.Internal.Data.Fold.Type as Fold+-- >>> import qualified Streamly.Internal.Data.Fold as Fold -- >>> import qualified Streamly.Internal.Data.Stream as Stream  -- All folds in the Fold module should be implemented using Refolds.
src/Streamly/Internal/Data/Ring.hs view
@@ -1,164 +1,619 @@ -- | -- Module      : Streamly.Internal.Data.Ring--- Copyright   : (c) 2021 Composewell Technologies--- License     : BSD-3-Clause+-- Copyright   : (c) 2019 Composewell Technologies+-- License     : BSD3 -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC --+-- A ring array is a circular mutable array. +-- XXX Write benchmarks+-- XXX Make the implementation similar to mutable array+-- XXX Rename this module to Data.RingArray.Storable+ module Streamly.Internal.Data.Ring     ( Ring(..) -    -- * Generation-    , createRing-    , writeLastN+    -- * Construction+    , new+    , newRing+    , writeN -    -- * Modification-    , seek-    , unsafeInsertRingWith+    , advance+    , moveBy+    , startOf -    -- * Conversion-    , toMutArray-    , toStreamWith+    -- * Random writes+    , unsafeInsert+    , slide+    , putIndex+    , modifyIndex++    -- * Unfolds+    , read+    , readRev++    -- * Random reads+    , getIndex+    , getIndexUnsafe+    , getIndexRev++    -- * Size+    , length+    , byteLength+    -- , capacity+    , byteCapacity+    , bytesFree++    -- * Casting+    , cast+    , castUnsafe+    , asBytes+    , fromArray++    -- * Folds+    , unsafeFoldRing+    , unsafeFoldRingM+    , unsafeFoldRingFullM+    , unsafeFoldRingNM++    -- * Stream of Arrays+    , ringsOf++    -- * Fast Byte Comparisons+    , unsafeEqArray+    , unsafeEqArrayN++    , slidingWindow+    , slidingWindowWith     ) where -#include "assert.hs"+#include "ArrayMacros.h"+#include "inline.hs" -import Control.Monad.IO.Class (liftIO, MonadIO)-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Array.Generic.Mut.Type-    ( MutArray(..)-    , new-    , uninit-    , putIndexUnsafe-    , putSliceUnsafe-    )--- import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream-import qualified Streamly.Internal.Data.Fold.Type as Fold+import Control.Exception (assert)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Word (Word8)+import Foreign.Storable+import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)+import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)+import Foreign.Ptr (plusPtr, minusPtr, castPtr)+import Streamly.Internal.Data.Unbox as Unboxed (Unbox(peekAt))+import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)+import GHC.Ptr (Ptr(..))+import Streamly.Internal.Data.MutArray.Type (MutArray)+import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Stream.Step (Step(..))+import Streamly.Internal.Data.Unfold.Type (Unfold(..))+import Streamly.Internal.System.IO (unsafeInlineIO) --- XXX Use MutableArray rather than keeping a MutArray here.+import qualified Streamly.Internal.Data.MutArray.Type as MA+import qualified Streamly.Internal.Data.Array.Type as A++import Prelude hiding (length, concat, read)++-- $setup+-- >>> :m+-- >>> import qualified Streamly.Internal.Data.Ring as Ring++-- | A ring buffer is a mutable array of fixed size. Initially the array is+-- empty, with ringStart pointing at the start of allocated memory. We call the+-- next location to be written in the ring as ringHead. Initially ringHead ==+-- ringStart. When the first item is added, ringHead points to ringStart ++-- sizeof item. When the buffer becomes full ringHead would wrap around to+-- ringStart. When the buffer is full, ringHead always points at the oldest+-- item in the ring and the newest item added always overwrites the oldest+-- item.+--+-- When using it we should keep in mind that a ringBuffer is a mutable data+-- structure. We should not leak out references to it for immutable use.+-- data Ring a = Ring-    { ringArr :: MutArray a-    -- XXX We can keep the current fill amount, Or we can keep a count of total-    -- elements inserted and compute ring head as well using mod on that,-    -- assuming it won't overflow. But mod could be expensive.-    , ringHead :: !Int -- current index to be over-written-    , ringMax :: !Int  -- first index beyond allocated memory+    { ringStart :: {-# UNPACK #-} !(ForeignPtr a) -- first address+    , ringBound :: {-# UNPACK #-} !(Ptr a)        -- first address beyond allocated memory     }  ---------------------------------------------------------------------------------- Generation+-- Construction ------------------------------------------------------------------------------- --- XXX If we align the ringMax to nearest power of two then computation of the--- index to write could be cheaper.-{-# INLINE createRing #-}-createRing :: MonadIO m => Int -> m (Ring a)-createRing count = liftIO $ do-    arr <- new count-    arr1 <- uninit arr count+-- | Get the first address of the ring as a pointer.+startOf :: Ring a -> Ptr a+startOf = unsafeForeignPtrToPtr . ringStart++-- | Create a new ringbuffer and return the ring buffer and the ringHead.+-- Returns the ring and the ringHead, the ringHead is same as ringStart.+{-# INLINE new #-}+new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)+new count = do+    let size = count * max 1 (sizeOf (undefined :: a))+    fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))+    let p = unsafeForeignPtrToPtr fptr     return (Ring-        { ringArr = arr1-        , ringHead = 0-        , ringMax = count-        })+        { ringStart = fptr+        , ringBound = p `plusPtr` size+        }, p) +-- XXX Rename this to "new".+--+-- | @newRing count@ allocates an empty array that can hold 'count' items.  The+-- memory of the array is uninitialized and the allocation is aligned as per+-- the 'Storable' instance of the type.+--+-- /Unimplemented/+{-# INLINE newRing #-}+newRing :: Int -> m (Ring a)+newRing = undefined -{-# INLINE writeLastN #-}-writeLastN :: MonadIO m => Int -> Fold m a (Ring a)-writeLastN n = Fold step initial extract+-- | Advance the ringHead by 1 item, wrap around if we hit the end of the+-- array.+{-# INLINE advance #-}+advance :: forall a. Storable a => Ring a -> Ptr a -> Ptr a+advance Ring{..} ringHead =+    let ptr = PTR_NEXT(ringHead,a)+    in if ptr <  ringBound+       then ptr+       else unsafeForeignPtrToPtr ringStart +-- | Move the ringHead by n items. The direction depends on the sign on whether+-- n is positive or negative. Wrap around if we hit the beginning or end of the+-- array.+{-# INLINE moveBy #-}+moveBy :: forall a. Storable a => Int -> Ring a -> Ptr a -> Ptr a+moveBy by Ring {..} ringHead = ringStartPtr `plusPtr` advanceFromHead+     where -    initial = do+    elemSize = STORABLE_SIZE_OF(a)+    ringStartPtr = unsafeForeignPtrToPtr ringStart+    lenInBytes = ringBound `minusPtr` ringStartPtr+    offInBytes = ringHead `minusPtr` ringStartPtr+    len = assert (lenInBytes `mod` elemSize == 0) $ lenInBytes `div` elemSize+    off = assert (offInBytes `mod` elemSize == 0) $ offInBytes `div` elemSize+    advanceFromHead = (off + by `mod` len) * elemSize++-- XXX Move the writeLastN from array module here.+--+-- | @writeN n@ is a rolling fold that keeps the last n elements of the stream+-- in a ring array.+--+-- /Unimplemented/+{-# INLINE writeN #-}+writeN :: -- (Storable a, MonadIO m) =>+    Int -> Fold m a (Ring a)+writeN = undefined++-------------------------------------------------------------------------------+-- Conversions+-------------------------------------------------------------------------------++-- | Cast a mutable array to a ring array.+fromArray :: MutArray a -> Ring a+fromArray = undefined++-------------------------------------------------------------------------------+-- Conversion to/from array+-------------------------------------------------------------------------------++-- | Modify a given index of a ring array using a modifier function.+--+-- /Unimplemented/+modifyIndex :: -- forall m a b. (MonadIO m, Storable a) =>+    Ring a -> Int -> (a -> (a, b)) -> m b+modifyIndex = undefined++-- | /O(1)/ Write the given element at the given index in the ring array.+-- Performs in-place mutation of the array.+--+-- >>> putIndex arr ix val = Ring.modifyIndex arr ix (const (val, ()))+--+-- /Unimplemented/+{-# INLINE putIndex #-}+putIndex :: -- (MonadIO m, Storable a) =>+    Ring a -> Int -> a -> m ()+putIndex = undefined++-- | Insert an item at the head of the ring, when the ring is full this+-- replaces the oldest item in the ring with the new item. This is unsafe+-- beause ringHead supplied is not verified to be within the Ring. Also,+-- the ringStart foreignPtr must be guaranteed to be alive by the caller.+{-# INLINE unsafeInsert #-}+unsafeInsert :: Storable a => Ring a -> Ptr a -> a -> IO (Ptr a)+unsafeInsert rb ringHead newVal = do+    poke ringHead newVal+    -- touchForeignPtr (ringStart rb)+    return $ advance rb ringHead++-- | Insert an item at the head of the ring, when the ring is full this+-- replaces the oldest item in the ring with the new item.+--+-- /Unimplemented/+slide :: -- forall m a. (MonadIO m, Storable a) =>+    Ring a -> a -> m (Ring a)+slide = undefined++-------------------------------------------------------------------------------+-- Random reads+-------------------------------------------------------------------------------++-- | Return the element at the specified index without checking the bounds.+--+-- Unsafe because it does not check the bounds of the ring array.+{-# INLINE_NORMAL getIndexUnsafe #-}+getIndexUnsafe :: -- forall m a. (MonadIO m, Storable a) =>+    Ring a -> Int -> m a+getIndexUnsafe = undefined++-- | /O(1)/ Lookup the element at the given index. Index starts from 0.+--+{-# INLINE getIndex #-}+getIndex :: -- (MonadIO m, Storable a) =>+    Ring a -> Int -> m a+getIndex = undefined++-- | /O(1)/ Lookup the element at the given index from the end of the array.+-- Index starts from 0.+--+-- Slightly faster than computing the forward index and using getIndex.+--+{-# INLINE getIndexRev #-}+getIndexRev :: -- (MonadIO m, Storable a) =>+    Ring a -> Int -> m a+getIndexRev = undefined++-------------------------------------------------------------------------------+-- Size+-------------------------------------------------------------------------------++-- | /O(1)/ Get the byte length of the array.+--+-- /Unimplemented/+{-# INLINE byteLength #-}+byteLength :: Ring a -> Int+byteLength = undefined++-- | /O(1)/ Get the length of the array i.e. the number of elements in the+-- array.+--+-- Note that 'byteLength' is less expensive than this operation, as 'length'+-- involves a costly division operation.+--+-- /Unimplemented/+{-# INLINE length #-}+length :: -- forall a. Storable a =>+    Ring a -> Int+length = undefined++-- | Get the total capacity of an array. An array may have space reserved+-- beyond the current used length of the array.+--+-- /Pre-release/+{-# INLINE byteCapacity #-}+byteCapacity :: Ring a -> Int+byteCapacity = undefined++-- | The remaining capacity in the array for appending more elements without+-- reallocation.+--+-- /Pre-release/+{-# INLINE bytesFree #-}+bytesFree :: Ring a -> Int+bytesFree = undefined++-------------------------------------------------------------------------------+-- Unfolds+-------------------------------------------------------------------------------++-- XXX We can read the ring in a loop and use "take" to restrict the number of+-- elements to be taken.+--+-- | Read n elements from the ring starting at the supplied ring head. If n is+-- more than the ring size it keeps reading the ring in a circular fashion.+--+-- If the ring is not full the user must ensure than n is less than or equal to+-- the number of valid elements in the ring.+--+-- /Internal/+{-# INLINE_NORMAL read #-}+read :: forall m a. (MonadIO m, Storable a) => Unfold m (Ring a, Ptr a, Int) a+read = Unfold step return++    where++    step (rb, rh, n) = do         if n <= 0-        then Fold.Done <$> createRing 0+        then do+            liftIO $ touchForeignPtr (ringStart rb)+            return Stop         else do-            rb <- createRing n-            return $ Fold.Partial $ Tuple' rb (0 :: Int)+            x <- liftIO $ peek rh+            let rh1 = advance rb rh+            return $ Yield x (rb, rh1, n - 1) -    step (Tuple' rb cnt) x = do-        rh1 <- liftIO $ unsafeInsertRingWith rb x-        return $ Fold.Partial $ Tuple' (rb {ringHead = rh1}) (cnt + 1)+-- | Unfold a ring array into a stream in reverse order.+--+-- /Unimplemented/+{-# INLINE_NORMAL readRev #-}+readRev :: -- forall m a. (MonadIO m, Storable a) =>+    Unfold m (MutArray a) a+readRev = undefined -    extract (Tuple' rb@Ring{..} cnt) =-        return $-            if cnt < ringMax-            then Ring ringArr 0 ringHead-            else rb+-------------------------------------------------------------------------------+-- Stream of arrays+------------------------------------------------------------------------------- +-- XXX Move this module to a lower level Ring/Type module and move ringsOf to a+-- higher level ring module where we can import "scan".++-- | @ringsOf n stream@ groups the input stream into a stream of+-- ring arrays of size n. Each ring is a sliding window of size n.+--+-- /Unimplemented/+{-# INLINE_NORMAL ringsOf #-}+ringsOf :: -- forall m a. (MonadIO m, Storable a) =>+    Int -> Stream m a -> Stream m (MutArray a)+ringsOf = undefined -- Stream.scan (writeN n)+ ---------------------------------------------------------------------------------- Modification+-- Casting ------------------------------------------------------------------------------- --- XXX This is safe--- Take the ring head and return the new ring head.-{-# INLINE unsafeInsertRingWith #-}-unsafeInsertRingWith :: Ring a -> a -> IO Int-unsafeInsertRingWith Ring{..} x = do-    assertM(ringMax >= 1)-    assertM(ringHead < ringMax)-    putIndexUnsafe ringHead ringArr x-    let rh1 = ringHead + 1-        next = if rh1 == ringMax then 0 else rh1-    return next+-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The array size must be a multiple of the size of type @b@.+--+-- /Unimplemented/+--+castUnsafe :: Ring a -> Ring b+castUnsafe = undefined --- | Move the ring head clockwise (+ve adj) or counter clockwise (-ve adj) by--- the given amount.-{-# INLINE seek #-}-seek :: MonadIO m => Int -> Ring a -> m (Ring a)-seek adj rng@Ring{..}-    | ringMax > 0 = liftIO $ do-        -- XXX try avoiding mod when in bounds-        let idx1 = ringHead + adj-            next = mod idx1 ringMax-        return $ Ring ringArr next ringMax-    | otherwise = pure rng+-- | Cast an @Array a@ into an @Array Word8@.+--+-- /Unimplemented/+--+asBytes :: Ring a -> Ring Word8+asBytes = castUnsafe +-- | Cast an array having elements of type @a@ into an array having elements of+-- type @b@. The length of the array should be a multiple of the size of the+-- target element otherwise 'Nothing' is returned.+--+-- /Pre-release/+--+cast :: forall a b. Storable b => Ring a -> Maybe (Ring b)+cast arr =+    let len = byteLength arr+        r = len `mod` STORABLE_SIZE_OF(b)+     in if r /= 0+        then Nothing+        else Just $ castUnsafe arr+ ---------------------------------------------------------------------------------- Conversion+-- Equality ------------------------------------------------------------------------------- --- | @toMutArray rignHeadAdjustment lengthToRead ring@.--- Convert the ring into a boxed mutable array. Note that the returned MutArray--- may share the same underlying memory as the Ring.-{-# INLINE toMutArray #-}-toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)-toMutArray adj n Ring{..} = do-    let len = min ringMax n-    let idx = mod (ringHead + adj) ringMax-        end = idx + len-    if end <= ringMax-    then-        -- putSliceUnsafe ringArr idx arr1 0 len-        return $ ringArr { arrStart = idx, arrLen = len }-    else do-        -- XXX Just swap the elements in the existing ring and return the-        -- same array without reallocation.-        arr <- liftIO $ new len-        arr1 <- uninit arr len-        putSliceUnsafe ringArr idx arr1 0 (ringMax - idx)-        putSliceUnsafe ringArr 0 arr1 (ringMax - idx) (end - ringMax)-        return arr1+-- XXX remove all usage of unsafeInlineIO+--+-- | Like 'unsafeEqArray' but compares only N bytes instead of entire length of+-- the ring buffer. This is unsafe because the ringHead Ptr is not checked to+-- be in range.+{-# INLINE unsafeEqArrayN #-}+unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool+unsafeEqArrayN Ring{..} rh A.Array{..} nBytes+    | nBytes < 0 = error "unsafeEqArrayN: n should be >= 0"+    | nBytes == 0 = True+    | otherwise = unsafeInlineIO $ check (castPtr rh) 0 --- This would be theoretically slower than toMutArray because of a branch--- introduced for each element in the second half of the ring.+    where --- | Seek by n and then read the entire ring. Use 'take' on the stream to--- restrict the reads.-toStreamWith :: Int -> Ring a -> Stream m a-toStreamWith = undefined-{--toStreamWith n Ring{..}-    | ringMax > 0 = concatEffect $ liftIO $ do-        idx <- readIORef ringHead-        let idx1 = idx + adj-            next = mod idx1 ringMax-            s1 = undefined  -- stream initial slice-            s2 = undefined  -- stream next slice-        return (s1 `Stream.append` s2)-    | otherwise = Stream.nil--}+    w8Contents = arrContents++    check p i = do+        (relem :: Word8) <- peek p+        aelem <- peekAt i w8Contents+        if relem == aelem+        then go (p `plusPtr` 1) (i + 1)+        else return False++    go p i+        | i == nBytes = return True+        | castPtr p == ringBound =+            go (castPtr (unsafeForeignPtrToPtr ringStart)) i+        | castPtr p == rh = touchForeignPtr ringStart >> return True+        | otherwise = check p i++-- XXX This is not modular. We should probably just convert the array and the+-- ring buffer to streams and compare the two streams. Need to check perf+-- though.++-- | Byte compare the entire length of ringBuffer with the given array,+-- starting at the supplied ringHead pointer.  Returns true if the Array and+-- the ringBuffer have identical contents.+--+-- This is unsafe because the ringHead Ptr is not checked to be in range. The+-- supplied array must be equal to or bigger than the ringBuffer, ARRAY BOUNDS+-- ARE NOT CHECKED.+{-# INLINE unsafeEqArray #-}+unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool+unsafeEqArray Ring{..} rh A.Array{..} =+    unsafeInlineIO $ check (castPtr rh) 0++    where++    w8Contents = arrContents++    check p i = do+        (relem :: Word8) <- peek p+        aelem <- peekAt i w8Contents+        if relem == aelem+        then go (p `plusPtr` 1) (i + 1)+        else return False++    go p i+        | castPtr p ==+              ringBound = go (castPtr (unsafeForeignPtrToPtr ringStart)) i+        | castPtr p == rh = touchForeignPtr ringStart >> return True+        | otherwise = check p i++-------------------------------------------------------------------------------+-- Folding+-------------------------------------------------------------------------------++-- XXX We can unfold it into a stream and fold the stream instead.+-- XXX use MonadIO+--+-- | Fold the buffer starting from ringStart up to the given 'Ptr' using a pure+-- step function. This is useful to fold the items in the ring when the ring is+-- not full. The supplied pointer is usually the end of the ring.+--+-- Unsafe because the supplied Ptr is not checked to be in range.+{-# INLINE unsafeFoldRing #-}+unsafeFoldRing :: forall a b. Storable a+    => Ptr a -> (b -> a -> b) -> b -> Ring a -> b+unsafeFoldRing ptr f z Ring{..} =+    let !res = unsafeInlineIO $ withForeignPtr ringStart $ \p ->+                    go z p ptr+    in res+    where+      go !acc !p !q+        | p == q = return acc+        | otherwise = do+            x <- peek p+            go (f acc x) (PTR_NEXT(p,a)) q++-- XXX Can we remove MonadIO here?+withForeignPtrM :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b+withForeignPtrM fp fn = do+    r <- fn $ unsafeForeignPtrToPtr fp+    liftIO $ touchForeignPtr fp+    return r++-- | Like unsafeFoldRing but with a monadic step function.+{-# INLINE unsafeFoldRingM #-}+unsafeFoldRingM :: forall m a b. (MonadIO m, Storable a)+    => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+unsafeFoldRingM ptr f z Ring {..} =+    withForeignPtrM ringStart $ \x -> go z x ptr+  where+    go !acc !start !end+        | start == end = return acc+        | otherwise = do+            let !x = unsafeInlineIO $ peek start+            acc1 <- f acc x+            go acc1 (PTR_NEXT(start,a)) end++-- | Fold the entire length of a ring buffer starting at the supplied ringHead+-- pointer.  Assuming the supplied ringHead pointer points to the oldest item,+-- this would fold the ring starting from the oldest item to the newest item in+-- the ring.+--+-- Note, this will crash on ring of 0 size.+--+{-# INLINE unsafeFoldRingFullM #-}+unsafeFoldRingFullM :: forall m a b. (MonadIO m, Storable a)+    => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+unsafeFoldRingFullM rh f z rb@Ring {..} =+    withForeignPtrM ringStart $ \_ -> go z rh+  where+    go !acc !start = do+        let !x = unsafeInlineIO $ peek start+        acc' <- f acc x+        let ptr = advance rb start+        if ptr == rh+            then return acc'+            else go acc' ptr++-- | Fold @Int@ items in the ring starting at @Ptr a@.  Won't fold more+-- than the length of the ring.+--+-- Note, this will crash on ring of 0 size.+--+{-# INLINE unsafeFoldRingNM #-}+unsafeFoldRingNM :: forall m a b. (MonadIO m, Storable a)+    => Int -> Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b+unsafeFoldRingNM count rh f z rb@Ring {..} =+    withForeignPtrM ringStart $ \_ -> go count z rh++    where++    go 0 acc _ = return acc+    go !n !acc !start = do+        let !x = unsafeInlineIO $ peek start+        acc' <- f acc x+        let ptr = advance rb start+        if ptr == rh || n == 0+            then return acc'+            else go (n - 1) acc' ptr++data Tuple4' a b c d = Tuple4' !a !b !c !d deriving Show++-- | Like slidingWindow but also provides the entire ring contents as an Array.+-- The array reflects the state of the ring after inserting the incoming+-- element.+--+-- IMPORTANT NOTE: The ring is mutable, therefore, the result of @(m (Array+-- a))@ action depends on when it is executed. It does not capture the sanpshot+-- of the ring at a particular time.+{-# INLINE slidingWindowWith #-}+slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unbox a)+    => Int -> Fold m ((a, Maybe a), m (MutArray a)) b -> Fold m a b+slidingWindowWith n (Fold step1 initial1 extract1 final1) =+    Fold step initial extract final++    where++    initial = do+        if n <= 0+        then error "Window size must be > 0"+        else do+            r <- initial1+            (rb, rh) <- liftIO $ new n+            return $+                case r of+                    Partial s -> Partial $ Tuple4' rb rh (0 :: Int) s+                    Done b -> Done b++    toArray foldRing rb rh = do+        -- Using unpinned array here instead of pinned+        arr <- liftIO $ MA.new n+        let snoc' b a = liftIO $ MA.snocUnsafe b a+        foldRing rh snoc' arr rb++    step (Tuple4' rb rh i st) a+        | i < n = do+            rh1 <- liftIO $ unsafeInsert rb rh a+            liftIO $ touchForeignPtr (ringStart rb)+            let action = toArray unsafeFoldRingM rb (PTR_NEXT(rh, a))+            r <- step1 st ((a, Nothing), action)+            return $+                case r of+                    Partial s -> Partial $ Tuple4' rb rh1 (i + 1) s+                    Done b -> Done b+        | otherwise = do+            old <- liftIO $ peek rh+            rh1 <- liftIO $ unsafeInsert rb rh a+            liftIO $ touchForeignPtr (ringStart rb)+            r <- step1 st ((a, Just old), toArray unsafeFoldRingFullM rb rh1)+            return $+                case r of+                    Partial s -> Partial $ Tuple4' rb rh1 (i + 1) s+                    Done b -> Done b++    extract (Tuple4' _ _ _ st) = extract1 st++    final (Tuple4' _ _ _ st) = final1 st++-- | @slidingWindow collector@ is an incremental sliding window+-- fold that does not require all the intermediate elements in a computation.+-- This maintains @n@ elements in the window, when a new element comes it slides+-- out the oldest element and the new element along with the old element are+-- supplied to the collector fold.+--+-- The 'Maybe' type is for the case when initially the window is filling and+-- there is no old element.+--+{-# INLINE slidingWindow #-}+slidingWindow :: forall m a b. (MonadIO m, Storable a, Unbox a)+    => Int -> Fold m (a, Maybe a) b -> Fold m a b+slidingWindow n f = slidingWindowWith n (lmap fst f)
+ src/Streamly/Internal/Data/Ring/Generic.hs view
@@ -0,0 +1,185 @@+-- |+-- Module      : Streamly.Internal.Data.Ring.Generic+-- Copyright   : (c) 2021 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--++module Streamly.Internal.Data.Ring.Generic+    ( Ring(..)++    -- * Generation+    , createRing+    , writeLastN++    -- * Modification+    , seek+    , unsafeInsertRingWith++    -- * Conversion+    , toMutArray+    , copyToMutArray+    , toStreamWith+    ) where++#include "assert.hs"++import Control.Monad.IO.Class (liftIO, MonadIO)+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.MutArray.Generic (MutArray(..))++-- import qualified Streamly.Internal.Data.Stream.Type as Stream+import qualified Streamly.Internal.Data.Fold.Type as Fold+import qualified Streamly.Internal.Data.MutArray.Generic as MutArray++-- XXX Use MutableArray rather than keeping a MutArray here.+data Ring a = Ring+    { ringArr :: MutArray a+    -- XXX We can keep the current fill amount, Or we can keep a count of total+    -- elements inserted and compute ring head as well using mod on that,+    -- assuming it won't overflow. But mod could be expensive.+    , ringHead :: !Int -- current index to be over-written+    , ringMax :: !Int  -- first index beyond allocated memory+    }++-------------------------------------------------------------------------------+-- Generation+-------------------------------------------------------------------------------++-- XXX If we align the ringMax to nearest power of two then computation of the+-- index to write could be cheaper.+{-# INLINE createRing #-}+createRing :: MonadIO m => Int -> m (Ring a)+createRing count = liftIO $ do+    arr <- MutArray.new count+    arr1 <- MutArray.uninit arr count+    return (Ring+        { ringArr = arr1+        , ringHead = 0+        , ringMax = count+        })+++-- | Note that it is not safe to return a reference to the mutable Ring using a+-- scan as the Ring is continuously getting mutated. You could however copy out+-- the Ring.+{-# INLINE writeLastN #-}+writeLastN :: MonadIO m => Int -> Fold m a (Ring a)+writeLastN n = Fold step initial extract extract++    where++    initial = do+        if n <= 0+        then Fold.Done <$> createRing 0+        else do+            rb <- createRing n+            return $ Fold.Partial $ Tuple' rb (0 :: Int)++    step (Tuple' rb cnt) x = do+        rh1 <- liftIO $ unsafeInsertRingWith rb x+        return $ Fold.Partial $ Tuple' (rb {ringHead = rh1}) (cnt + 1)++    extract (Tuple' rb@Ring{..} cnt) =+        return $+            if cnt < ringMax+            then Ring ringArr 0 ringHead+            else rb++-------------------------------------------------------------------------------+-- Modification+-------------------------------------------------------------------------------++-- XXX This is safe+-- Take the ring head and return the new ring head.+{-# INLINE unsafeInsertRingWith #-}+unsafeInsertRingWith :: Ring a -> a -> IO Int+unsafeInsertRingWith Ring{..} x = do+    assertM(ringMax >= 1)+    assertM(ringHead < ringMax)+    MutArray.putIndexUnsafe ringHead ringArr x+    let rh1 = ringHead + 1+        next = if rh1 == ringMax then 0 else rh1+    return next++-- | Move the ring head clockwise (+ve adj) or counter clockwise (-ve adj) by+-- the given amount.+{-# INLINE seek #-}+seek :: MonadIO m => Int -> Ring a -> m (Ring a)+seek adj rng@Ring{..}+    | ringMax > 0 = liftIO $ do+        -- XXX try avoiding mod when in bounds+        let idx1 = ringHead + adj+            next = mod idx1 ringMax+        return $ Ring ringArr next ringMax+    | otherwise = pure rng++-------------------------------------------------------------------------------+-- Conversion+-------------------------------------------------------------------------------++-- | @toMutArray rignHeadAdjustment lengthToRead ring@.+-- Convert the ring into a boxed mutable array. Note that the returned MutArray+-- shares the same underlying memory as the Ring, the user of this API needs to+-- ensure that the ring is not mutated during and after the conversion.+--+{-# INLINE toMutArray #-}+toMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)+toMutArray adj n Ring{..} =+    -- XXX for empty Ring it will raise an Exception: divide by zero+    if ringMax <= 0+    then MutArray.nil+    else do+        let len = min ringMax n+        let idx = mod (ringHead + adj) ringMax+            end = idx + len+        if end <= ringMax+        then+            return $ ringArr { arrStart = idx, arrLen = len }+        else do+            -- XXX Just swap the elements in the existing ring and return the+            -- same array without reallocation.+            arr <- liftIO $ MutArray.new len+            arr1 <- MutArray.uninit arr len+            MutArray.putSliceUnsafe ringArr idx arr1 0 (ringMax - idx)+            MutArray.putSliceUnsafe ringArr 0 arr1 (ringMax - idx) (end - ringMax)+            return arr1++-- | Copy out the mutable ring to a mutable Array.+{-# INLINE copyToMutArray #-}+copyToMutArray :: MonadIO m => Int -> Int -> Ring a -> m (MutArray a)+copyToMutArray adj n Ring{..} = do+    if ringMax <= 0+    then MutArray.nil+    else do+        let len = min ringMax n+        let idx = mod (ringHead + adj) ringMax+            end = idx + len+        arr <- MutArray.new len+        arr1 <- MutArray.uninit arr len+        MutArray.putSliceUnsafe ringArr idx arr1 0 (ringMax - idx)+        MutArray.putSliceUnsafe ringArr 0 arr1 (ringMax - idx) (end - ringMax)+        return arr1++-- This would be theoretically slower than toMutArray because of a branch+-- introduced for each element in the second half of the ring.++-- | Seek by n and then read the entire ring. Use 'take' on the stream to+-- restrict the reads.+toStreamWith :: Int -> Ring a -> Stream m a+toStreamWith = undefined+{-+toStreamWith n Ring{..}+    | ringMax > 0 = concatEffect $ liftIO $ do+        idx <- readIORef ringHead+        let idx1 = idx + adj+            next = mod idx1 ringMax+            s1 = undefined  -- stream initial slice+            s2 = undefined  -- stream next slice+        return (s1 `Stream.append` s2)+    | otherwise = Stream.nil+-}
− src/Streamly/Internal/Data/Ring/Unboxed.hs
@@ -1,615 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Ring.Unboxed--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ A ring array is a circular mutable array.---- XXX Write benchmarks--- XXX Make the implementation similar to mutable array--- XXX Rename this module to Data.RingArray.Storable--module Streamly.Internal.Data.Ring.Unboxed-    ( Ring(..)--    -- * Construction-    , new-    , newRing-    , writeN--    , advance-    , moveBy-    , startOf--    -- * Random writes-    , unsafeInsert-    , slide-    , putIndex-    , modifyIndex--    -- * Unfolds-    , read-    , readRev--    -- * Random reads-    , getIndex-    , getIndexUnsafe-    , getIndexRev--    -- * Size-    , length-    , byteLength-    -- , capacity-    , byteCapacity-    , bytesFree--    -- * Casting-    , cast-    , castUnsafe-    , asBytes-    , fromArray--    -- * Folds-    , unsafeFoldRing-    , unsafeFoldRingM-    , unsafeFoldRingFullM-    , unsafeFoldRingNM--    -- * Stream of Arrays-    , ringsOf--    -- * Fast Byte Comparisons-    , unsafeEqArray-    , unsafeEqArrayN--    , slidingWindow-    , slidingWindowWith-    ) where--#include "ArrayMacros.h"-#include "inline.hs"--import Control.Exception (assert)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Word (Word8)-import Foreign.Storable-import Foreign.ForeignPtr (ForeignPtr, withForeignPtr, touchForeignPtr)-import Foreign.ForeignPtr.Unsafe (unsafeForeignPtrToPtr)-import Foreign.Ptr (plusPtr, minusPtr, castPtr)-import Streamly.Internal.Data.Unboxed as Unboxed (Unbox, peekWith)-import GHC.ForeignPtr (mallocPlainForeignPtrAlignedBytes)-import GHC.Ptr (Ptr(..))-import Streamly.Internal.Data.Array.Mut.Type (MutArray)-import Streamly.Internal.Data.Fold.Type (Fold(..), Step(..), lmap)-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))-import Streamly.Internal.Data.Unfold.Type (Unfold(..))-import Streamly.Internal.System.IO (unsafeInlineIO)--import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Array.Type as A--import Prelude hiding (length, concat, read)---- $setup--- >>> :m--- >>> import qualified Streamly.Internal.Data.Ring.Unboxed as Ring---- | A ring buffer is a mutable array of fixed size. Initially the array is--- empty, with ringStart pointing at the start of allocated memory. We call the--- next location to be written in the ring as ringHead. Initially ringHead ==--- ringStart. When the first item is added, ringHead points to ringStart +--- sizeof item. When the buffer becomes full ringHead would wrap around to--- ringStart. When the buffer is full, ringHead always points at the oldest--- item in the ring and the newest item added always overwrites the oldest--- item.------ When using it we should keep in mind that a ringBuffer is a mutable data--- structure. We should not leak out references to it for immutable use.----data Ring a = Ring-    { ringStart :: {-# UNPACK #-} !(ForeignPtr a) -- first address-    , ringBound :: {-# UNPACK #-} !(Ptr a)        -- first address beyond allocated memory-    }------------------------------------------------------------------------------------ Construction------------------------------------------------------------------------------------ | Get the first address of the ring as a pointer.-startOf :: Ring a -> Ptr a-startOf = unsafeForeignPtrToPtr . ringStart---- | Create a new ringbuffer and return the ring buffer and the ringHead.--- Returns the ring and the ringHead, the ringHead is same as ringStart.-{-# INLINE new #-}-new :: forall a. Storable a => Int -> IO (Ring a, Ptr a)-new count = do-    let size = count * max 1 (sizeOf (undefined :: a))-    fptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: a))-    let p = unsafeForeignPtrToPtr fptr-    return (Ring-        { ringStart = fptr-        , ringBound = p `plusPtr` size-        }, p)---- XXX Rename this to "new".------ | @newRing count@ allocates an empty array that can hold 'count' items.  The--- memory of the array is uninitialized and the allocation is aligned as per--- the 'Storable' instance of the type.------ /Unimplemented/-{-# INLINE newRing #-}-newRing :: Int -> m (Ring a)-newRing = undefined---- | Advance the ringHead by 1 item, wrap around if we hit the end of the--- array.-{-# INLINE advance #-}-advance :: forall a. Storable a => Ring a -> Ptr a -> Ptr a-advance Ring{..} ringHead =-    let ptr = PTR_NEXT(ringHead,a)-    in if ptr <  ringBound-       then ptr-       else unsafeForeignPtrToPtr ringStart---- | Move the ringHead by n items. The direction depends on the sign on whether--- n is positive or negative. Wrap around if we hit the beginning or end of the--- array.-{-# INLINE moveBy #-}-moveBy :: forall a. Storable a => Int -> Ring a -> Ptr a -> Ptr a-moveBy by Ring {..} ringHead = ringStartPtr `plusPtr` advanceFromHead--    where--    elemSize = STORABLE_SIZE_OF(a)-    ringStartPtr = unsafeForeignPtrToPtr ringStart-    lenInBytes = ringBound `minusPtr` ringStartPtr-    offInBytes = ringHead `minusPtr` ringStartPtr-    len = assert (lenInBytes `mod` elemSize == 0) $ lenInBytes `div` elemSize-    off = assert (offInBytes `mod` elemSize == 0) $ offInBytes `div` elemSize-    advanceFromHead = (off + by `mod` len) * elemSize---- XXX Move the writeLastN from array module here.------ | @writeN n@ is a rolling fold that keeps the last n elements of the stream--- in a ring array.------ /Unimplemented/-{-# INLINE writeN #-}-writeN :: -- (Storable a, MonadIO m) =>-    Int -> Fold m a (Ring a)-writeN = undefined------------------------------------------------------------------------------------ Conversions------------------------------------------------------------------------------------ | Cast a mutable array to a ring array.-fromArray :: MutArray a -> Ring a-fromArray = undefined------------------------------------------------------------------------------------ Conversion to/from array------------------------------------------------------------------------------------ | Modify a given index of a ring array using a modifier function.------ /Unimplemented/-modifyIndex :: -- forall m a b. (MonadIO m, Storable a) =>-    Ring a -> Int -> (a -> (a, b)) -> m b-modifyIndex = undefined---- | /O(1)/ Write the given element at the given index in the ring array.--- Performs in-place mutation of the array.------ >>> putIndex arr ix val = Ring.modifyIndex arr ix (const (val, ()))------ /Unimplemented/-{-# INLINE putIndex #-}-putIndex :: -- (MonadIO m, Storable a) =>-    Ring a -> Int -> a -> m ()-putIndex = undefined---- | Insert an item at the head of the ring, when the ring is full this--- replaces the oldest item in the ring with the new item. This is unsafe--- beause ringHead supplied is not verified to be within the Ring. Also,--- the ringStart foreignPtr must be guaranteed to be alive by the caller.-{-# INLINE unsafeInsert #-}-unsafeInsert :: Storable a => Ring a -> Ptr a -> a -> IO (Ptr a)-unsafeInsert rb ringHead newVal = do-    poke ringHead newVal-    -- touchForeignPtr (ringStart rb)-    return $ advance rb ringHead---- | Insert an item at the head of the ring, when the ring is full this--- replaces the oldest item in the ring with the new item.------ /Unimplemented/-slide :: -- forall m a. (MonadIO m, Storable a) =>-    Ring a -> a -> m (Ring a)-slide = undefined------------------------------------------------------------------------------------ Random reads------------------------------------------------------------------------------------ | Return the element at the specified index without checking the bounds.------ Unsafe because it does not check the bounds of the ring array.-{-# INLINE_NORMAL getIndexUnsafe #-}-getIndexUnsafe :: -- forall m a. (MonadIO m, Storable a) =>-    Ring a -> Int -> m a-getIndexUnsafe = undefined---- | /O(1)/ Lookup the element at the given index. Index starts from 0.----{-# INLINE getIndex #-}-getIndex :: -- (MonadIO m, Storable a) =>-    Ring a -> Int -> m a-getIndex = undefined---- | /O(1)/ Lookup the element at the given index from the end of the array.--- Index starts from 0.------ Slightly faster than computing the forward index and using getIndex.----{-# INLINE getIndexRev #-}-getIndexRev :: -- (MonadIO m, Storable a) =>-    Ring a -> Int -> m a-getIndexRev = undefined------------------------------------------------------------------------------------ Size------------------------------------------------------------------------------------ | /O(1)/ Get the byte length of the array.------ /Unimplemented/-{-# INLINE byteLength #-}-byteLength :: Ring a -> Int-byteLength = undefined---- | /O(1)/ Get the length of the array i.e. the number of elements in the--- array.------ Note that 'byteLength' is less expensive than this operation, as 'length'--- involves a costly division operation.------ /Unimplemented/-{-# INLINE length #-}-length :: -- forall a. Storable a =>-    Ring a -> Int-length = undefined---- | Get the total capacity of an array. An array may have space reserved--- beyond the current used length of the array.------ /Pre-release/-{-# INLINE byteCapacity #-}-byteCapacity :: Ring a -> Int-byteCapacity = undefined---- | The remaining capacity in the array for appending more elements without--- reallocation.------ /Pre-release/-{-# INLINE bytesFree #-}-bytesFree :: Ring a -> Int-bytesFree = undefined------------------------------------------------------------------------------------ Unfolds------------------------------------------------------------------------------------ XXX We can read the ring in a loop and use "take" to restrict the number of--- elements to be taken.------ | Read n elements from the ring starting at the supplied ring head. If n is--- more than the ring size it keeps reading the ring in a circular fashion.------ If the ring is not full the user must ensure than n is less than or equal to--- the number of valid elements in the ring.------ /Internal/-{-# INLINE_NORMAL read #-}-read :: forall m a. (MonadIO m, Storable a) => Unfold m (Ring a, Ptr a, Int) a-read = Unfold step return--    where--    step (rb, rh, n) = do-        if n <= 0-        then do-            liftIO $ touchForeignPtr (ringStart rb)-            return Stop-        else do-            x <- liftIO $ peek rh-            let rh1 = advance rb rh-            return $ Yield x (rb, rh1, n - 1)---- | Unfold a ring array into a stream in reverse order.------ /Unimplemented/-{-# INLINE_NORMAL readRev #-}-readRev :: -- forall m a. (MonadIO m, Storable a) =>-    Unfold m (MutArray a) a-readRev = undefined------------------------------------------------------------------------------------ Stream of arrays------------------------------------------------------------------------------------ XXX Move this module to a lower level Ring/Type module and move ringsOf to a--- higher level ring module where we can import "scan".---- | @ringsOf n stream@ groups the input stream into a stream of--- ring arrays of size n. Each ring is a sliding window of size n.------ /Unimplemented/-{-# INLINE_NORMAL ringsOf #-}-ringsOf :: -- forall m a. (MonadIO m, Storable a) =>-    Int -> Stream m a -> Stream m (MutArray a)-ringsOf = undefined -- Stream.scan (writeN n)------------------------------------------------------------------------------------ Casting------------------------------------------------------------------------------------ | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The array size must be a multiple of the size of type @b@.------ /Unimplemented/----castUnsafe :: Ring a -> Ring b-castUnsafe = undefined---- | Cast an @Array a@ into an @Array Word8@.------ /Unimplemented/----asBytes :: Ring a -> Ring Word8-asBytes = castUnsafe---- | Cast an array having elements of type @a@ into an array having elements of--- type @b@. The length of the array should be a multiple of the size of the--- target element otherwise 'Nothing' is returned.------ /Pre-release/----cast :: forall a b. Storable b => Ring a -> Maybe (Ring b)-cast arr =-    let len = byteLength arr-        r = len `mod` STORABLE_SIZE_OF(b)-     in if r /= 0-        then Nothing-        else Just $ castUnsafe arr------------------------------------------------------------------------------------ Equality------------------------------------------------------------------------------------ XXX remove all usage of unsafeInlineIO------ | Like 'unsafeEqArray' but compares only N bytes instead of entire length of--- the ring buffer. This is unsafe because the ringHead Ptr is not checked to--- be in range.-{-# INLINE unsafeEqArrayN #-}-unsafeEqArrayN :: Ring a -> Ptr a -> A.Array a -> Int -> Bool-unsafeEqArrayN Ring{..} rh A.Array{..} nBytes-    | nBytes < 0 = error "unsafeEqArrayN: n should be >= 0"-    | nBytes == 0 = True-    | otherwise = unsafeInlineIO $ check (castPtr rh) 0--    where--    w8Contents = arrContents--    check p i = do-        (relem :: Word8) <- peek p-        aelem <- peekWith w8Contents i-        if relem == aelem-        then go (p `plusPtr` 1) (i + 1)-        else return False--    go p i-        | i == nBytes = return True-        | castPtr p == ringBound =-            go (castPtr (unsafeForeignPtrToPtr ringStart)) i-        | castPtr p == rh = touchForeignPtr ringStart >> return True-        | otherwise = check p i---- XXX This is not modular. We should probably just convert the array and the--- ring buffer to streams and compare the two streams. Need to check perf--- though.---- | Byte compare the entire length of ringBuffer with the given array,--- starting at the supplied ringHead pointer.  Returns true if the Array and--- the ringBuffer have identical contents.------ This is unsafe because the ringHead Ptr is not checked to be in range. The--- supplied array must be equal to or bigger than the ringBuffer, ARRAY BOUNDS--- ARE NOT CHECKED.-{-# INLINE unsafeEqArray #-}-unsafeEqArray :: Ring a -> Ptr a -> A.Array a -> Bool-unsafeEqArray Ring{..} rh A.Array{..} =-    unsafeInlineIO $ check (castPtr rh) 0--    where--    w8Contents = arrContents--    check p i = do-        (relem :: Word8) <- peek p-        aelem <- peekWith w8Contents i-        if relem == aelem-        then go (p `plusPtr` 1) (i + 1)-        else return False--    go p i-        | castPtr p ==-              ringBound = go (castPtr (unsafeForeignPtrToPtr ringStart)) i-        | castPtr p == rh = touchForeignPtr ringStart >> return True-        | otherwise = check p i------------------------------------------------------------------------------------ Folding------------------------------------------------------------------------------------ XXX We can unfold it into a stream and fold the stream instead.--- XXX use MonadIO------ | Fold the buffer starting from ringStart up to the given 'Ptr' using a pure--- step function. This is useful to fold the items in the ring when the ring is--- not full. The supplied pointer is usually the end of the ring.------ Unsafe because the supplied Ptr is not checked to be in range.-{-# INLINE unsafeFoldRing #-}-unsafeFoldRing :: forall a b. Storable a-    => Ptr a -> (b -> a -> b) -> b -> Ring a -> b-unsafeFoldRing ptr f z Ring{..} =-    let !res = unsafeInlineIO $ withForeignPtr ringStart $ \p ->-                    go z p ptr-    in res-    where-      go !acc !p !q-        | p == q = return acc-        | otherwise = do-            x <- peek p-            go (f acc x) (PTR_NEXT(p,a)) q---- XXX Can we remove MonadIO here?-withForeignPtrM :: MonadIO m => ForeignPtr a -> (Ptr a -> m b) -> m b-withForeignPtrM fp fn = do-    r <- fn $ unsafeForeignPtrToPtr fp-    liftIO $ touchForeignPtr fp-    return r---- | Like unsafeFoldRing but with a monadic step function.-{-# INLINE unsafeFoldRingM #-}-unsafeFoldRingM :: forall m a b. (MonadIO m, Storable a)-    => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b-unsafeFoldRingM ptr f z Ring {..} =-    withForeignPtrM ringStart $ \x -> go z x ptr-  where-    go !acc !start !end-        | start == end = return acc-        | otherwise = do-            let !x = unsafeInlineIO $ peek start-            acc1 <- f acc x-            go acc1 (PTR_NEXT(start,a)) end---- | Fold the entire length of a ring buffer starting at the supplied ringHead--- pointer.  Assuming the supplied ringHead pointer points to the oldest item,--- this would fold the ring starting from the oldest item to the newest item in--- the ring.------ Note, this will crash on ring of 0 size.----{-# INLINE unsafeFoldRingFullM #-}-unsafeFoldRingFullM :: forall m a b. (MonadIO m, Storable a)-    => Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b-unsafeFoldRingFullM rh f z rb@Ring {..} =-    withForeignPtrM ringStart $ \_ -> go z rh-  where-    go !acc !start = do-        let !x = unsafeInlineIO $ peek start-        acc' <- f acc x-        let ptr = advance rb start-        if ptr == rh-            then return acc'-            else go acc' ptr---- | Fold @Int@ items in the ring starting at @Ptr a@.  Won't fold more--- than the length of the ring.------ Note, this will crash on ring of 0 size.----{-# INLINE unsafeFoldRingNM #-}-unsafeFoldRingNM :: forall m a b. (MonadIO m, Storable a)-    => Int -> Ptr a -> (b -> a -> m b) -> b -> Ring a -> m b-unsafeFoldRingNM count rh f z rb@Ring {..} =-    withForeignPtrM ringStart $ \_ -> go count z rh--    where--    go 0 acc _ = return acc-    go !n !acc !start = do-        let !x = unsafeInlineIO $ peek start-        acc' <- f acc x-        let ptr = advance rb start-        if ptr == rh || n == 0-            then return acc'-            else go (n - 1) acc' ptr--data Tuple4' a b c d = Tuple4' !a !b !c !d deriving Show---- | Like slidingWindow but also provides the entire ring contents as an Array.--- The array reflects the state of the ring after inserting the incoming--- element.------ IMPORTANT NOTE: The ring is mutable, therefore, the result of @(m (Array--- a))@ action depends on when it is executed. It does not capture the sanpshot--- of the ring at a particular time.-{-# INLINE slidingWindowWith #-}-slidingWindowWith :: forall m a b. (MonadIO m, Storable a, Unbox a)-    => Int -> Fold m ((a, Maybe a), m (MutArray a)) b -> Fold m a b-slidingWindowWith n (Fold step1 initial1 extract1) = Fold step initial extract--    where--    initial = do-        if n <= 0-        then error "Window size must be > 0"-        else do-            r <- initial1-            (rb, rh) <- liftIO $ new n-            return $-                case r of-                    Partial s -> Partial $ Tuple4' rb rh (0 :: Int) s-                    Done b -> Done b--    toArray foldRing rb rh = do-        arr <- liftIO $ MA.newPinned n-        let snoc' b a = liftIO $ MA.snocUnsafe b a-        foldRing rh snoc' arr rb--    step (Tuple4' rb rh i st) a-        | i < n = do-            rh1 <- liftIO $ unsafeInsert rb rh a-            liftIO $ touchForeignPtr (ringStart rb)-            let action = toArray unsafeFoldRingM rb (PTR_NEXT(rh, a))-            r <- step1 st ((a, Nothing), action)-            return $-                case r of-                    Partial s -> Partial $ Tuple4' rb rh1 (i + 1) s-                    Done b -> Done b-        | otherwise = do-            old <- liftIO $ peek rh-            rh1 <- liftIO $ unsafeInsert rb rh a-            liftIO $ touchForeignPtr (ringStart rb)-            r <- step1 st ((a, Just old), toArray unsafeFoldRingFullM rb rh1)-            return $-                case r of-                    Partial s -> Partial $ Tuple4' rb rh1 (i + 1) s-                    Done b -> Done b--    extract (Tuple4' _ _ _ st) = extract1 st---- | @slidingWindow collector@ is an incremental sliding window--- fold that does not require all the intermediate elements in a computation.--- This maintains @n@ elements in the window, when a new element comes it slides--- out the oldest element and the new element along with the old element are--- supplied to the collector fold.------ The 'Maybe' type is for the case when initially the window is filling and--- there is no old element.----{-# INLINE slidingWindow #-}-slidingWindow :: forall m a b. (MonadIO m, Storable a, Unbox a)-    => Int -> Fold m (a, Maybe a) b -> Fold m a b-slidingWindow n f = slidingWindowWith n (lmap fst f)
+ src/Streamly/Internal/Data/Serialize/TH.hs view
@@ -0,0 +1,524 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.Data.Serialize.TH+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Serialize.TH+    (+    -- Deriving+      deriveSerialize+    , deriveSerializeWith++    -- Utilities+    , module Streamly.Internal.Data.Serialize.TH.Bottom+    -- ** Common+    , module Streamly.Internal.Data.Serialize.TH.Common+    -- ** RecHeader+    , module Streamly.Internal.Data.Serialize.TH.RecHeader+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Data.List (foldl')+import Data.Word (Word16, Word32, Word64, Word8)++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Streamly.Internal.Data.Serialize.Type++import Streamly.Internal.Data.Unbox.TH+    ( DataCon(..)+    , DataType(..)+    , reifyDataType+    )++import qualified Streamly.Internal.Data.Serialize.TH.RecHeader as RecHeader++import Streamly.Internal.Data.Serialize.TH.Bottom+import Streamly.Internal.Data.Serialize.TH.Common+import Streamly.Internal.Data.Serialize.TH.RecHeader++--------------------------------------------------------------------------------+-- Domain specific helpers+--------------------------------------------------------------------------------++exprGetSize :: Q Exp -> (Int, Type) -> Q Exp+exprGetSize acc (i, _) = [|addSizeTo $(acc) $(varE (mkFieldName i))|]++getTagSize :: Int -> Int+getTagSize numConstructors+    | numConstructors == 1 = 0+    | fromIntegral (maxBound :: Word8) >= numConstructors = 1+    | fromIntegral (maxBound :: Word16) >= numConstructors = 2+    | fromIntegral (maxBound :: Word32) >= numConstructors = 4+    | fromIntegral (maxBound :: Word64) >= numConstructors = 8+    | otherwise = error "Too many constructors"++getTagType :: Int -> Name+getTagType numConstructors+    | numConstructors == 1 = error "No tag for 1 constructor"+    | fromIntegral (maxBound :: Word8) >= numConstructors = ''Word8+    | fromIntegral (maxBound :: Word16) >= numConstructors = ''Word16+    | fromIntegral (maxBound :: Word32) >= numConstructors = ''Word32+    | fromIntegral (maxBound :: Word64) >= numConstructors = ''Word64+    | otherwise = error "Too many constructors"++--------------------------------------------------------------------------------+-- Size+--------------------------------------------------------------------------------++getNameBaseLen :: Name -> Word8+getNameBaseLen cname =+    let x = length (nameBase cname)+     in if x > 63+        then error "Max Constructor Len: 63 characters"+        else fromIntegral x++conEncLen :: Name -> Word8+conEncLen cname = getNameBaseLen cname + 1++--------------------------------------------------------------------------------+-- Size+--------------------------------------------------------------------------------++mkSizeOfExpr :: Bool -> Bool -> TypeOfType -> Q Exp+mkSizeOfExpr True False tyOfTy =+    case tyOfTy of+        UnitType cname ->+            lamE+                [varP _acc, wildP]+                [|$(varE _acc) + $(litIntegral (conEncLen cname))|]+        TheType con ->+            lamE+                [varP _acc, varP _x]+                (caseE (varE _x) [matchCons (varE _acc) con])+        MultiType constructors -> sizeOfHeadDt constructors++    where++    sizeOfFields acc fields =+        foldl' exprGetSize acc $ zip [0..] fields++    matchCons acc (SimpleDataCon cname fields) =+        let a = litIntegral (conEncLen cname)+            b = sizeOfFields acc (map snd fields)+            expr = [|$(a) + $(b)|]+         in matchConstructor cname (length fields) expr++    sizeOfHeadDt cons =+        let acc = [|$(varE _acc)|]+         in lamE+                [varP _acc, varP _x]+                (caseE (varE _x) (fmap (matchCons acc) cons))++mkSizeOfExpr False False tyOfTy =+    case tyOfTy of+        UnitType _ -> lamE [varP _acc, wildP] [|$(varE _acc) + 1|]+        TheType con ->+            lamE+                [varP _acc, varP _x]+                (caseE (varE _x) [matchCons (varE _acc) con])+        MultiType constructors -> sizeOfHeadDt constructors++    where++    tagSizeExp numConstructors =+        litE (IntegerL (fromIntegral (getTagSize numConstructors)))++    -- XXX fields of the same type can be folded together, will reduce the code+    -- size when there are many fields of the same type.+    -- XXX const size fields can be calculated statically.+    -- XXX This can result in large compilation times due to nesting when there+    -- are many constructors. We can create a list and sum the list at run time+    -- to avoid that depending on the number of constructors. Or using a let+    -- statement for each case may help?+    -- appE (varE 'sum) (listE (acc : map (exprGetSize (litE (IntegerL 0))) (zip [0..] fields)))+    sizeOfFields acc fields =+        foldl' exprGetSize acc $ zip [0..] fields++    matchCons acc (SimpleDataCon cname fields) =+        let expr = sizeOfFields acc (map snd fields)+         in matchConstructor cname (length fields) expr++    -- XXX We fix VarSize for simplicity. Should be changed later.+    sizeOfHeadDt cons =+        let numCons = length cons+            acc = [|$(varE _acc) + $(tagSizeExp numCons)|]+         in lamE+                [varP _acc, varP _x]+                (caseE (varE _x) (fmap (matchCons acc) cons))++mkSizeOfExpr False True (TheType con) = RecHeader.mkRecSizeOfExpr con++mkSizeOfExpr _ _ _ = errorUnimplemented++mkSizeDec :: SerializeConfig -> Type -> [DataCon] -> Q [Dec]+mkSizeDec (SerializeConfig {..}) headTy cons = do+    -- INLINE on sizeOf actually worsens some benchmarks, and improves none+    sizeOfMethod <-+        mkSizeOfExpr+            cfgConstructorTagAsString+            cfgRecordSyntaxWithHeader+            (typeOfType headTy cons)+    pure+        ( maybe+            []+            (\x -> [PragmaD (InlineP 'addSizeTo x FunLike AllPhases)])+            cfgInlineSize+         ++ [FunD 'addSizeTo [Clause [] (NormalB sizeOfMethod) []]]+        )++--------------------------------------------------------------------------------+-- Peek+--------------------------------------------------------------------------------++mkDeserializeExpr :: Bool -> Bool -> Type -> TypeOfType -> Q Exp+mkDeserializeExpr True False headTy tyOfTy =+    case tyOfTy of+        UnitType cname -> deserializeConsExpr [SimpleDataCon cname []]+        TheType con -> deserializeConsExpr [con]+        MultiType cons -> deserializeConsExpr cons++  where++    deserializeConsExpr cons = do+        conLen <- newName "conLen"+        off1 <- newName "off1"+        [|do ($(varP off1), $(varP conLen) :: Word8) <-+                 deserializeAt+                     $(varE _initialOffset)+                     $(varE _arr)+                     $(varE _endOffset)+             $(multiIfE (map (guardCon conLen off1) cons ++ [catchAll]))|]++    catchAll =+        normalGE+            [|True|]+            [|error+               ("Found invalid tag while peeking (" +++                   $(lift (pprint headTy)) ++ ")")|]++    guardCon conLen off con@(SimpleDataCon cname _) = do+        let lenCname = getNameBaseLen cname+            tag = map c2w (nameBase cname)+        normalGE+            [|($(litIntegral lenCname) == $(varE conLen))+                   && $(xorCmp tag off _arr)|]+            [|let $(varP (makeI 0)) = $(varE off) + $(litIntegral lenCname)+               in $(mkDeserializeExprOne 'deserializeAt con)|]++mkDeserializeExpr False False headTy tyOfTy =+    case tyOfTy of+        -- Unit constructor+        UnitType cname ->+            [|pure ($(varE _initialOffset) + 1, $(conE cname))|]+        -- Product type+        TheType con ->+            letE+                [valD (varP (mkName "i0")) (normalB (varE _initialOffset)) []]+                (mkDeserializeExprOne 'deserializeAt con)+        -- Sum type+        MultiType cons -> do+            let lenCons = length cons+                tagType = getTagType lenCons+            doE+                [ bindS+                      (tupP [varP (mkName "i0"), varP _tag])+                      [|deserializeAt $(varE _initialOffset) $(varE _arr) $(varE _endOffset)|]+                , noBindS+                      (caseE+                           (sigE (varE _tag) (conT tagType))+                           (map peekMatch (zip [0 ..] cons) ++ [peekErr]))+                ]+  where+    peekMatch (i, con) =+        match+            (litP (IntegerL i))+            (normalB (mkDeserializeExprOne 'deserializeAt con)) []+    peekErr =+        match+            wildP+            (normalB+                -- XXX Print the tag+                 [|error+                       ("Found invalid tag while peeking (" +++                        $(lift (pprint headTy)) ++ ")")|])+            []++mkDeserializeExpr False True _ (TheType con@(SimpleDataCon _ fields)) = do+    deserializeWithKeys <- newName "deserializeWithKeys"+    updateFunc <- newName "updateFunc"+    updateFuncDec <- RecHeader.conUpdateFuncDec updateFunc fields+    deserializeWithKeysDec <-+        RecHeader.mkDeserializeKeysDec deserializeWithKeys updateFunc con+    letE+        (pure <$> (deserializeWithKeysDec ++ updateFuncDec))+        (RecHeader.mkRecDeserializeExpr+             _initialOffset+             _endOffset+             deserializeWithKeys+             con)++mkDeserializeExpr _ _ _ _ = errorUnimplemented++mkDeserializeDec :: SerializeConfig -> Type -> [DataCon] -> Q [Dec]+mkDeserializeDec (SerializeConfig {..}) headTy cons = do+    peekMethod <-+        mkDeserializeExpr+            cfgConstructorTagAsString+            cfgRecordSyntaxWithHeader+            headTy+            (typeOfType headTy cons)+    pure+        ( maybe+            []+            (\x -> [PragmaD (InlineP 'deserializeAt x FunLike AllPhases)])+            cfgInlineDeserialize+         +++            [ FunD+              'deserializeAt+              [ Clause+                    (if isUnitType cons && not cfgConstructorTagAsString+                         then [VarP _initialOffset, WildP, WildP]+                         else [VarP _initialOffset, VarP _arr, VarP _endOffset])+                    (NormalB peekMethod)+                    []+              ]+            ]+        )++--------------------------------------------------------------------------------+-- Poke+--------------------------------------------------------------------------------++mkSerializeExprTag :: Name -> Int -> Q Exp+mkSerializeExprTag tagType tagVal =+    [|serializeAt+          $(varE _initialOffset)+          $(varE _arr)+          $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]++mkSerializeExpr :: Bool -> Bool -> TypeOfType -> Q Exp+mkSerializeExpr True False tyOfTy =+    case tyOfTy of+        -- Unit type+        UnitType cname ->+            caseE+                (varE _val)+                [serializeDataCon (SimpleDataCon cname [])]+        -- Product type+        (TheType con) ->+            caseE+                (varE _val)+                [serializeDataCon con]+        -- Sum type+        (MultiType cons) ->+            caseE+                (varE _val)+                (map serializeDataCon cons)++    where++    serializeDataCon (SimpleDataCon cname fields) = do+        let tagLen8 = getNameBaseLen cname+            conEnc = tagLen8 : map c2w (nameBase cname)+        matchConstructor+            cname+            (length fields)+            (doE [ bindS+                       (varP (mkName "i0"))+                       (serializeW8List _initialOffset _arr conEnc)+                 , noBindS (mkSerializeExprFields 'serializeAt fields)+                 ])++mkSerializeExpr False False tyOfTy =+    case tyOfTy of+        -- Unit type+        UnitType _ -> [|pure ($(varE _initialOffset) + 1)|]+        -- Product type+        (TheType (SimpleDataCon cname fields)) ->+            letE+                [valD (varP (mkName "i0")) (normalB (varE _initialOffset)) []]+                (caseE+                     (varE _val)+                     [ matchConstructor+                           cname+                           (length fields)+                           (mkSerializeExprFields 'serializeAt fields)+                     ])+        -- Sum type+        (MultiType cons) -> do+            let lenCons = length cons+                tagType = getTagType lenCons+            caseE+                (varE _val)+                (map (\(tagVal, (SimpleDataCon cname fields)) ->+                          matchConstructor+                              cname+                              (length fields)+                              (doE [ bindS+                                         (varP (mkName "i0"))+                                         (mkSerializeExprTag tagType tagVal)+                                   , noBindS+                                         (mkSerializeExprFields+                                              'serializeAt+                                              fields)+                                   ]))+                     (zip [0 ..] cons))++mkSerializeExpr False True (TheType con) =+    RecHeader.mkRecSerializeExpr _initialOffset con++mkSerializeExpr _ _ _ = errorUnimplemented++mkSerializeDec :: SerializeConfig -> Type -> [DataCon] -> Q [Dec]+mkSerializeDec (SerializeConfig {..}) headTy cons = do+    pokeMethod <-+        mkSerializeExpr+            cfgConstructorTagAsString+            cfgRecordSyntaxWithHeader+            (typeOfType headTy cons)+    pure+        ( maybe+            []+            (\x -> [PragmaD (InlineP 'serializeAt x FunLike AllPhases)])+            cfgInlineSerialize+         +++            [FunD+                  'serializeAt+                  [ Clause+                        (if isUnitType cons && not cfgConstructorTagAsString+                             then [VarP _initialOffset, WildP, WildP]+                             else [VarP _initialOffset, VarP _arr, VarP _val])+                        (NormalB pokeMethod)+                        []+                  ]+            ]+        )++--------------------------------------------------------------------------------+-- Main+--------------------------------------------------------------------------------++-- | A general function to derive Serialize instances where you can control+-- which Constructors of the datatype to consider and what the Context for the+-- 'Serialize' instance would be.+--+-- Consider the datatype:+-- @+-- data CustomDataType a b+--     = CDTConstructor1+--     | CDTConstructor2 Bool+--     | CDTConstructor3 Bool b+--     deriving (Show, Eq)+-- @+--+-- Usage:+-- @+-- $(deriveSerializeInternal+--       serializeConfig+--       [AppT (ConT ''Serialize) (VarT (mkName "b"))]+--       (AppT+--            (AppT (ConT ''CustomDataType) (VarT (mkName "a")))+--            (VarT (mkName "b")))+--       [ DataCon 'CDTConstructor1 [] [] []+--       , DataCon 'CDTConstructor2 [] [] [(Nothing, (ConT ''Bool))]+--       , DataCon+--             'CDTConstructor3+--             []+--             []+--             [(Nothing, (ConT ''Bool)), (Nothing, (VarT (mkName "b")))]+--       ])+-- @+deriveSerializeInternal ::+       SerializeConfig -> Type -> [DataCon] -> ([Dec] -> Q [Dec]) -> Q [Dec]+deriveSerializeInternal conf headTy cons next = do+    sizeDec <- mkSizeDec conf headTy cons+    peekDec <- mkDeserializeDec conf headTy cons+    pokeDec <- mkSerializeDec conf headTy cons+    let methods = concat [sizeDec, peekDec, pokeDec]+    next methods++-- | @deriveSerializeWith config-modifier instance-dec@ generates a template+-- Haskell splice consisting of a declaration of a 'Serialize' instance.+-- @instance-dec@ is a template Haskell declaration splice consisting of a+-- standard Haskell instance declaration without the type class methods (e.g.+-- @[d|instance Serialize a => Serialize (Maybe a)|]@).+--+-- The type class methods for the given instance are generated according to the+-- supplied @config-modifier@ parameter. See 'SerializeConfig' for default+-- configuration settings.+--+-- Usage:+--+-- @+-- \$(deriveSerializeWith+--       ( inlineSerializeAt (Just NoInline)+--       . inlineDeserializeAt (Just NoInline)+--       )+--       [d|instance Serialize a => Serialize (Maybe a)|])+-- @+deriveSerializeWith ::+    (SerializeConfig -> SerializeConfig) -> Q [Dec] -> Q [Dec]+deriveSerializeWith modifier mDecs = do+    dec <- mDecs+    case dec of+        [InstanceD mo preds headTyWC []] -> do+            let headTy = unwrap dec headTyWC+            dt <- reifyDataType (getMainTypeName dec headTy)+            let cons = dtCons dt+            deriveSerializeInternal+                (modifier serializeConfig) headTy cons (next mo preds headTyWC)+        _ -> errorMessage dec++    where++    next mo preds headTyWC methods = pure [InstanceD mo preds headTyWC methods]++    errorMessage dec =+        error $ unlines+            [ "Error: deriveSerializeWith:"+            , ""+            , ">> " ++ pprint dec+            , ""+            , "The supplied declaration is not a valid instance declaration."+            , "Provide a valid Haskell instance declaration without a body."+            , ""+            , "Examples:"+            , "instance Serialize (Proxy a)"+            , "instance Serialize a => Serialize (Identity a)"+            , "instance Serialize (TableT Identity)"+            ]++    unwrap _ (AppT (ConT _) r) = r+    unwrap dec _ = errorMessage dec++    getMainTypeName dec = go++        where++        go (ConT nm) = nm+        go (AppT l _) = go l+        go _ = errorMessage dec++-- | Given an 'Serialize' instance declaration splice without the methods (e.g.+-- @[d|instance Serialize a => Serialize (Maybe a)|]@), generate an instance+-- declaration including all the type class method implementations.+--+-- >>> deriveSerialize = deriveSerializeWith id+--+-- Usage:+--+-- @+-- \$(deriveSerialize+--       [d|instance Serialize a => Serialize (Maybe a)|])+-- @+deriveSerialize :: Q [Dec] -> Q [Dec]+deriveSerialize = deriveSerializeWith id
+ src/Streamly/Internal/Data/Serialize/TH/Bottom.hs view
@@ -0,0 +1,475 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.Data.Serialize.TH.Bottom+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Serialize.TH.Bottom+    (+    -- ** Config+      SerializeConfig(..)+    , serializeConfig+    , inlineAddSizeTo+    , inlineSerializeAt+    , inlineDeserializeAt+    , encodeConstrNames+    , encodeRecordFields++    -- ** Other Utilities+    , TypeOfType(..)+    , typeOfType+    , SimpleDataCon(..)+    , simplifyDataCon+    , Field+    , mkFieldName+    , isUnitType+    , isRecordSyntax+    , c2w+    , wListToString+    , xorCmp+    , serializeW8List+    , litIntegral+    , litProxy+    , matchConstructor+    , openConstructor+    , makeI+    , makeN+    , makeA+    , int_w8+    , int_w32+    , w32_int+    , w8_int+    , _acc+    , _arr+    , _endOffset+    , _initialOffset+    , _x+    , _tag+    , _val+    , errorUnsupported+    , errorUnimplemented+    ) where++import Data.Maybe (isJust)+import Data.Char (chr, ord)+import Data.List (foldl')+import Data.Word (Word16, Word32, Word64, Word8)+import Data.Bits (Bits, (.|.), shiftL, zeroBits, xor)+import Streamly.Internal.System.IO (unsafeInlineIO)+import Streamly.Internal.Data.Unbox (Unbox)+import Data.Proxy (Proxy)++import Language.Haskell.TH+import Streamly.Internal.Data.Serialize.Type++import qualified Streamly.Internal.Data.Unbox as Unbox++import Streamly.Internal.Data.Unbox.TH (DataCon(..))++--------------------------------------------------------------------------------+-- Config+--------------------------------------------------------------------------------++-- NOTE: 'Nothing' is not eqvivalant to 'Just Inlinable'. Ie. Having no inline+-- specific pragma and having an Inlinable pragma are different. Having an+-- Inlinable pragma makes GHC put the code in the interface file whereas having+-- no inline specific pragma let's GHC decide whether to put the code in+-- interface file or not.++-- | Configuration to control how the 'Serialize' instance is generated. The+-- configuration is opaque and is modified by composing config modifier+-- functions, for example:+--+-- >>> (inlineSerializeAt (Just NoInline)) . (inlineSerializeAt (Just Inlinable))+--+-- The default configuration settings are:+--+-- * 'inlineAddSizeTo' Nothing+-- * 'inlineSerializeAt' (Just Inline)+-- * 'inlineDeserializeAt' (Just Inline)+--+-- The following experimental options are also available:+--+-- * 'encodeConstrNames' False+-- * 'encodeRecordFields' False+--+data SerializeConfig =+    SerializeConfig+        { cfgInlineSize :: Maybe Inline+        , cfgInlineSerialize :: Maybe Inline+        , cfgInlineDeserialize :: Maybe Inline+        , cfgConstructorTagAsString :: Bool+        , cfgRecordSyntaxWithHeader :: Bool+        }++-- | How should we inline the 'addSizeTo' function? The default is 'Nothing'+-- which means left to the compiler. Forcing inline on @addSizeTo@ function+-- actually worsens some benchmarks and improves none.+inlineAddSizeTo :: Maybe Inline -> SerializeConfig -> SerializeConfig+inlineAddSizeTo v cfg = cfg {cfgInlineSize = v}++-- XXX Should we make the default Inlinable instead?++-- | How should we inline the 'serialize' function? The default 'Just Inline'.+-- However, aggressive inlining can bloat the code and increase in compilation+-- times when there are big functions and too many nesting levels so you can+-- change it accordingly. A 'Nothing' value leaves the decision to the+-- compiler.+inlineSerializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+inlineSerializeAt v cfg = cfg {cfgInlineSerialize = v}++-- XXX Should we make the default Inlinable instead?++-- | How should we inline the 'deserialize' function? See guidelines in+-- 'inlineSerializeAt'.+inlineDeserializeAt :: Maybe Inline -> SerializeConfig -> SerializeConfig+inlineDeserializeAt v cfg = cfg {cfgInlineDeserialize = v}++-- | __Experimental__+--+-- In sum types, use Latin-1 encoded original constructor names rather than+-- binary values to identify constructors. This option is not applicable to+-- product types.+--+-- This option enables the following behavior:+--+-- * __Reordering__: Order of the fields can be changed without affecting+-- serialization.+-- * __Addition__: If a field is added in the new version, the old version of+-- the data type can still be deserialized by the new version. The new value+-- would never occur in the old one.+-- * __Deletion__: If a field is deleted in the new version, deserialization+-- of the old version will result in an error. TBD: We can possibly designate a+-- catch-all case to handle this scenario.+--+-- Note that if you change a type, change the semantics of a type, or delete a+-- field and add a new field with the same name, deserialization of old data+-- may result in silent unexpected behavior.+--+-- This option has to be the same on both encoding and decoding side.+--+-- The default is 'False'.+--+encodeConstrNames :: Bool -> SerializeConfig -> SerializeConfig+encodeConstrNames v cfg = cfg {cfgConstructorTagAsString = v}++-- XXX We can deserialize each field to Either, so if there is a+-- deserialization error in any field it can handled independently. Also, a+-- unique type/version identifier of the field (based on the versions of the+-- packages, full module name space + type identifier) can be serialized along+-- with the value for stricter compatibility, semantics checking. Or we can+-- store a type hash.++-- | __Experimental__+--+-- In explicit record types, use Latin-1 encoded record field names rather than+-- binary values to identify the record fields. Note that this option is not+-- applicable to sum types. Also, it does not work on a product type which is+-- not a record, because there are no field names to begin with.+--+-- This option enables the following behavior:+--+-- * __Reordering__: Order of the fields can be changed without affecting+-- serialization.+-- * __Addition__: If a 'Maybe' type field is added in the new version, the old+-- version of the data type can still be deserialized by the new version, the+-- field value in the older version is assumed to be 'Nothing'. If any other+-- type of field is added, deserialization of the older version results in an+-- error but only when that field is actually accessed in the deserialized+-- record.+-- * __Deletion__: If a field is deleted in the new version and it is+-- encountered in a previously serialized version then the field is discarded.+--+-- This option has to be the same on both encoding and decoding side.+--+-- There is a constant performance overhead proportional to the total length of+-- the record field names and the number of record fields.+--+-- The default is 'False'.+--+encodeRecordFields :: Bool -> SerializeConfig -> SerializeConfig+encodeRecordFields v cfg = cfg {cfgRecordSyntaxWithHeader = v}++serializeConfig :: SerializeConfig+serializeConfig =+    SerializeConfig+        { cfgInlineSize = Nothing+        , cfgInlineSerialize = Just Inline+        , cfgInlineDeserialize = Just Inline+        , cfgConstructorTagAsString = False+        , cfgRecordSyntaxWithHeader = False+        }++--------------------------------------------------------------------------------+-- Helpers+--------------------------------------------------------------------------------++type Field = (Maybe Name, Type)++_x :: Name+_x = mkName "x"++_acc :: Name+_acc = mkName "acc"++_arr :: Name+_arr = mkName "arr"++_tag :: Name+_tag = mkName "tag"++_initialOffset :: Name+_initialOffset = mkName "initialOffset"++_endOffset :: Name+_endOffset = mkName "endOffset"++_val :: Name+_val = mkName "val"++mkFieldName :: Int -> Name+mkFieldName i = mkName ("field" ++ show i)++makeI :: Int -> Name+makeI i = mkName $ "i" ++ show i++makeN :: Int -> Name+makeN i = mkName $ "n" ++ show i++makeA :: Int -> Name+makeA i = mkName $ "a" ++ show i++--------------------------------------------------------------------------------+-- Domain specific helpers+--------------------------------------------------------------------------------++openConstructor :: Name -> Int -> Q Pat+openConstructor cname numFields =+    conP cname (map varP (map mkFieldName [0 .. (numFields - 1)]))++matchConstructor :: Name -> Int -> Q Exp -> Q Match+matchConstructor cname numFields exp0 =+    match (openConstructor cname numFields) (normalB exp0) []++--------------------------------------------------------------------------------+-- Constructor types+--------------------------------------------------------------------------------++data SimpleDataCon =+    SimpleDataCon Name [Field]+    deriving (Eq)++simplifyDataCon :: DataCon -> SimpleDataCon+simplifyDataCon (DataCon cname _ _ fields) = SimpleDataCon cname fields++data TypeOfType+    = UnitType Name             -- 1 constructor and 1 field+    | TheType SimpleDataCon      -- 1 constructor and 1+ fields+    | MultiType [SimpleDataCon] -- 1+ constructors+    deriving (Eq)++typeOfType :: Type -> [DataCon] -> TypeOfType+typeOfType headTy [] =+    error+        ("Attempting to get size with no constructors (" +++         (pprint headTy) ++ ")")+typeOfType _ [DataCon cname _ _ []] = UnitType cname+typeOfType _ [con@(DataCon _ _ _ _)] = TheType $ simplifyDataCon con+typeOfType _ cons = MultiType $ map simplifyDataCon cons++isUnitType :: [DataCon] -> Bool+isUnitType [DataCon _ _ _ []] = True+isUnitType _ = False++isRecordSyntax :: SimpleDataCon -> Bool+isRecordSyntax (SimpleDataCon _ fields) = and (isJust . fst <$> fields)++--------------------------------------------------------------------------------+-- Type casting+--------------------------------------------------------------------------------++int_w8 :: Int -> Word8+int_w8 = fromIntegral++int_w32 :: Int -> Word32+int_w32 = fromIntegral++w8_w16 :: Word8 -> Word16+w8_w16 = fromIntegral++w8_w32 :: Word8 -> Word32+w8_w32 = fromIntegral++w8_w64 :: Word8 -> Word64+w8_w64 = fromIntegral++w8_int :: Word8 -> Int+w8_int = fromIntegral++w32_int :: Word32 -> Int+w32_int = fromIntegral++c2w :: Char -> Word8+c2w = fromIntegral . ord++wListToString :: [Word8] -> String+wListToString = fmap (chr . fromIntegral)++--------------------------------------------------------------------------------+-- Bit manipulation+--------------------------------------------------------------------------------++shiftAdd :: Bits a => (b -> a) -> [b] -> a+shiftAdd conv xs =+    foldl' (.|.) zeroBits $+    map (\(j, x) -> shiftL x (j * 8)) $ zip [0 ..] $ map conv xs++-- Note: This only works in little endian machines+-- TODO:+-- Instead of generating this via TH can't we write it directly in Haskell and+-- use that? Creating one comparison function for each deserialization may be+-- too much code and may not be necessary.+-- Benchmark both the implementations and check.+xorCmp :: [Word8] -> Name -> Name -> Q Exp+xorCmp tag off arr =+    case tagLen of+        x | x < 2 -> [|$(go8 0) == zeroBits|]+        x | x < 4 -> [|$(go16 0) == zeroBits|]+        x | x < 8 -> [|$(go32 0) == zeroBits|]+        _ -> [|$(go64 0) == zeroBits|]+  where+    tagLen = length tag+    go8 i | i >= tagLen = [|zeroBits|]+    go8 i = do+        let wIntegral = litIntegral i+        [|xor (unsafeInlineIO+                   (Unbox.peekAt+                        ($(varE off) + $(litIntegral i))+                        $(varE arr)))+              ($(wIntegral) :: Word8) .|.+          $(go8 (i + 1))|]+    go16 i+        | i >= tagLen = [|zeroBits|]+    go16 i+        | tagLen - i < 2 = go16 (tagLen - 2)+    go16 i = do+        let wIntegral =+                litIntegral+                    (shiftAdd w8_w16 [tag !! i, tag !! (i + 1)] :: Word16)+        [|xor (unsafeInlineIO+                   (Unbox.peekAt+                        ($(varE off) + $(litIntegral i))+                        $(varE arr)))+              ($(wIntegral) :: Word16) .|.+          $(go16 (i + 2))|]+    go32 i+        | i >= tagLen = [|zeroBits|]+    go32 i+        | tagLen - i < 4 = go32 (tagLen - 4)+    go32 i = do+        let wIntegral =+                litIntegral+                    (shiftAdd+                         w8_w32+                         [ tag !! i+                         , tag !! (i + 1)+                         , tag !! (i + 2)+                         , tag !! (i + 3)+                         ] :: Word32)+        [|xor (unsafeInlineIO+                   (Unbox.peekAt+                        ($(varE off) + $(litIntegral i))+                        $(varE arr)))+              ($(wIntegral) :: Word32) .|.+          $(go32 (i + 4))|]+    go64 i+        | i >= tagLen = [|zeroBits|]+    go64 i+        | tagLen - i < 8 = go64 (tagLen - 8)+    go64 i = do+        let wIntegral =+                litIntegral+                    (shiftAdd+                         w8_w64+                         [ tag !! i+                         , tag !! (i + 1)+                         , tag !! (i + 2)+                         , tag !! (i + 3)+                         , tag !! (i + 4)+                         , tag !! (i + 5)+                         , tag !! (i + 6)+                         , tag !! (i + 7)+                         ])+        [|xor (unsafeInlineIO+                   (Unbox.peekAt+                        ($(varE off) + $(litIntegral i))+                        $(varE arr)))+              ($(wIntegral) :: Word64) .|.+          $(go64 (i + 8))|]++--------------------------------------------------------------------------------+-- Primitive serialization+--------------------------------------------------------------------------------++-- TODO:+-- Will this be too much of a code bloat?+-- Loop with the loop body unrolled?+-- Serialize this in batches similar to batch comparision in xorCmp?+serializeW8List :: Name -> Name -> [Word8] -> Q Exp+serializeW8List off arr w8List = do+    [|let $(varP (makeN 0)) = $(varE off)+       in $(doE (fmap makeBind [0 .. (lenW8List - 1)] +++                 [noBindS ([|pure $(varE (makeN lenW8List))|])]))|]++    where++    lenW8List = length w8List+    makeBind i =+        bindS+            (varP (makeN (i + 1)))+            [|$(varE 'serializeAt)+                  $(varE (makeN i))+                  $(varE arr)+                  ($(litIntegral (w8List !! i)) :: Word8)|]++--------------------------------------------------------------------------------+-- TH Helpers+--------------------------------------------------------------------------------++litIntegral :: Integral a => a -> Q Exp+litIntegral = litE . IntegerL . fromIntegral++litProxy :: Unbox a => Proxy a -> Q Exp+litProxy = litE . IntegerL . fromIntegral . Unbox.sizeOf++--------------------------------------------------------------------------------+-- Error codes+--------------------------------------------------------------------------------++errorUnsupported :: String -> a+errorUnsupported err =+    error+        $ unlines+              [ "Unsupported"+              , "==========="+              , "This is improper use of the library."+              , "This case is unsupported."+              , "Please contact the developer if this case is of interest."+              , ""+              , "Message"+              , "-------"+              , err+              ]++errorUnimplemented :: a+errorUnimplemented =+    error+        $ unlines+              [ "Unimplemented"+              , "============="+              , "Please contact the developer if this case is of interest."+              ]
+ src/Streamly/Internal/Data/Serialize/TH/Common.hs view
@@ -0,0 +1,70 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.Data.Serialize.TH.Common+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Serialize.TH.Common+    ( mkDeserializeExprOne+    , mkSerializeExprFields+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Language.Haskell.TH+import Streamly.Internal.Data.Serialize.TH.Bottom++--------------------------------------------------------------------------------+-- Code+--------------------------------------------------------------------------------++mkDeserializeExprOne :: Name -> SimpleDataCon -> Q Exp+mkDeserializeExprOne peeker (SimpleDataCon cname fields) =+    case fields of+        -- Only tag is serialized for unit fields, no actual value+        [] -> [|pure ($(varE (mkName "i0")), $(conE cname))|]+        _ ->+            doE+                (concat+                     [ fmap makeBind [0 .. (numFields - 1)]+                     , [ noBindS+                             (appE+                                  (varE 'pure)+                                  (tupE+                                       [ varE (makeI numFields)+                                       , appsE+                                             (conE cname :+                                              (map (varE . makeA)+                                                   [0 .. (numFields - 1)]))+                                       ]))+                       ]+                     ])+  where+    numFields = length fields+    makeBind i =+        bindS+            (tupP [varP (makeI (i + 1)), varP (makeA i)])+            [|$(varE peeker) $(varE (makeI i)) $(varE _arr) $(varE _endOffset)|]++mkSerializeExprFields :: Name -> [Field] -> Q Exp+mkSerializeExprFields poker fields =+    case fields of+        -- Unit constructor, do nothing just tag is enough+        [] -> [|pure ($(varE (mkName "i0")))|]+        _ ->+            doE+                (fmap makeBind [0 .. (numFields - 1)] +++                 [noBindS ([|pure $(varE (makeI numFields))|])])+  where+    numFields = length fields+    makeBind i =+        bindS+            (varP (makeI (i + 1)))+            [|$(varE poker)+                   $(varE (makeI i)) $(varE _arr) $(varE (mkFieldName i))|]
+ src/Streamly/Internal/Data/Serialize/TH/RecHeader.hs view
@@ -0,0 +1,411 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.Data.Serialize.TH.RecHeader+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Serialize.TH.RecHeader+    ( mkRecSerializeExpr+    , mkRecDeserializeExpr+    , mkRecSizeOfExpr+    , conUpdateFuncDec+    , mkDeserializeKeysDec+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Control.Monad (void)+import Data.List (foldl')+import Data.Word (Word32, Word8)+import Data.Maybe (fromJust)+import Language.Haskell.TH+import Streamly.Internal.Data.Serialize.Type (Serialize(..))+import Data.Foldable (foldlM)+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray)+import Data.Proxy (Proxy(..))++import qualified Streamly.Internal.Data.Unbox as Unbox++import Streamly.Internal.Data.Serialize.TH.Bottom+import Streamly.Internal.Data.Serialize.TH.Common++--------------------------------------------------------------------------------+-- Notes+--------------------------------------------------------------------------------++-- Compatibility Algorithm+-- =======================+--+-- The algorithm is written without any low level implementation details. See+-- the code for any low level implementation details.+--+-- Serialization:+-- --------------+--+-- To serialize the data,+--+-- * Get the list of keys for the record as @keyList@.+-- * Serialize the @keyList@.+-- * Serialize the @fields@ one-by-one after serializing the @keyList@.+--+-- Deserialization:+-- ----------------+--+-- To deserialize the data to type @T@,+--+-- __Checking for type match__:+--+-- * Get the list of keys for type @T@ as @targetKeyList@.+-- * Get the list of keys encoded as @encodedKeyList@.+-- * If @targetKeyList == encodedKeyList@ see the __Type Match__ section else+--   see the __No Type Match__ section.+--+-- __Type Match__:+--+-- * Decode the fields one-by-one and construct the type @T@ in the end.+--+-- __No Type Match__:+--+-- * Decode the list of keys encoded into @encodedKeyList@.+-- * Get the list of keys for type @T@ as @targetKeyList@.+-- * Loop through @encodedKeyList@ and start deserializing the encoded data.+-- * If the key is present in @encodedKeyList@ and not in @targetKeyList@+--   then skip parsing the corresponding value.+-- * If the key is present in @targetKeyList@ and not in @encodedKeyList@+--   then set the value for that key as @Nothing@.+-- * If the key is present in both @encodedKeyList@ and in @targetKeyList@+--   parse the value.+-- * Construct @T@ after parsing all the data.++-- Developer Notes+-- ===============+--+-- * Record update syntax is not robust across language extensions and common+--   record plugins (like record-dot-processor, large-records, etc.).++--------------------------------------------------------------------------------+-- Compact lists+--------------------------------------------------------------------------------++-- Like haskell list but the maximum length of the list is 255+newtype CompactList a =+    CompactList+        { unCompactList :: [a]+        }++-- We use 'Word8' to encode the length, hence the maximim number of elements in+-- the list is 255.+instance forall a. Serialize a => Serialize (CompactList a) where++    -- {-# INLINE addSizeTo #-}+    addSizeTo acc (CompactList xs) =+        foldl' addSizeTo (acc + (Unbox.sizeOf (Proxy :: Proxy Word8))) xs++    -- Inlining this causes large compilation times for tests+    {-# INLINABLE deserializeAt #-}+    deserializeAt off arr sz = do+        (off1, len8) <- deserializeAt off arr sz :: IO (Int, Word8)+        let len = w8_int len8+            peekList f o i | i >= 3 = do+              -- Unfold the loop three times+              (o1, x1) <- deserializeAt o arr sz+              (o2, x2) <- deserializeAt o1 arr sz+              (o3, x3) <- deserializeAt o2 arr sz+              peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)+            peekList f o 0 = pure (o, f [])+            peekList f o i = do+              (o1, x) <- deserializeAt o arr sz+              peekList (f . (x:)) o1 (i - 1)+        (nextOff, lst) <- peekList id off1 len+        pure (nextOff, CompactList lst)++    -- Inlining this causes large compilation times for tests+    {-# INLINABLE serializeAt #-}+    serializeAt off arr (CompactList val) = do+        void $ serializeAt off arr (int_w8 (length val) :: Word8)+        let off1 = off + Unbox.sizeOf (Proxy :: Proxy Word8)+        let pokeList o [] = pure o+            pokeList o (x:xs) = do+              o1 <- serializeAt o arr x+              pokeList o1 xs+        pokeList off1 val++--------------------------------------------------------------------------------+-- Helpers+--------------------------------------------------------------------------------++fieldToNameBase :: Field -> String+fieldToNameBase = nameBase . fromJust . fst++isMaybeType :: Type -> Bool+isMaybeType (AppT (ConT m) _) = m == ''Maybe+isMaybeType _ = False++--------------------------------------------------------------------------------+-- Size+--------------------------------------------------------------------------------++-- We add 4 here because we use 'serializeWithSize' for serializing.+exprGetSize :: Q Exp -> (Int, Type) -> Q Exp+exprGetSize acc (i, _) =+    [|addSizeTo $(acc) $(varE (mkFieldName i)) + 4|]++sizeOfHeader :: SimpleDataCon -> Int+sizeOfHeader (SimpleDataCon _ fields) =+    sizeForFinalOff + sizeForHeaderLength + sizeForNumFields+        + sum (map ((+ sizeForFieldLen) . length . fieldToNameBase) fields)++    where++    sizeForFinalOff = 4+    sizeForHeaderLength = 4 -- Max header length is (255 * (255 + 1) + 1) and+                            -- hence 2 bytes is enough to store it. But we still+                            -- use 4 bytes as using 2 bytes introduces+                            -- regression.+    sizeForNumFields = 1 -- At max 255 fields in the record constructor+    sizeForFieldLen = 1  -- At max 255 letters in the key++mkRecSizeOfExpr :: SimpleDataCon -> Q Exp+mkRecSizeOfExpr con = do+    n_acc <- newName "acc"+    n_x <- newName "x"+    (lamE+         [varP n_acc, varP n_x]+         [|$(litIntegral hlen) ++            $(caseE (varE n_x) [matchCons (varE n_acc) con])|])++    where++    hlen = sizeOfHeader con+    sizeOfFields acc fields = foldl' exprGetSize acc $ zip [0 ..] fields+    matchCons acc (SimpleDataCon cname fields) =+        let expr = sizeOfFields acc (map snd fields)+         in matchConstructor cname (length fields) expr++--------------------------------------------------------------------------------+-- Header+--------------------------------------------------------------------------------++headerValue :: SimpleDataCon -> [Word8]+headerValue (SimpleDataCon _ fields) =+    int_w8 numFields : concat (fmap lengthPrependedFieldEncoding fields)++    where++    -- Error out if the number of fields or the length of key is >= 256. We use+    -- Word8 for encoding the info and hence the max value is 255.+    numFields =+        let lenFields = length fields+         in if lenFields <= 255+            then lenFields+            else errorUnsupported+                     "Number of fields in the record should be <= 255."+    lengthPrependedFieldEncoding field =+        let fEnc =+                let fEnc_ = map c2w (fieldToNameBase field)+                    lenFEnc = length fEnc_+                 in if lenFEnc <= 255+                    then fEnc_+                    else+                        errorUnsupported+                            "Length of any key should be <= 255."+         in (int_w8 (length fEnc)) : fEnc++--------------------------------------------------------------------------------+-- Peek+--------------------------------------------------------------------------------++-- Encoding the size is required if we want to skip the field without knowing+-- its type. We encode the size as 'Word32' hence there is a 4 bytes increase+-- in size.+{-# INLINE serializeWithSize #-}+serializeWithSize :: Serialize a => Int -> MutByteArray -> a -> IO Int+serializeWithSize off arr val = do+    off1 <- serializeAt (off + 4) arr val+    Unbox.pokeAt off arr (int_w32 (off1 - off - 4) :: Word32)+    pure off1++mkRecSerializeExpr :: Name -> SimpleDataCon -> Q Exp+mkRecSerializeExpr initialOffset (con@(SimpleDataCon cname fields)) = do+    afterHLen <- newName "afterHLen"+    -- Encoding the header length is required.+    -- We first compare the header length encoded and the current header+    -- length. Only if the header lengths match, we compare the headers.+    [|do $(varP afterHLen) <-+             serializeAt+                 ($(varE initialOffset) + 4)+                 $(varE _arr)+                 ($(litIntegral hlen) :: Word32)+         $(varP (makeI 0)) <- $(serializeW8List afterHLen _arr hval)+         let $(openConstructor cname (length fields)) = $(varE _val)+         finalOff <- $(mkSerializeExprFields 'serializeWithSize fields)+         Unbox.pokeAt+             $(varE initialOffset)+             $(varE _arr)+             ((fromIntegral :: Int -> Word32)+                  (finalOff - $(varE initialOffset)))+         pure finalOff|]++    where++    hval = headerValue con+    hlen = length hval++--------------------------------------------------------------------------------+-- Poke+--------------------------------------------------------------------------------++{-# INLINE deserializeWithSize #-}+deserializeWithSize ::+       Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)+deserializeWithSize off arr endOff = deserializeAt (off + 4) arr endOff++conUpdateFuncDec :: Name -> [Field] -> Q [Dec]+conUpdateFuncDec funcName fields = do+    prevAcc <- newName "prevAcc"+    curOff <- newName "curOff"+    endOff <- newName "endOff"+    arr <- newName "arr"+    key <- newName "key"+    method <-+        (caseE+             (varE key)+             (concat+                  [ map (matchField arr endOff (prevAcc, curOff)) fnames+                  , [ match+                          wildP+                          (normalB+                               [|do (valOff, valLen :: Word32) <-+                                        deserializeAt+                                            $(varE curOff)+                                            $(varE arr)+                                            $(varE endOff)+                                    pure+                                        ( $(varE prevAcc)+                                        , valOff + w32_int valLen)|])+                          []+                    ]+                  ]))+    pure+        [ PragmaD (InlineP funcName NoInline FunLike AllPhases)+        , FunD+              funcName+              [ Clause+                    [ VarP arr+                    , VarP endOff+                    , TupP [VarP prevAcc, VarP curOff]+                    , VarP key+                    ]+                    (NormalB method)+                    []+              ]+        ]++    where++    fnames = fmap (fromJust . fst) fields+    matchField :: Name -> Name -> (Name, Name) -> Name -> Q Match+    matchField arr endOff (acc, currOff) fname = do+        let fnameLit = StringL (nameBase fname)+        match+            (litP fnameLit)+            (normalB+                 [|do (valOff, valLen :: Word32) <-+                        deserializeAt+                            $(varE currOff)+                            $(varE arr)+                            $(varE endOff)+                      pure+                          ( ($(litE fnameLit), $(varE currOff)) : $(varE acc)+                          , valOff + w32_int valLen)|])+            []++mkDeserializeKeysDec :: Name -> Name -> SimpleDataCon -> Q [Dec]+mkDeserializeKeysDec funcName updateFunc (SimpleDataCon cname fields) = do+    hOff <- newName "hOff"+    finalOff <- newName "finalOff"+    arr <- newName "arr"+    endOff <- newName "endOff"+    kvEncoded <- newName "kvEncoded"+    finalRec <- newName "finalRec"+    let deserializeFieldExpr (Just name, ty) = do+            let nameLit = litE (StringL (nameBase name))+            [|case lookup $(nameLit) $(varE kvEncoded) of+                  Nothing -> $(emptyTy name ty)+                  Just off -> do+                      val <- deserializeWithSize off $(varE arr) $(varE endOff)+                      pure $ snd val|]+        deserializeFieldExpr _ =+            errorUnsupported "The datatype should use record syntax."+    method <-+        [|do (dataOff, hlist :: CompactList (CompactList Word8)) <-+                 deserializeAt $(varE hOff) $(varE arr) $(varE endOff)+             let keys = wListToString . unCompactList <$> unCompactList hlist+             ($(varP kvEncoded), _) <-+                 foldlM+                     ($(varE updateFunc) $(varE arr) $(varE endOff))+                     ([], dataOff)+                     keys+             $(varP finalRec) <-+                 $(foldl+                       (\acc i ->+                            [|$(acc) <*>+                              $(deserializeFieldExpr i)|])+                       [|pure $(conE cname)|]+                       fields)+             pure ($(varE finalOff), $(varE finalRec))|]+    pure+        [ PragmaD (InlineP funcName NoInline FunLike AllPhases)+        , FunD+              funcName+              [ Clause+                    [ VarP hOff+                    , VarP finalOff+                    , VarP arr+                    , VarP endOff+                    ]+                    (NormalB method)+                    []+              ]+        ]++    where++    emptyTy k ty =+        if isMaybeType ty+            then [|pure Nothing|]+            else [|error $(litE (StringL (nameBase k ++ " is not found.")))|]+++mkRecDeserializeExpr :: Name -> Name -> Name -> SimpleDataCon -> Q Exp+mkRecDeserializeExpr initialOff endOff deserializeWithKeys con = do+    hOff <- newName "hOff"+    let  sizeForFinalOff = 4     -- Word32+         sizeForHeaderLength = 4 -- Word32+         sizePreData = sizeForFinalOff + sizeForHeaderLength + hlen+    [|do (hlenOff, encLen :: Word32) <-+             deserializeAt $(varE initialOff) $(varE _arr) $(varE endOff)+         ($(varP hOff), hlen1 :: Word32) <-+             deserializeAt hlenOff $(varE _arr) $(varE endOff)+         if (hlen1 == $(litIntegral hlen)) && $(xorCmp hval hOff _arr)+         then do+             let $(varP (makeI 0)) =+                     $(varE initialOff) ++                     $(litIntegral sizePreData)+             $(mkDeserializeExprOne 'deserializeWithSize con)+         else $(varE deserializeWithKeys)+                  $(varE hOff)+                  ($(varE initialOff) + w32_int encLen)+                  $(varE _arr)+                  $(varE endOff)|]++    where++    hval = headerValue con+    hlen = length hval
+ src/Streamly/Internal/Data/Serialize/Type.hs view
@@ -0,0 +1,317 @@+-- |+-- Module      : Streamly.Internal.Data.Serialize.Type+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Portability : GHC+--++module Streamly.Internal.Data.Serialize.Type+    (+      Serialize(..)+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Control.Monad (when)+import Data.List (foldl')+import Data.Proxy (Proxy (..))+import Streamly.Internal.Data.Unbox (Unbox)+import Streamly.Internal.Data.MutByteArray.Type (MutByteArray(..))+import Streamly.Internal.Data.Array.Type (Array(..))+import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))+import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))+import GHC.Stable (StablePtr(..))++import qualified Streamly.Internal.Data.MutByteArray.Type as MBA+import qualified Streamly.Internal.Data.Unbox as Unbox+import qualified Streamly.Internal.Data.Array.Type as Array+import qualified Streamly.Internal.Data.MutArray as MutArray++import GHC.Exts++--------------------------------------------------------------------------------+-- Developer Note+--------------------------------------------------------------------------------++-- IMPORTANT+-- =========+--+-- Don't ever serialize the absolute offsets in the encoding. Serialize length+-- instead. Absolute offsets are NOT stable.+--+-- They will only work if the start offset of the Array when encoding and+-- decoding is the same. This is almost never the case.++--------------------------------------------------------------------------------+-- Types+--------------------------------------------------------------------------------++-- | The 'Serialize' type class provides operations for serialization and+-- deserialization of general Haskell data types to and from their byte stream+-- representation.+--+-- Unlike 'Unbox', 'Serialize' uses variable length encoding, therefore, it can+-- serialize recursive and variable length data types like lists, or variable+-- length sum types where the length of the value may vary depending on a+-- particular constructor. For variable length data types the length is encoded+-- along with the data.+--+-- The 'deserializeAt' operation reads bytes from the mutable byte array and+-- builds a Haskell data type from these bytes, the number of bytes it reads+-- depends on the type and the encoded value it is reading. 'serializeAt'+-- operation converts a Haskell data type to its binary representation which+-- must consist of as many bytes as added by the @addSizeTo@ operation for that+-- value and then stores these bytes into the mutable byte array. The+-- programmer is expected to use the @addSizeTo@ operation and allocate an+-- array of sufficient length before calling 'serializeAt'.+--+-- IMPORTANT: The serialized data's byte ordering remains the same as the host+-- machine's byte order. Therefore, it can not be deserialized from host+-- machines with a different byte ordering.+--+-- Instances can be derived via Template Haskell, or written manually.+--+-- Here is an example, for deriving an instance of this type class using+-- template Haskell:+--+-- >>> :{+-- data Object = Object+--     { _obj1 :: [Int]+--     , _obj2 :: Int+--     }+-- :}+--+-- @+-- import Streamly.Data.MutByteArray (deriveSerialize)+-- \$(deriveSerialize [d|instance Serialize Object|])+-- @+--+-- See 'Streamly.Data.MutByteArray.deriveSerialize' and+-- 'Streamly.Data.MutByteArray.deriveSerializeWith' for more information on+-- deriving using Template Haskell.+--+-- Here is an example of a manual instance.+--+-- >>> import Streamly.Data.MutByteArray (Serialize(..))+--+-- >>> :{+-- instance Serialize Object where+--     addSizeTo acc obj = addSizeTo (addSizeTo acc (_obj1 obj)) (_obj2 obj)+--     deserializeAt i arr len = do+--          -- Check the array bounds before reading+--         (i1, x0) <- deserializeAt i arr len+--         (i2, x1) <- deserializeAt i1 arr len+--         pure (i2, Object x0 x1)+--     serializeAt i arr (Object x0 x1) = do+--         i1 <- serializeAt i arr x0+--         i2 <- serializeAt i1 arr x1+--         pure i2+-- :}+--+class Serialize a where+    -- XXX Use (a -> Sum Int) instead, remove the Size type++    -- A left fold step to fold a generic structure to its serializable size.+    -- It is of the form @Int -> a -> Int@ because you can have tail-recursive+    -- traversal of the structures.++    -- | @addSizeTo accum value@ returns @accum@ incremented by the size of the+    -- serialized representation of @value@ in bytes. Size cannot be zero. It+    -- should be at least 1 byte.+    addSizeTo :: Int -> a -> Int++    -- We can implement the following functions without returning the `Int`+    -- offset but that may require traversing the Haskell structure again to get+    -- the size. Therefore, this is a performance optimization.++    -- | @deserializeAt byte-offset array arrayLen@ deserializes a value from+    -- the given byte-offset in the array. Returns a tuple consisting of the+    -- next byte-offset and the deserialized value.+    --+    -- The arrayLen passed is the entire length of the input buffer. It is to+    -- be used to check if we would overflow the input buffer when+    -- deserializing.+    --+    -- Throws an exception if the operation would exceed the supplied arrayLen.+    deserializeAt :: Int -> MutByteArray -> Int -> IO (Int, a)++    -- | @serializeAt byte-offset array value@ writes the serialized+    -- representation of the @value@ in the array at the given byte-offset.+    -- Returns the next byte-offset.+    --+    -- This is an unsafe operation, the programmer must ensure that the array+    -- has enough space available to serialize the value as determined by the+    -- @addSizeTo@ operation.+    serializeAt :: Int -> MutByteArray -> a -> IO Int++--------------------------------------------------------------------------------+-- Instances+--------------------------------------------------------------------------------++-- _size is the length from array start to the last accessed byte.+#ifdef DEBUG+{-# INLINE checkBounds #-}+checkBounds :: String -> Int -> MutByteArray -> IO ()+checkBounds _label _size _arr = do+    sz <- MBA.sizeOfMutableByteArray _arr+    if (_size > sz)+    then error+        $ _label+            ++ ": accessing array at offset = "+            ++ show (_size - 1)+            ++ " max valid offset = " ++ show (sz - 1)+    else return ()+#endif++-- Note: Instead of passing around the size parameter, we can use+-- (sizeOfMutableByteArray arr) for checking the array bound, but that turns+-- out to be more expensive.+--+-- Another way to optimize this is to avoid the check for fixed size+-- structures. For fixed size structures we can do a check at the top level and+-- then use checkless deserialization using the Unbox type class. That will+-- require ConstSize and VarSize constructors in size. The programmer can+-- bundle all const size fields in a newtype to make serialization faster. This+-- can speed up the computation of size when serializing and checking size when+-- deserialing.+--+-- For variable size non-recursive structures a separate size validation method+-- could be used to validate the size before deserializing. "validate" can also+-- be used to collpase multiple chunks of arrays coming from network into a+-- single array for deserializing. But that can also be done by framing the+-- serialized value with a size header.+--+{-# INLINE deserializeChecked #-}+deserializeChecked :: forall a. Unbox a => Int -> MutByteArray -> Int -> IO (Int, a)+deserializeChecked off arr sz =+    let next = off + Unbox.sizeOf (Proxy :: Proxy a)+     in do+        -- Keep likely path in the straight branch.+        if (next <= sz)+        then Unbox.peekAt off arr >>= \val -> pure (next, val)+        else error+            $ "deserializeAt: accessing array at offset = "+                ++ show (next - 1)+                ++ " max valid offset = " ++ show (sz - 1)++{-# INLINE serializeUnsafe #-}+serializeUnsafe :: forall a. Unbox a => Int -> MutByteArray -> a -> IO Int+serializeUnsafe off arr val =+    let next = off + Unbox.sizeOf (Proxy :: Proxy a)+     in do+#ifdef DEBUG+        checkBounds "serializeAt" next arr+#endif+        Unbox.pokeAt off arr val+        pure next++#define DERIVE_SERIALIZE_FROM_UNBOX(_type) \+instance Serialize _type where \+; {-# INLINE addSizeTo #-} \+;    addSizeTo acc _ = acc +  Unbox.sizeOf (Proxy :: Proxy _type) \+; {-# INLINE deserializeAt #-} \+;    deserializeAt off arr end = deserializeChecked off arr end :: IO (Int, _type) \+; {-# INLINE serializeAt #-} \+;    serializeAt =  \+        serializeUnsafe :: Int -> MutByteArray -> _type -> IO Int++DERIVE_SERIALIZE_FROM_UNBOX(())+DERIVE_SERIALIZE_FROM_UNBOX(Bool)+DERIVE_SERIALIZE_FROM_UNBOX(Char)+DERIVE_SERIALIZE_FROM_UNBOX(Int8)+DERIVE_SERIALIZE_FROM_UNBOX(Int16)+DERIVE_SERIALIZE_FROM_UNBOX(Int32)+DERIVE_SERIALIZE_FROM_UNBOX(Int)+DERIVE_SERIALIZE_FROM_UNBOX(Int64)+DERIVE_SERIALIZE_FROM_UNBOX(Word)+DERIVE_SERIALIZE_FROM_UNBOX(Word8)+DERIVE_SERIALIZE_FROM_UNBOX(Word16)+DERIVE_SERIALIZE_FROM_UNBOX(Word32)+DERIVE_SERIALIZE_FROM_UNBOX(Word64)+DERIVE_SERIALIZE_FROM_UNBOX(Double)+DERIVE_SERIALIZE_FROM_UNBOX(Float)+DERIVE_SERIALIZE_FROM_UNBOX((StablePtr a))+DERIVE_SERIALIZE_FROM_UNBOX((Ptr a))+DERIVE_SERIALIZE_FROM_UNBOX((FunPtr a))++instance forall a. Serialize a => Serialize [a] where++    -- {-# INLINE addSizeTo #-}+    addSizeTo acc xs =+        foldl' addSizeTo (acc + (Unbox.sizeOf (Proxy :: Proxy Int))) xs++    -- Inlining this causes large compilation times for tests+    {-# INLINABLE deserializeAt #-}+    deserializeAt off arr sz = do+        (off1, len64) <- deserializeAt off arr sz :: IO (Int, Int64)+        let len = (fromIntegral :: Int64 -> Int) len64+            peekList f o i | i >= 3 = do+              -- Unfold the loop three times+              (o1, x1) <- deserializeAt o arr sz+              (o2, x2) <- deserializeAt o1 arr sz+              (o3, x3) <- deserializeAt o2 arr sz+              peekList (f . (\xs -> x1:x2:x3:xs)) o3 (i - 3)+            peekList f o 0 = pure (o, f [])+            peekList f o i = do+              (o1, x) <- deserializeAt o arr sz+              peekList (f . (x:)) o1 (i - 1)+        peekList id off1 len++    -- Inlining this causes large compilation times for tests+    {-# INLINABLE serializeAt #-}+    serializeAt off arr val = do+        let off1 = off + Unbox.sizeOf (Proxy :: Proxy Int64)+        let pokeList acc o [] =+              Unbox.pokeAt off arr (acc :: Int64) >> pure o+            pokeList acc o (x:xs) = do+              o1 <- serializeAt o arr x+              pokeList (acc + 1) o1 xs+        pokeList 0 off1 val++instance+#ifdef DEVBUILD+    Unbox a =>+#endif+  Serialize (Array a) where+    {-# INLINE addSizeTo #-}+    addSizeTo i (Array {..}) = i + (arrEnd - arrStart) + 8++    {-# INLINE deserializeAt #-}+    deserializeAt off arr len = do+        (off1, byteLen) <- deserializeAt off arr len :: IO (Int, Int)+        let off2 = off1 + byteLen+        when (off2 > len) $+            error+                $ "deserializeAt: accessing array at offset = "+                    ++ show (off2 - 1)+                    ++ " max valid offset = " ++ show (len - 1)+        -- XXX Use MutByteArray.cloneSliceUnsafe+        let slice = MutArray.MutArray arr off1 off2 off2+        newArr <- MutArray.clone slice+        pure (off2, Array.unsafeFreeze newArr)++    {-# INLINE serializeAt #-}+    serializeAt off arr (Array {..}) = do+        let arrLen = arrEnd - arrStart+        off1 <- serializeAt off arr arrLen+        MBA.putSliceUnsafe arrContents arrStart arr off1 arrLen+        pure (off1 + arrLen)++instance (Serialize a, Serialize b) => Serialize (a, b) where++    {-# INLINE addSizeTo #-}+    addSizeTo acc (a, b) = addSizeTo (addSizeTo acc a) b++    {-# INLINE serializeAt #-}+    serializeAt off arr (a, b) = do+        off1 <- serializeAt off arr a+        serializeAt off1 arr b++    {-# INLINE deserializeAt #-}+    deserializeAt off arr end = do+        (off1, a) <- deserializeAt off arr end+        (off2, b) <- deserializeAt off1 arr end+        pure (off2, (a, b))
src/Streamly/Internal/Data/Stream.hs view
@@ -1,14 +1,42 @@ -- | -- Module      : Streamly.Internal.Data.Stream--- Copyright   : (c) 2019 Composewell Technologies+-- Copyright   : (c) 2018 Composewell Technologies -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC --+-- Direct style re-implementation of CPS stream in+-- "Streamly.Internal.Data.StreamK".  The symbol or suffix 'D' in this+-- module denotes the "Direct" style.  GHC is able to INLINE and fuse direct+-- style better, providing better performance than CPS implementation.+--+-- @+-- import qualified Streamly.Internal.Data.Stream as D+-- @+ module Streamly.Internal.Data.Stream-    ( module Streamly.Internal.Data.Stream.StreamD+    (+      module Streamly.Internal.Data.Stream.Type+    , module Streamly.Internal.Data.Stream.Generate+    , module Streamly.Internal.Data.Stream.Eliminate+    , module Streamly.Internal.Data.Stream.Exception+    , module Streamly.Internal.Data.Stream.Lift+    , module Streamly.Internal.Data.Stream.Transformer+    , module Streamly.Internal.Data.Stream.Nesting+    , module Streamly.Internal.Data.Stream.Transform+    , module Streamly.Internal.Data.Stream.Top+    , module Streamly.Internal.Data.Stream.Container     ) where -import Streamly.Internal.Data.Stream.StreamD+import Streamly.Internal.Data.Stream.Type+import Streamly.Internal.Data.Stream.Generate+import Streamly.Internal.Data.Stream.Eliminate+import Streamly.Internal.Data.Stream.Exception+import Streamly.Internal.Data.Stream.Lift+import Streamly.Internal.Data.Stream.Transformer+import Streamly.Internal.Data.Stream.Nesting+import Streamly.Internal.Data.Stream.Transform+import Streamly.Internal.Data.Stream.Top+import Streamly.Internal.Data.Stream.Container
− src/Streamly/Internal/Data/Stream/Bottom.hs
@@ -1,670 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.Bottom--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Bottom level Stream module that can be used by all other upper level--- Stream modules.--module Streamly.Internal.Data.Stream.Bottom-    (-    -- * Generation-      fromPure-    , fromEffect-    , fromList-    , timesWith-    , absTimesWith-    , relTimesWith--    -- * Folds-    , fold-    , foldBreak-    , foldBreak2-    , foldEither-    , foldEither2-    , foldConcat--    -- * Builders-    , foldAdd-    , foldAddLazy--    -- * Scans-    , smapM-    -- $smapM_Notes-    , postscan-    , catMaybes-    , scanMaybe--    , take-    , takeWhile-    , takeEndBy-    , drop-    , findIndices--    -- * Merge-    , intersperseM--    -- * Fold and Unfold-    , reverse-    , reverse'--    -- * Expand-    , concatEffect-    , concatEffect2-    , concatMapM-    , concatMap--    -- * Reduce-    , foldManyPost--    -- * Zipping-    , zipWithM-    , zipWith-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO(..))-import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Fold.Type (Fold (..))-import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)-import Streamly.Internal.Data.Unboxed (Unbox)-import Streamly.Internal.Data.Producer.Type (Producer(..))-import Streamly.Internal.System.IO (defaultChunkSize)-import Streamly.Internal.Data.SVar.Type (defState)--import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Fold as Fold-import qualified Streamly.Internal.Data.Stream.StreamK as K-import qualified Streamly.Internal.Data.Stream.StreamD as D--import Prelude hiding (take, takeWhile, drop, reverse, concatMap, map, zipWith)--import Streamly.Internal.Data.Stream.Type------- $setup--- >>> :m--- >>> import Control.Monad (join, (>=>), (<=<))--- >>> import Data.Function (fix, (&))--- >>> import Data.Functor.Identity (Identity)--- >>> import Data.Maybe (fromJust, isJust)--- >>> import Prelude hiding (take, takeWhile, drop, reverse)--- >>> import Streamly.Data.Array (Array)--- >>> import Streamly.Data.Fold (Fold)--- >>> import Streamly.Data.Stream (Stream)--- >>> import System.IO.Unsafe (unsafePerformIO)--- >>> import qualified Streamly.Data.Array as Array--- >>> import qualified Streamly.Data.MutArray as MArray--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Parser as Parser--- >>> import qualified Streamly.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Fold as Fold (toStream)--- >>> import Streamly.Internal.Data.Stream as Stream----------------------------------------------------------------------------------- Generation - Time related----------------------------------------------------------------------------------- | @timesWith g@ returns a stream of time value tuples. The first component--- of the tuple is an absolute time reference (epoch) denoting the start of the--- stream and the second component is a time relative to the reference.------ The argument @g@ specifies the granularity of the relative time in seconds.--- A lower granularity clock gives higher precision but is more expensive in--- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.------ >>> import Control.Concurrent (threadDelay)--- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)--- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE timesWith #-}-timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)-timesWith g = fromStreamD $ D.timesWith g---- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of--- granularity @g@ specified in seconds. A low granularity clock is more--- expensive in terms of CPU usage.  Any granularity lower than 1 ms is treated--- as 1 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE absTimesWith #-}-absTimesWith :: MonadIO m => Double -> Stream m AbsTime-absTimesWith = fmap (uncurry addToAbsTime64) . timesWith---- | @relTimesWith g@ returns a stream of relative time values starting from 0,--- using a clock of granularity @g@ specified in seconds. A low granularity--- clock is more expensive in terms of CPU usage.  Any granularity lower than 1--- ms is treated as 1 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE relTimesWith #-}-relTimesWith :: MonadIO m => Double -> Stream m RelTime64-relTimesWith = fmap snd . timesWith----------------------------------------------------------------------------------- Elimination - Running a Fold----------------------------------------------------------------------------------- | Append a stream to a fold lazily to build an accumulator incrementally.------ Example, to continue folding a list of streams on the same sum fold:------ >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]--- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams--- >>> Stream.fold f Stream.nil--- 55----{-# INLINE foldAddLazy #-}-foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b-foldAddLazy f s = D.foldAddLazy f $ toStreamD s---- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce---- |--- >>> foldAdd = flip Fold.addStream----foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)-foldAdd f = fold (Fold.duplicate f)---- >>> fold f = Fold.extractM . Stream.foldAddLazy f--- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f--- >>> fold f = Fold.extractM <=< Stream.foldAdd f---- | Fold a stream using the supplied left 'Fold' and reducing the resulting--- expression strictly at each step. The behavior is similar to 'foldl''. A--- 'Fold' can terminate early without consuming the full stream. See the--- documentation of individual 'Fold's for termination behavior.------ Definitions:------ >>> fold f = fmap fst . Stream.foldBreak f--- >>> fold f = Stream.parse (Parser.fromFold f)------ Example:------ >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)--- 5050----{-# INLINE fold #-}-fold :: Monad m => Fold m a b -> Stream m a -> m b-fold fl strm = D.fold fl $ D.fromStreamK $ toStreamK strm---- Alternative name foldSome, but may be confused vs foldMany.---- | Like 'fold' but also returns the remaining stream. The resulting stream--- would be 'Stream.nil' if the stream finished before the fold.------ /CPS/----{-# INLINE foldBreak #-}-foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)-foldBreak fl strm = fmap f $ K.foldBreak fl (toStreamK strm)--    where--    f (b, str) = (b, fromStreamK str)---- XXX The quadratic slowdown in recursive use is because recursive function--- cannot be inlined and StreamD/StreamK conversions pile up and cannot be--- eliminated by rewrite rules.---- | Like 'foldBreak' but fuses.------ /Note:/ Unlike 'foldBreak', recursive application on the resulting stream--- would lead to quadratic slowdown. If you need recursion with fusion (within--- one iteration of recursion) use StreamD.foldBreak directly.------ /Internal/-{-# INLINE foldBreak2 #-}-foldBreak2 :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)-foldBreak2 fl strm = fmap f $ D.foldBreak fl $ toStreamD strm--    where--    f (b, str) = (b, fromStreamD str)---- | Fold resulting in either breaking the stream or continuation of the fold.--- Instead of supplying the input stream in one go we can run the fold multiple--- times, each time supplying the next segment of the input stream. If the fold--- has not yet finished it returns a fold that can be run again otherwise it--- returns the fold result and the residual stream.------ /Internal/-{-# INLINE foldEither #-}-foldEither :: Monad m =>-    Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))-foldEither fl strm = fmap (fmap f) $ K.foldEither fl $ toStreamK strm--    where--    f (b, str) = (b, fromStreamK str)---- | Like 'foldEither' but fuses. However, recursive application on resulting--- stream would lead to quadratic slowdown.------ /Internal/-{-# INLINE foldEither2 #-}-foldEither2 :: Monad m =>-    Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))-foldEither2 fl strm = fmap (fmap f) $ D.foldEither fl $ toStreamD strm--    where--    f (b, str) = (b, fromStreamD str)---- XXX Array folds can be implemented using this.--- foldContainers? Specialized to foldArrays.---- | Generate streams from individual elements of a stream and fold the--- concatenation of those streams using the supplied fold. Return the result of--- the fold and residual stream.------ For example, this can be used to efficiently fold an Array Word8 stream--- using Word8 folds.------ The outer stream forces CPS to allow scalable appends and the inner stream--- forces direct style for stream fusion.------ /Internal/-{-# INLINE foldConcat #-}-foldConcat :: Monad m =>-    Producer m a b -> Fold m b c -> Stream m a -> m (c, Stream m a)-foldConcat-    (Producer pstep pinject pextract)-    (Fold fstep begin done)-    stream = do--    res <- begin-    case res of-        Fold.Partial fs -> go fs streamK-        Fold.Done fb -> return (fb, fromStreamK streamK)--    where--    streamK = toStreamK stream--    go !acc m1 = do-        let stop = do-                r <- done acc-                return (r, fromStreamK K.nil)-            single a = do-                st <- pinject a-                res <- go1 SPEC acc st-                case res of-                    Left fs -> do-                        r <- done fs-                        return (r, fromStreamK K.nil)-                    Right (b, s) -> do-                        x <- pextract s-                        return (b, fromStreamK (K.fromPure x))-            yieldk a r = do-                st <- pinject a-                res <- go1 SPEC acc st-                case res of-                    Left fs -> go fs r-                    Right (b, s) -> do-                        x <- pextract s-                        return (b, fromStreamK (x `K.cons` r))-         in K.foldStream defState yieldk single stop m1--    {-# INLINE go1 #-}-    go1 !_ !fs st = do-        r <- pstep st-        case r of-            D.Yield x s -> do-                res <- fstep fs x-                case res of-                    Fold.Done b -> return $ Right (b, s)-                    Fold.Partial fs1 -> go1 SPEC fs1 s-            D.Skip s -> go1 SPEC fs s-            D.Stop -> return $ Left fs----------------------------------------------------------------------------------- Transformation---------------------------------------------------------------------------------{---- |--- >>> map = fmap------ Same as 'fmap'.------ >>> Stream.fold Fold.toList $ fmap (+1) $ Stream.fromList [1,2,3]--- [2,3,4]----{-# INLINE map #-}-map :: Monad m => (a -> b) -> Stream m a -> Stream m b-map f = fromStreamD . D.map f . toStreamD--}---- | Postscan a stream using the given monadic fold.------ The following example extracts the input stream up to a point where the--- running average of elements is no more than 10:------ >>> import Data.Maybe (fromJust)--- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)--- >>> s = Stream.enumerateFromTo 1.0 100.0--- >>> :{---  Stream.fold Fold.toList---   $ fmap (fromJust . fst)---   $ Stream.takeWhile (\(_,x) -> x <= 10)---   $ Stream.postscan (Fold.tee Fold.latest avg) s--- :}--- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]----{-# INLINE postscan #-}-postscan :: Monad m => Fold m a b -> Stream m a -> Stream m b-postscan fld = fromStreamD . D.postscan fld . toStreamD---- $smapM_Notes------ The stateful step function can be simplified to @(s -> a -> m b)@ to provide--- a read-only environment. However, that would just be 'mapM'.------ The initial action could be @m (s, Maybe b)@, and we can also add a final--- action @s -> m (Maybe b)@. This can be used to get pre/post scan like--- functionality and also to flush the state in the end like scanlMAfter'.--- We can also use it along with a fusible version of bracket to get--- scanlMAfter' like functionality. See issue #677.------ This can be further generalized to a type similar to Fold/Parser, giving it--- filtering and parsing capability as well (this is in fact equivalent to--- parseMany):------ smapM :: (s -> a -> m (Step s b)) -> m s -> Stream m a -> Stream m b------- | A stateful 'mapM', equivalent to a left scan, more like mapAccumL.--- Hopefully, this is a better alternative to @scan@. Separation of state from--- the output makes it easier to think in terms of a shared state, and also--- makes it easier to keep the state fully strict and the output lazy.------ See also: 'postscan'------ /Pre-release/----{-# INLINE smapM #-}-smapM :: Monad m =>-       (s -> a -> m (s, b))-    -> m s-    -> Stream m a-    -> Stream m b-smapM step initial stream =-    -- XXX implement this directly instead of using postscan-    let f = Fold.foldlM'-                (\(s, _) a -> step s a)-                (fmap (,undefined) initial)-     in fmap snd $ postscan f stream---- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.------ >>> catMaybes = Stream.mapMaybe id--- >>> catMaybes = fmap fromJust . Stream.filter isJust------ /Pre-release/----{-# INLINE catMaybes #-}-catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a--- catMaybes = fmap fromJust . filter isJust-catMaybes = fromStreamD . D.catMaybes . toStreamD---- | Use a filtering fold on a stream.------ >>> scanMaybe f = Stream.catMaybes . Stream.postscan f----{-# INLINE scanMaybe #-}-scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b-scanMaybe p = catMaybes . postscan p----------------------------------------------------------------------------------- Transformation - Trimming----------------------------------------------------------------------------------- | Take first 'n' elements from the stream and discard the rest.----{-# INLINE take #-}-take :: Monad m => Int -> Stream m a -> Stream m a--- take n = scanMaybe (Fold.taking n)-take n m = fromStreamD $ D.take n $ toStreamD m---- | End the stream as soon as the predicate fails on an element.----{-# INLINE takeWhile #-}-takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a--- takeWhile p = scanMaybe (Fold.takingEndBy_ (not . p))-takeWhile p m = fromStreamD $ D.takeWhile p $ toStreamD m--{-# INLINE takeEndBy #-}-takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a--- takeEndBy p = scanMaybe (Fold.takingEndBy p)-takeEndBy p m = fromStreamD $ D.takeEndBy p $ toStreamD m---- | Discard first 'n' elements from the stream and take the rest.----{-# INLINE drop #-}-drop :: Monad m => Int -> Stream m a -> Stream m a--- drop n = scanMaybe (Fold.dropping n)-drop n m = fromStreamD $ D.drop n $ toStreamD m----------------------------------------------------------------------------------- Searching----------------------------------------------------------------------------------- | Find all the indices where the element in the stream satisfies the given--- predicate.------ >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)----{-# INLINE findIndices #-}-findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int--- findIndices p = scanMaybe (Fold.findIndices p)-findIndices p m = fromStreamD $ D.findIndices p (toStreamD m)----------------------------------------------------------------------------------- Transformation by Inserting----------------------------------------------------------------------------------- intersperseM = intersperseMWith 1---- | Insert an effect and its output before consuming an element of a stream--- except the first one.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input--- h.,e.,l.,l.,o"h,e,l,l,o"------ Be careful about the order of effects. In the above example we used trace--- after the intersperse, if we use it before the intersperse the output would--- be he.l.l.o."h,e,l,l,o".------ >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input--- he.l.l.o."h,e,l,l,o"----{-# INLINE intersperseM #-}-intersperseM :: Monad m => m a -> Stream m a -> Stream m a-intersperseM m = fromStreamD . D.intersperseM m . toStreamD----------------------------------------------------------------------------------- Transformation by Reordering----------------------------------------------------------------------------------- XXX Use a compact region list to temporarily store the list, in both reverse--- as well as in reverse'.------ /Note:/ 'reverse'' is much faster than this, use that when performance--- matters.------ | Returns the elements of the stream in reverse order.  The stream must be--- finite. Note that this necessarily buffers the entire stream in memory.------ >>> reverse = Stream.foldlT (flip Stream.cons) Stream.nil----{-# INLINE reverse #-}-reverse :: Stream m a -> Stream m a-reverse s = fromStreamK $ K.reverse $ toStreamK s---- | Like 'reverse' but several times faster, requires a 'Storable' instance.------ /O(n) space/------ /Pre-release/-{-# INLINE reverse' #-}-reverse' :: (MonadIO m, Unbox a) => Stream m a -> Stream m a--- reverse' s = fromStreamD $ D.reverse' $ toStreamD s-reverse' =-        fromStreamD-        . A.flattenArraysRev -- unfoldMany A.readRev-        . D.fromStreamK-        . K.reverse-        . D.toStreamK-        . A.chunksOf defaultChunkSize-        . toStreamD----------------------------------------------------------------------------------- Combine streams and flatten----------------------------------------------------------------------------------- | Map a stream producing monadic function on each element of the stream--- and then flatten the results into a single stream. Since the stream--- generation function is monadic, unlike 'concatMap', it can produce an--- effect at the beginning of each iteration of the inner loop.------ See 'unfoldMany' for a fusible alternative.----{-# INLINE concatMapM #-}-concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b-concatMapM f m = fromStreamD $ D.concatMapM (fmap toStreamD . f) (toStreamD m)---- | Map a stream producing function on each element of the stream and then--- flatten the results into a single stream.------ >>> concatMap f = Stream.concatMapM (return . f)--- >>> concatMap f = Stream.concatMapWith Stream.append f--- >>> concatMap f = Stream.concat . fmap f--- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)------ See 'unfoldMany' for a fusible alternative.----{-# INLINE concatMap #-}-concatMap ::Monad m => (a -> Stream m b) -> Stream m a -> Stream m b-concatMap f m = fromStreamD $ D.concatMap (toStreamD . f) (toStreamD m)---- >>> concatEffect = Stream.concat . lift    -- requires (MonadTrans t)--- >>> concatEffect = join . lift             -- requires (MonadTrans t, Monad (Stream m))---- | Given a stream value in the underlying monad, lift and join the underlying--- monad with the stream monad.------ >>> concatEffect = Stream.concat . Stream.fromEffect------ See also: 'concat', 'sequence'------ See 'concatEffect2' for a fusible alternative.------  /CPS/----{-# INLINE concatEffect #-}-concatEffect :: Monad m => m (Stream m a) -> Stream m a-concatEffect generator =-    fromStreamK $ K.concatEffect $ fmap toStreamK generator--{-# INLINE concatEffect2 #-}-concatEffect2 :: Monad m => m (Stream m a) -> Stream m a--- concatEffect generator = concatMapM (\() -> generator) (fromPure ())-concatEffect2 generator =-    fromStreamD $ D.concatEffect $ fmap toStreamD generator---- XXX Need a more intuitive name, and need to reconcile the names--- foldMany/fold/parse/parseMany/parseManyPost etc.---- | Like 'foldMany' but evaluates the fold before the stream, and yields its--- output even if the stream is empty, therefore, always results in a non-empty--- output even on an empty stream (default result of the fold).------ Example, empty stream:------ >>> f = Fold.take 2 Fold.sum--- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f--- >>> fmany $ Stream.fromList []--- [0]------ Example, last fold empty:------ >>> fmany $ Stream.fromList [1..4]--- [3,7,0]------ Example, last fold non-empty:------ >>> fmany $ Stream.fromList [1..5]--- [3,7,5]------ Note that using a closed fold e.g. @Fold.take 0@, would result in an--- infinite stream without consuming the input.------ /Pre-release/----{-# INLINE foldManyPost #-}-foldManyPost-    :: Monad m-    => Fold m a b-    -> Stream m a-    -> Stream m b-foldManyPost f m = fromStreamD $ D.foldManyPost f (toStreamD m)----------------------------------------------------------------------------------- Zipping----------------------------------------------------------------------------------- | Like 'zipWith' but using a monadic zipping function.----{-# INLINE zipWithM #-}-zipWithM :: Monad m =>-    (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c-zipWithM f m1 m2 = fromStreamK $ K.zipWithM f (toStreamK m1) (toStreamK m2)---- | Stream @a@ is evaluated first, followed by stream @b@, the resulting--- elements @a@ and @b@ are then zipped using the supplied zip function and the--- result @c@ is yielded to the consumer.------ If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends--- first, the element @a@ from previous evaluation of stream @a@ is discarded.------ >>> s1 = Stream.fromList [1,2,3]--- >>> s2 = Stream.fromList [4,5,6]--- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2--- [5,7,9]----{-# INLINE zipWith #-}-zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c-zipWith f m1 m2 = fromStreamK $ K.zipWith f (toStreamK m1) (toStreamK m2)
− src/Streamly/Internal/Data/Stream/Chunked.hs
@@ -1,1215 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.Chunked--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD3-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : pre-release--- Portability : GHC------ Combinators to efficiently manipulate streams of immutable arrays.----module Streamly.Internal.Data.Stream.Chunked-    (-    -- * Creation-      chunksOf--    -- * Flattening to elements-    , concat-    , concatRev-    , interpose-    , interposeSuffix-    , intercalateSuffix-    , unlines--    -- * Elimination-    -- ** Element Folds-    -- The byte level foldBreak can work as efficiently as the chunk level. We-    -- can flatten the stream to byte stream and use that. But if we want the-    -- remaining stream to be a chunk stream then this could be handy. But it-    -- could also be implemented using parseBreak.-    , foldBreak -- StreamK.foldBreakChunks-    , foldBreakD-    -- The byte level parseBreak cannot work efficiently. Because the stream-    -- will have to be a StreamK for backtracking, StreamK at byte level would-    -- not be efficient.-    , parseBreak -- StreamK.parseBreakChunks-    -- , parseBreakD-    -- , foldManyChunks-    -- , parseManyChunks--    -- ** Array Folds-    -- XXX Use Parser.Chunked instead, need only chunkedParseBreak,-    -- foldBreak can be implemented using parseBreak. Use StreamK.-    , runArrayFold-    , runArrayFoldBreak-    -- , parseArr-    , runArrayParserDBreak -- StreamK.chunkedParseBreak-    , runArrayFoldMany     -- StreamK.chunkedParseMany--    , toArray--    -- * Compaction-    -- We can use something like foldManyChunks, parseManyChunks with a take-    -- fold.-    , lpackArraysChunksOf -- Fold.compactChunks-    , compact -- rechunk, compactChunks--    -- * Splitting-    -- We can use something like foldManyChunks, parseManyChunks with an-    -- appropriate splitting fold.-    , splitOn       -- Stream.rechunkOn-    , splitOnSuffix -- Stream.rechunkOnSuffix-    )-where--#include "ArrayMacros.h"-#include "inline.hs"--import Data.Bifunctor (second)-import Control.Exception (assert)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Proxy (Proxy(..))-import Data.Word (Word8)-import Streamly.Internal.Data.Unboxed (Unbox, peekWith, sizeOf)-import Fusion.Plugin.Types (Fuse(..))-import GHC.Exts (SpecConstrAnnotation(..))-import GHC.Types (SPEC(..))-import Prelude hiding (null, last, (!!), read, concat, unlines)--import Streamly.Data.Fold (Fold)-import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Array.Mut.Type (MutArray)-import Streamly.Internal.Data.Fold.Chunked (ChunkFold(..))-import Streamly.Internal.Data.Parser (ParseError(..))-import Streamly.Internal.Data.Stream.StreamD (Stream)-import Streamly.Internal.Data.Stream.StreamK (StreamK, fromStream, toStream)-import Streamly.Internal.Data.SVar.Type (adaptState, defState)-import Streamly.Internal.Data.Array.Mut.Type-    (allocBytesToElemCount)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))--import qualified Streamly.Data.Fold as FL-import qualified Streamly.Internal.Data.Array as A-import qualified Streamly.Internal.Data.Array as Array-import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Array.Mut.Stream as AS-import qualified Streamly.Internal.Data.Fold.Type as FL (Fold(..), Step(..))-import qualified Streamly.Internal.Data.Parser as PR-import qualified Streamly.Internal.Data.Parser.ParserD as PRD-    (Parser(..), Initial(..))-import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamK as K---- XXX Since these are immutable arrays MonadIO constraint can be removed from--- most places.------------------------------------------------------------------------------------ Generation------------------------------------------------------------------------------------ | @chunksOf n stream@ groups the elements in the input stream into arrays of--- @n@ elements each.------ > chunksOf n = Stream.groupsOf n (Array.writeN n)------ /Pre-release/-{-# INLINE chunksOf #-}-chunksOf :: (MonadIO m, Unbox a)-    => Int -> Stream m a -> Stream m (Array a)-chunksOf = A.chunksOf------------------------------------------------------------------------------------ Append------------------------------------------------------------------------------------ XXX efficiently compare two streams of arrays. Two streams can have chunks--- of different sizes, we can handle that in the stream comparison abstraction.--- This could be useful e.g. to fast compare whether two files differ.---- | Convert a stream of arrays into a stream of their elements.------ Same as the following:------ > concat = Stream.unfoldMany Array.read------ @since 0.7.0-{-# INLINE concat #-}-concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a--- concat m = fromStreamD $ A.flattenArrays (toStreamD m)--- concat m = fromStreamD $ D.concatMap A.toStreamD (toStreamD m)-concat = D.unfoldMany A.reader---- | Convert a stream of arrays into a stream of their elements reversing the--- contents of each array before flattening.------ > concatRev = Stream.unfoldMany Array.readerRev------ @since 0.7.0-{-# INLINE concatRev #-}-concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a--- concatRev m = fromStreamD $ A.flattenArraysRev (toStreamD m)-concatRev = D.unfoldMany A.readerRev------------------------------------------------------------------------------------ Intersperse and append------------------------------------------------------------------------------------ | Flatten a stream of arrays after inserting the given element between--- arrays.------ /Pre-release/-{-# INLINE interpose #-}-interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a-interpose x = D.interpose x A.reader--{-# INLINE intercalateSuffix #-}-intercalateSuffix :: (Monad m, Unbox a)-    => Array a -> Stream m (Array a) -> Stream m a-intercalateSuffix = D.intercalateSuffix A.reader---- | Flatten a stream of arrays appending the given element after each--- array.------ @since 0.7.0-{-# INLINE interposeSuffix #-}-interposeSuffix :: (Monad m, Unbox a)-    => a -> Stream m (Array a) -> Stream m a--- interposeSuffix x = fromStreamD . A.unlines x . toStreamD-interposeSuffix x = D.interposeSuffix x A.reader--data FlattenState s =-      OuterLoop s-    | InnerLoop s !MA.MutableByteArray !Int !Int---- XXX This is a special case of interposeSuffix, can be removed.--- XXX Remove monadIO constraint-{-# INLINE_NORMAL unlines #-}-unlines :: forall m a. (MonadIO m, Unbox a)-    => a -> D.Stream m (Array a) -> D.Stream m a-unlines sep (D.Stream step state) = D.Stream step' (OuterLoop state)-    where-    {-# INLINE_LATE step' #-}-    step' gst (OuterLoop st) = do-        r <- step (adaptState gst) st-        return $ case r of-            D.Yield Array{..} s ->-                D.Skip (InnerLoop s arrContents arrStart arrEnd)-            D.Skip s -> D.Skip (OuterLoop s)-            D.Stop -> D.Stop--    step' _ (InnerLoop st _ p end) | p == end =-        return $ D.Yield sep $ OuterLoop st--    step' _ (InnerLoop st contents p end) = do-        x <- liftIO $ peekWith contents p-        return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)------------------------------------------------------------------------------------ Compact------------------------------------------------------------------------------------ XXX These would not be needed once we implement compactLEFold, see--- module Streamly.Internal.Data.Array.Mut.Stream------ XXX Note that this thaws immutable arrays for appending, that may be--- problematic if multiple users do the same thing, however, immutable arrays--- would usually have no capacity to append, therefore, a copy will be forced--- anyway. Confirm this. We can forcefully trim the array capacity before thaw--- to ensure this.-{-# INLINE_NORMAL packArraysChunksOf #-}-packArraysChunksOf :: (MonadIO m, Unbox a)-    => Int -> D.Stream m (Array a) -> D.Stream m (Array a)-packArraysChunksOf n str =-    D.map A.unsafeFreeze $ AS.packArraysChunksOf n $ D.map A.unsafeThaw str---- XXX instead of writing two different versions of this operation, we should--- write it as a pipe.------ XXX Confirm that immutable arrays won't be modified.-{-# INLINE_NORMAL lpackArraysChunksOf #-}-lpackArraysChunksOf :: (MonadIO m, Unbox a)-    => Int -> Fold m (Array a) () -> Fold m (Array a) ()-lpackArraysChunksOf n fld =-    FL.lmap A.unsafeThaw $ AS.lpackArraysChunksOf n (FL.lmap A.unsafeFreeze fld)---- | Coalesce adjacent arrays in incoming stream to form bigger arrays of a--- maximum specified size in bytes.------ @since 0.7.0-{-# INLINE compact #-}-compact :: (MonadIO m, Unbox a)-    => Int -> Stream m (Array a) -> Stream m (Array a)-compact = packArraysChunksOf------------------------------------------------------------------------------------ Split----------------------------------------------------------------------------------data SplitState s arr-    = Initial s-    | Buffering s arr-    | Splitting s arr-    | Yielding arr (SplitState s arr)-    | Finishing---- | Split a stream of arrays on a given separator byte, dropping the separator--- and coalescing all the arrays between two separators into a single array.------ @since 0.7.0-{-# INLINE_NORMAL _splitOn #-}-_splitOn-    :: MonadIO m-    => Word8-    -> D.Stream m (Array Word8)-    -> D.Stream m (Array Word8)-_splitOn byte (D.Stream step state) = D.Stream step' (Initial state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (Initial st) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                (arr1, marr2) <- A.breakOn byte arr-                return $ case marr2 of-                    Nothing   -> D.Skip (Buffering s arr1)-                    Just arr2 -> D.Skip (Yielding arr1 (Splitting s arr2))-            D.Skip s -> return $ D.Skip (Initial s)-            D.Stop -> return D.Stop--    step' gst (Buffering st buf) = do-        r <- step gst st-        case r of-            D.Yield arr s -> do-                (arr1, marr2) <- A.breakOn byte arr-                buf' <- A.splice buf arr1-                return $ case marr2 of-                    Nothing -> D.Skip (Buffering s buf')-                    Just x -> D.Skip (Yielding buf' (Splitting s x))-            D.Skip s -> return $ D.Skip (Buffering s buf)-            D.Stop -> return $-                if A.byteLength buf == 0-                then D.Stop-                else D.Skip (Yielding buf Finishing)--    step' _ (Splitting st buf) = do-        (arr1, marr2) <- A.breakOn byte buf-        return $ case marr2 of-                Nothing -> D.Skip $ Buffering st arr1-                Just arr2 -> D.Skip $ Yielding arr1 (Splitting st arr2)--    step' _ (Yielding arr next) = return $ D.Yield arr next-    step' _ Finishing = return D.Stop---- XXX Remove MonadIO constraint.--- | Split a stream of arrays on a given separator byte, dropping the separator--- and coalescing all the arrays between two separators into a single array.------ @since 0.7.0-{-# INLINE splitOn #-}-splitOn-    :: (MonadIO m)-    => Word8-    -> Stream m (Array Word8)-    -> Stream m (Array Word8)-splitOn byte = D.splitInnerBy (A.breakOn byte) A.splice--{-# INLINE splitOnSuffix #-}-splitOnSuffix-    :: (MonadIO m)-    => Word8-    -> Stream m (Array Word8)-    -> Stream m (Array Word8)--- splitOn byte s = fromStreamD $ A.splitOn byte $ toStreamD s-splitOnSuffix byte = D.splitInnerBySuffix (A.breakOn byte) A.splice------------------------------------------------------------------------------------ Elimination - Running folds----------------------------------------------------------------------------------{-# INLINE_NORMAL foldBreakD #-}-foldBreakD :: forall m a b. (MonadIO m, Unbox a) =>-    Fold m a b -> D.Stream m (Array a) -> m (b, D.Stream m (Array a))-foldBreakD (FL.Fold fstep initial extract) stream@(D.Stream step state) = do-    res <- initial-    case res of-        FL.Partial fs -> go SPEC state fs-        FL.Done fb -> return $! (fb, stream)--    where--    {-# INLINE go #-}-    go !_ st !fs = do-        r <- step defState st-        case r of-            D.Yield (Array contents start end) s ->-                let fp = Tuple' end contents-                 in goArray SPEC s fp start fs-            D.Skip s -> go SPEC s fs-            D.Stop -> do-                b <- extract fs-                return (b, D.nil)--    goArray !_ s (Tuple' end _) !cur !fs-        | cur == end = do-            go SPEC s fs-    goArray !_ st fp@(Tuple' end contents) !cur !fs = do-        x <- liftIO $ peekWith contents cur-        res <- fstep fs x-        let next = INDEX_NEXT(cur,a)-        case res of-            FL.Done b -> do-                let arr = Array contents next end-                return $! (b, D.cons arr (D.Stream step st))-            FL.Partial fs1 -> goArray SPEC st fp next fs1--{-# INLINE_NORMAL foldBreakK #-}-foldBreakK :: forall m a b. (MonadIO m, Unbox a) =>-    Fold m a b -> K.StreamK m (Array a) -> m (b, K.StreamK m (Array a))-foldBreakK (FL.Fold fstep initial extract) stream = do-    res <- initial-    case res of-        FL.Partial fs -> go fs stream-        FL.Done fb -> return (fb, stream)--    where--    {-# INLINE go #-}-    go !fs st = do-        let stop = (, K.nil) <$> extract fs-            single a = yieldk a K.nil-            yieldk (Array contents start end) r =-                let fp = Tuple' end contents-                 in goArray fs r fp start-         in K.foldStream defState yieldk single stop st--    goArray !fs st (Tuple' end _) !cur-        | cur == end = do-            go fs st-    goArray !fs st fp@(Tuple' end contents) !cur = do-        x <- liftIO $ peekWith contents cur-        res <- fstep fs x-        let next = INDEX_NEXT(cur,a)-        case res of-            FL.Done b -> do-                let arr = Array contents next end-                return $! (b, K.cons arr st)-            FL.Partial fs1 -> goArray fs1 st fp next---- | Fold an array stream using the supplied 'Fold'. Returns the fold result--- and the unconsumed stream.------ > foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)------ /Internal/----{-# INLINE_NORMAL foldBreak #-}-foldBreak ::-       (MonadIO m, Unbox a)-    => Fold m a b-    -> StreamK m (A.Array a)-    -> m (b, StreamK m (A.Array a))--- foldBreak f s = fmap fromStreamD <$> foldBreakD f (toStreamD s)-foldBreak = foldBreakK--- If foldBreak performs better than runArrayFoldBreak we can use a rewrite--- rule to rewrite runArrayFoldBreak to fold.--- foldBreak f = runArrayFoldBreak (ChunkFold.fromFold f)------------------------------------------------------------------------------------ Fold to a single Array------------------------------------------------------------------------------------ When we have to take an array partially, take the last part of the array.-{-# INLINE takeArrayListRev #-}-takeArrayListRev :: forall a. Unbox a => Int -> [Array a] -> [Array a]-takeArrayListRev = go--    where--    go _ [] = []-    go n _ | n <= 0 = []-    go n (x:xs) =-        let len = Array.length x-        in if n > len-           then x : go (n - len) xs-           else if n == len-           then [x]-           else let !(Array contents _ end) = x-                    !start = end - (n * SIZE_OF(a))-                 in [Array contents start end]---- When we have to take an array partially, take the last part of the array in--- the first split.-{-# INLINE splitAtArrayListRev #-}-splitAtArrayListRev ::-    forall a. Unbox a => Int -> [Array a] -> ([Array a],[Array a])-splitAtArrayListRev n ls-  | n <= 0 = ([], ls)-  | otherwise = go n ls-    where-        go :: Int -> [Array a] -> ([Array a], [Array a])-        go _  []     = ([], [])-        go m (x:xs) =-            let len = Array.length x-                (xs', xs'') = go (m - len) xs-             in if m > len-                then (x:xs', xs'')-                else if m == len-                then ([x],xs)-                else let !(Array contents start end) = x-                         end1 = end - (m * SIZE_OF(a))-                         arr2 = Array contents start end1-                         arr1 = Array contents end1 end-                      in ([arr1], arr2:xs)------------------------------------------------------------------------------------ Fold to a single Array------------------------------------------------------------------------------------ XXX Both of these implementations of splicing seem to perform equally well.--- We need to perform benchmarks over a range of sizes though.---- CAUTION! length must more than equal to lengths of all the arrays in the--- stream.-{-# INLINE spliceArraysLenUnsafe #-}-spliceArraysLenUnsafe :: (MonadIO m, Unbox a)-    => Int -> Stream m (MutArray a) -> m (MutArray a)-spliceArraysLenUnsafe len buffered = do-    arr <- liftIO $ MA.newPinned len-    D.foldlM' MA.spliceUnsafe (return arr) buffered--{-# INLINE _spliceArrays #-}-_spliceArrays :: (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-_spliceArrays s = do-    buffered <- D.foldr K.cons K.nil s-    len <- K.fold FL.sum (fmap Array.length buffered)-    arr <- liftIO $ MA.newPinned len-    final <- D.foldlM' writeArr (return arr) (toStream buffered)-    return $ A.unsafeFreeze final--    where--    writeArr dst arr = MA.spliceUnsafe dst (A.unsafeThaw arr)--{-# INLINE _spliceArraysBuffered #-}-_spliceArraysBuffered :: (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-_spliceArraysBuffered s = do-    buffered <- D.foldr K.cons K.nil s-    len <- K.fold FL.sum (fmap Array.length buffered)-    A.unsafeFreeze <$>-        spliceArraysLenUnsafe len (fmap A.unsafeThaw (toStream buffered))--{-# INLINE spliceArraysRealloced #-}-spliceArraysRealloced :: forall m a. (MonadIO m, Unbox a)-    => Stream m (Array a) -> m (Array a)-spliceArraysRealloced s = do-    let n = allocBytesToElemCount (undefined :: a) (4 * 1024)-        idst = liftIO $ MA.newPinned n--    arr <- D.foldlM' MA.spliceExp idst (fmap A.unsafeThaw s)-    liftIO $ A.unsafeFreeze <$> MA.rightSize arr---- XXX This should just be "fold A.write"------ | Given a stream of arrays, splice them all together to generate a single--- array. The stream must be /finite/.------ @since 0.7.0-{-# INLINE toArray #-}-toArray :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)-toArray = spliceArraysRealloced--- spliceArrays = _spliceArraysBuffered---- exponentially increasing sizes of the chunks upto the max limit.--- XXX this will be easier to implement with parsers/terminating folds--- With this we should be able to reduce the number of chunks/allocations.--- The reallocation/copy based toArray can also be implemented using this.----{--{-# INLINE toArraysInRange #-}-toArraysInRange :: (MonadIO m, Unbox a)-    => Int -> Int -> Fold m (Array a) b -> Fold m a b-toArraysInRange low high (Fold step initial extract) =--}--{---- | Fold the input to a pure buffered stream (List) of arrays.-{-# INLINE _toArraysOf #-}-_toArraysOf :: (MonadIO m, Unbox a)-    => Int -> Fold m a (Stream Identity (Array a))-_toArraysOf n = FL.groupsOf n (A.writeNF n) FL.toStream--}------------------------------------------------------------------------------------ Elimination - running element parsers------------------------------------------------------------------------------------ GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need--- to make a newtype.-{-# ANN type List NoSpecConstr #-}-newtype List a = List {getList :: [a]}--{---- This can be generalized to any type provided it can be unfolded to a stream--- and it can be combined using a semigroup operation.------ XXX This should be written using CPS (as parseK) if we want it to scale wrt--- to the number of times it can be called on the same stream.-{-# INLINE_NORMAL parseBreakD #-}-parseBreakD ::-       forall m a b. (MonadIO m, MonadThrow m, Unbox a)-    => PRD.Parser a m b-    -> D.Stream m (Array.Array a)-    -> m (b, D.Stream m (Array.Array a))-parseBreakD-    (PRD.Parser pstep initial extract) stream@(D.Stream step state) = do--    res <- initial-    case res of-        PRD.IPartial s -> go SPEC state (List []) s-        PRD.IDone b -> return (b, stream)-        PRD.IError err -> throwM $ ParseError err--    where--    -- "backBuf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go !_ st backBuf !pst = do-        r <- step defState st-        case r of-            D.Yield (Array contents start end) s ->-                gobuf SPEC s backBuf-                    (Tuple' end contents) start pst-            D.Skip s -> go SPEC s backBuf pst-            D.Stop -> do-                b <- extract pst-                return (b, D.nil)--    -- Use strictness on "cur" to keep it unboxed-    gobuf !_ s backBuf (Tuple' end _) !cur !pst-        | cur == end = do-            go SPEC s backBuf pst-    gobuf !_ s backBuf fp@(Tuple' end contents) !cur !pst = do-        x <- liftIO $ peekWith contents cur-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 pst1 ->-                 gobuf SPEC s (List []) fp next pst1-            PR.Partial n pst1 -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let src0 = Prelude.take n (x:getList backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                let !(Array cont1 start end1) = src-                    fp1 = Tuple' end1 cont1-                gobuf SPEC s (List []) fp1 start pst1-            PR.Continue 0 pst1 ->-                gobuf SPEC s (List (x:getList backBuf)) fp next pst1-            PR.Continue n pst1 -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:getList backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                let !(Array cont1 start end1) = src-                    fp1 = Tuple' end1 cont1-                gobuf SPEC s (List buf1) fp1 start pst1-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (b, D.cons arr (D.Stream step s))-            PR.Done n b -> do-                assert (n <= Prelude.length (x:getList backBuf)) (return ())-                let src0 = Prelude.take n (x:getList backBuf)-                    -- XXX create the array in reverse instead-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    -- XXX Use StreamK to avoid adding arbitrary layers of-                    -- constructors every time.-                    str = D.cons arr0 (D.cons arr1 (D.Stream step s))-                return (b, str)-            PR.Error err -> throwM $ ParseError err--}--{-# INLINE_NORMAL parseBreakK #-}-parseBreakK ::-       forall m a b. (MonadIO m, Unbox a)-    => PRD.Parser a m b-    -> K.StreamK m (Array.Array a)-    -> m (Either ParseError b, K.StreamK m (Array.Array a))-parseBreakK (PRD.Parser pstep initial extract) stream = do-    res <- initial-    case res of-        PRD.IPartial s -> go s stream []-        PRD.IDone b -> return (Right b, stream)-        PRD.IError err -> return (Left (ParseError err), stream)--    where--    -- "backBuf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go !pst st backBuf = do-        let stop = goStop pst backBuf -- (, K.nil) <$> extract pst-            single a = yieldk a K.nil-            yieldk arr r = goArray pst backBuf r arr-         in K.foldStream defState yieldk single stop st--    -- Use strictness on "cur" to keep it unboxed-    goArray !pst backBuf st (Array _ cur end) | cur == end = go pst st backBuf-    goArray !pst backBuf st (Array contents cur end) = do-        x <- liftIO $ peekWith contents cur-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 s ->-                 goArray s [] st (Array contents next end)-            PR.Partial n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goArray s [] st src-            PR.Continue 0 s ->-                goArray s (x:backBuf) st (Array contents next end)-            PR.Continue n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goArray s buf1 st src-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (Right b, K.cons arr st)-            PR.Done n b -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    str = K.cons arr0 (K.cons arr1 st)-                return (Right b, str)-            PR.Error err -> do-                let str = K.cons (Array contents cur end) stream-                return (Left (ParseError err), str)--    -- This is a simplified goArray-    goExtract !pst backBuf (Array _ cur end)-        | cur == end = goStop pst backBuf-    goExtract !pst backBuf (Array contents cur end) = do-        x <- liftIO $ peekWith contents cur-        pRes <- pstep pst x-        let next = INDEX_NEXT(cur,a)-        case pRes of-            PR.Partial 0 s ->-                 goExtract s [] (Array contents next end)-            PR.Partial n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let src0 = Prelude.take n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goExtract s [] src-            PR.Continue 0 s ->-                goExtract s backBuf (Array contents next end)-            PR.Continue n s -> do-                assert (n <= Prelude.length (x:backBuf)) (return ())-                let (src0, buf1) = splitAt n (x:backBuf)-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    src = arr0 <> arr1-                goExtract s buf1 src-            PR.Done 0 b -> do-                let arr = Array contents next end-                return (Right b, K.fromPure arr)-            PR.Done n b -> do-                assert (n <= Prelude.length backBuf) (return ())-                let src0 = Prelude.take n backBuf-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                    arr1 = Array contents next end-                    str = K.cons arr0 (K.fromPure arr1)-                return (Right b, str)-            PR.Error err -> do-                let str = K.fromPure (Array contents cur end)-                return (Left (ParseError err), str)--    -- This is a simplified goExtract-    {-# INLINE goStop #-}-    goStop !pst backBuf = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"-            PR.Continue 0 s ->-                goStop s backBuf-            PR.Continue n s -> do-                assert (n <= Prelude.length backBuf) (return ())-                let (src0, buf1) = splitAt n backBuf-                    arr = A.fromListN n (Prelude.reverse src0)-                goExtract s buf1 arr-            PR.Done 0 b ->-                return (Right b, K.nil)-            PR.Done n b -> do-                assert (n <= Prelude.length backBuf) (return ())-                let src0 = Prelude.take n backBuf-                    -- XXX Use fromListRevN once implemented-                    -- arr0 = A.fromListRevN n src0-                    arr0 = A.fromListN n (Prelude.reverse src0)-                return (Right b, K.fromPure arr0)-            PR.Error err ->-                return (Left (ParseError err), K.nil)---- | Parse an array stream using the supplied 'Parser'.  Returns the parse--- result and the unconsumed stream. Throws 'ParseError' if the parse fails.------ /Internal/----{-# INLINE_NORMAL parseBreak #-}-parseBreak ::-       (MonadIO m, Unbox a)-    => PR.Parser a m b-    -> StreamK m (A.Array a)-    -> m (Either ParseError b, StreamK m (A.Array a))-{--parseBreak p s =-    fmap fromStreamD <$> parseBreakD (PRD.fromParserK p) (toStreamD s)--}-parseBreak = parseBreakK------------------------------------------------------------------------------------ Elimination - Running Array Folds and parsers------------------------------------------------------------------------------------ | Note that this is not the same as using a @Parser (Array a) m b@ with the--- regular "Streamly.Internal.Data.IsStream.parse" function. The regular parse--- would consume the input arrays as single unit. This parser parses in the way--- as described in the ChunkFold module. The input arrays are treated as @n@--- element units and can be consumed partially. The remaining elements are--- inserted in the source stream as an array.----{-# INLINE_NORMAL runArrayParserDBreak #-}-runArrayParserDBreak ::-       forall m a b. (MonadIO m, Unbox a)-    => PRD.Parser (Array a) m b-    -> D.Stream m (Array.Array a)-    -> m (Either ParseError b, D.Stream m (Array.Array a))-runArrayParserDBreak-    (PRD.Parser pstep initial extract)-    stream@(D.Stream step state) = do--    res <- initial-    case res of-        PRD.IPartial s -> go SPEC state (List []) s-        PRD.IDone b -> return (Right b, stream)-        PRD.IError err -> return (Left (ParseError err), stream)--    where--    -- "backBuf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go _ st backBuf !pst = do-        r <- step defState st-        case r of-            D.Yield x s -> gobuf SPEC [x] s backBuf pst-            D.Skip s -> go SPEC s backBuf pst-            D.Stop -> goStop backBuf pst--    gobuf !_ [] s backBuf !pst = go SPEC s backBuf pst-    gobuf !_ (x:xs) s backBuf !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                 gobuf SPEC xs s (List []) pst1-            PR.Partial n pst1 -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let src0 = takeArrayListRev n (x:getList backBuf)-                    src  = Prelude.reverse src0 ++ xs-                gobuf SPEC src s (List []) pst1-            PR.Continue 0 pst1 ->-                gobuf SPEC xs s (List (x:getList backBuf)) pst1-            PR.Continue n pst1 -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)-                    src  = Prelude.reverse src0 ++ xs-                gobuf SPEC src s (List buf1) pst1-            PR.Done 0 b -> do-                let str = D.append (D.fromList xs) (D.Stream step s)-                return (Right b, str)-            PR.Done n b -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let src0 = takeArrayListRev n (x:getList backBuf)-                    src = Prelude.reverse src0 ++ xs-                return (Right b, D.append (D.fromList src) (D.Stream step s))-            PR.Error err -> do-                let strm = D.append (D.fromList (x:xs)) (D.Stream step s)-                return (Left (ParseError err), strm)--    -- This is a simplified gobuf-    goExtract _ [] backBuf !pst = goStop backBuf pst-    goExtract _ (x:xs) backBuf !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                 goExtract SPEC xs (List []) pst1-            PR.Partial n pst1 -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let src0 = takeArrayListRev n (x:getList backBuf)-                    src  = Prelude.reverse src0 ++ xs-                goExtract SPEC src (List []) pst1-            PR.Continue 0 pst1 ->-                goExtract SPEC xs (List (x:getList backBuf)) pst1-            PR.Continue n pst1 -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let (src0, buf1) = splitAtArrayListRev n (x:getList backBuf)-                    src  = Prelude.reverse src0 ++ xs-                goExtract SPEC src (List buf1) pst1-            PR.Done 0 b ->-                return (Right b, D.fromList xs)-            PR.Done n b -> do-                assert-                    (n <= sum (map Array.length (x:getList backBuf)))-                    (return ())-                let src0 = takeArrayListRev n (x:getList backBuf)-                    src = Prelude.reverse src0 ++ xs-                return (Right b, D.fromList src)-            PR.Error err ->-                return (Left (ParseError err), D.fromList (x:xs))--    -- This is a simplified goExtract-    {-# INLINE goStop #-}-    goStop backBuf pst = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: runArrayParserDBreak: Partial in extract"-            PR.Continue 0 pst1 ->-                goStop backBuf pst1-            PR.Continue n pst1 -> do-                assert-                    (n <= sum (map Array.length (getList backBuf)))-                    (return ())-                let (src0, buf1) = splitAtArrayListRev n (getList backBuf)-                    src = Prelude.reverse src0-                goExtract SPEC src (List buf1) pst1-            PR.Done 0 b -> return (Right b, D.nil)-            PR.Done n b -> do-                assert-                    (n <= sum (map Array.length (getList backBuf)))-                    (return ())-                let src0 = takeArrayListRev n (getList backBuf)-                    src = Prelude.reverse src0-                return (Right b, D.fromList src)-            PR.Error err ->-                return (Left (ParseError err), D.nil)--{---- | Parse an array stream using the supplied 'Parser'.  Returns the parse--- result and the unconsumed stream. Throws 'ParseError' if the parse fails.------ /Internal/----{-# INLINE parseArr #-}-parseArr ::-       (MonadIO m, MonadThrow m, Unbox a)-    => ASF.Parser a m b-    -> Stream m (A.Array a)-    -> m (b, Stream m (A.Array a))-parseArr p s = fmap fromStreamD <$> parseBreakD p (toStreamD s)--}---- | Fold an array stream using the supplied array stream 'Fold'.------ /Pre-release/----{-# INLINE runArrayFold #-}-runArrayFold :: (MonadIO m, Unbox a) =>-    ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b)-runArrayFold (ChunkFold p) s = fst <$> runArrayParserDBreak p (toStream s)---- | Like 'fold' but also returns the remaining stream.------ /Pre-release/----{-# INLINE runArrayFoldBreak #-}-runArrayFoldBreak :: (MonadIO m, Unbox a) =>-    ChunkFold m a b -> StreamK m (A.Array a) -> m (Either ParseError b, StreamK m (A.Array a))-runArrayFoldBreak (ChunkFold p) s =-    second fromStream <$> runArrayParserDBreak p (toStream s)--{-# ANN type ParseChunksState Fuse #-}-data ParseChunksState x inpBuf st pst =-      ParseChunksInit inpBuf st-    | ParseChunksInitBuf inpBuf-    | ParseChunksInitLeftOver inpBuf-    | ParseChunksStream st inpBuf !pst-    | ParseChunksStop inpBuf !pst-    | ParseChunksBuf inpBuf st inpBuf !pst-    | ParseChunksExtract inpBuf inpBuf !pst-    | ParseChunksYield x (ParseChunksState x inpBuf st pst)--{-# INLINE_NORMAL runArrayFoldManyD #-}-runArrayFoldManyD-    :: (Monad m, Unbox a)-    => ChunkFold m a b-    -> D.Stream m (Array a)-    -> D.Stream m (Either ParseError b)-runArrayFoldManyD-    (ChunkFold (PRD.Parser pstep initial extract)) (D.Stream step state) =--    D.Stream stepOuter (ParseChunksInit [] state)--    where--    {-# INLINE_LATE stepOuter #-}-    -- Buffer is empty, get the first element from the stream, initialize the-    -- fold and then go to stream processing loop.-    stepOuter gst (ParseChunksInit [] st) = do-        r <- step (adaptState gst) st-        case r of-            D.Yield x s -> do-                res <- initial-                case res of-                    PRD.IPartial ps ->-                        return $ D.Skip $ ParseChunksBuf [x] s [] ps-                    PRD.IDone pb -> do-                        let next = ParseChunksInit [x] s-                        return $ D.Skip $ ParseChunksYield (Right pb) next-                    PRD.IError err -> do-                        let next = ParseChunksInitLeftOver []-                        return-                            $ D.Skip-                            $ ParseChunksYield (Left (ParseError err)) next-            D.Skip s -> return $ D.Skip $ ParseChunksInit [] s-            D.Stop   -> return D.Stop--    -- Buffer is not empty, go to buffered processing loop-    stepOuter _ (ParseChunksInit src st) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ D.Skip $ ParseChunksBuf src st [] ps-            PRD.IDone pb ->-                let next = ParseChunksInit src st-                 in return $ D.Skip $ ParseChunksYield (Right pb) next-            PRD.IError err -> do-                let next = ParseChunksInitLeftOver []-                return-                    $ D.Skip-                    $ ParseChunksYield (Left (ParseError err)) next--    -- This is a simplified ParseChunksInit-    stepOuter _ (ParseChunksInitBuf src) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ D.Skip $ ParseChunksExtract src [] ps-            PRD.IDone pb ->-                let next = ParseChunksInitBuf src-                 in return $ D.Skip $ ParseChunksYield (Right pb) next-            PRD.IError err -> do-                let next = ParseChunksInitLeftOver []-                return-                    $ D.Skip-                    $ ParseChunksYield (Left (ParseError err)) next--    -- XXX we just discard any leftover input at the end-    stepOuter _ (ParseChunksInitLeftOver _) = return D.Stop--    -- Buffer is empty, process elements from the stream-    stepOuter gst (ParseChunksStream st backBuf pst) = do-        r <- step (adaptState gst) st-        case r of-            D.Yield x s -> do-                pRes <- pstep pst x-                case pRes of-                    PR.Partial 0 pst1 ->-                        return $ D.Skip $ ParseChunksStream s [] pst1-                    PR.Partial n pst1 -> do-                        assert-                            (n <= sum (map Array.length (x:backBuf)))-                            (return ())-                        let src0 = takeArrayListRev n (x:backBuf)-                            src  = Prelude.reverse src0-                        return $ D.Skip $ ParseChunksBuf src s [] pst1-                    PR.Continue 0 pst1 ->-                        return $ D.Skip $ ParseChunksStream s (x:backBuf) pst1-                    PR.Continue n pst1 -> do-                        assert-                            (n <= sum (map Array.length (x:backBuf)))-                            (return ())-                        let (src0, buf1) = splitAtArrayListRev n (x:backBuf)-                            src  = Prelude.reverse src0-                        return $ D.Skip $ ParseChunksBuf src s buf1 pst1-                    PR.Done 0 b -> do-                        return $ D.Skip $-                            ParseChunksYield (Right b) (ParseChunksInit [] s)-                    PR.Done n b -> do-                        assert-                            (n <= sum (map Array.length (x:backBuf)))-                            (return ())-                        let src0 = takeArrayListRev n (x:backBuf)-                            src = Prelude.reverse src0-                            next = ParseChunksInit src s-                        return-                            $ D.Skip-                            $ ParseChunksYield (Right b) next-                    PR.Error err -> do-                        let next = ParseChunksInitLeftOver []-                        return-                            $ D.Skip-                            $ ParseChunksYield (Left (ParseError err)) next--            D.Skip s -> return $ D.Skip $ ParseChunksStream s backBuf pst-            D.Stop -> return $ D.Skip $ ParseChunksStop backBuf pst--    -- go back to stream processing mode-    stepOuter _ (ParseChunksBuf [] s buf pst) =-        return $ D.Skip $ ParseChunksStream s buf pst--    -- buffered processing loop-    stepOuter _ (ParseChunksBuf (x:xs) s backBuf pst) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ D.Skip $ ParseChunksBuf xs s [] pst1-            PR.Partial n pst1 -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let src0 = takeArrayListRev n (x:backBuf)-                    src  = Prelude.reverse src0 ++ xs-                return $ D.Skip $ ParseChunksBuf src s [] pst1-            PR.Continue 0 pst1 ->-                return $ D.Skip $ ParseChunksBuf xs s (x:backBuf) pst1-            PR.Continue n pst1 -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let (src0, buf1) = splitAtArrayListRev n (x:backBuf)-                    src  = Prelude.reverse src0 ++ xs-                return $ D.Skip $ ParseChunksBuf src s buf1 pst1-            PR.Done 0 b ->-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInit xs s)-            PR.Done n b -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let src0 = takeArrayListRev n (x:backBuf)-                    src = Prelude.reverse src0 ++ xs-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInit src s)-            PR.Error err -> do-                let next = ParseChunksInitLeftOver []-                return-                    $ D.Skip-                    $ ParseChunksYield (Left (ParseError err)) next--    -- This is a simplified ParseChunksBuf-    stepOuter _ (ParseChunksExtract [] buf pst) =-        return $ D.Skip $ ParseChunksStop buf pst--    stepOuter _ (ParseChunksExtract (x:xs) backBuf pst) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ D.Skip $ ParseChunksExtract xs [] pst1-            PR.Partial n pst1 -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let src0 = takeArrayListRev n (x:backBuf)-                    src  = Prelude.reverse src0 ++ xs-                return $ D.Skip $ ParseChunksExtract src [] pst1-            PR.Continue 0 pst1 ->-                return $ D.Skip $ ParseChunksExtract xs (x:backBuf) pst1-            PR.Continue n pst1 -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let (src0, buf1) = splitAtArrayListRev n (x:backBuf)-                    src  = Prelude.reverse src0 ++ xs-                return $ D.Skip $ ParseChunksExtract src buf1 pst1-            PR.Done 0 b ->-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)-            PR.Done n b -> do-                assert (n <= sum (map Array.length (x:backBuf))) (return ())-                let src0 = takeArrayListRev n (x:backBuf)-                    src = Prelude.reverse src0 ++ xs-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)-            PR.Error err -> do-                let next = ParseChunksInitLeftOver []-                return-                    $ D.Skip-                    $ ParseChunksYield (Left (ParseError err)) next---    -- This is a simplified ParseChunksExtract-    stepOuter _ (ParseChunksStop backBuf pst) = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "runArrayFoldManyD: Partial in extract"-            PR.Continue 0 pst1 ->-                return $ D.Skip $ ParseChunksStop backBuf pst1-            PR.Continue n pst1 -> do-                assert (n <= sum (map Array.length backBuf)) (return ())-                let (src0, buf1) = splitAtArrayListRev n backBuf-                    src  = Prelude.reverse src0-                return $ D.Skip $ ParseChunksExtract src buf1 pst1-            PR.Done 0 b ->-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitLeftOver [])-            PR.Done n b -> do-                assert (n <= sum (map Array.length backBuf)) (return ())-                let src0 = takeArrayListRev n backBuf-                    src = Prelude.reverse src0-                return-                    $ D.Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)-            PR.Error err -> do-                let next = ParseChunksInitLeftOver []-                return-                    $ D.Skip-                    $ ParseChunksYield (Left (ParseError err)) next--    stepOuter _ (ParseChunksYield a next) = return $ D.Yield a next---- | Apply an 'ChunkFold' repeatedly on an array stream and emit the--- fold outputs in the output stream.------ See "Streamly.Data.Stream.foldMany" for more details.------ /Pre-release/-{-# INLINE runArrayFoldMany #-}-runArrayFoldMany-    :: (Monad m, Unbox a)-    => ChunkFold m a b-    -> StreamK m (Array a)-    -> StreamK m (Either ParseError b)-runArrayFoldMany p m = fromStream $ runArrayFoldManyD p (toStream m)
− src/Streamly/Internal/Data/Stream/Common.hs
@@ -1,105 +0,0 @@-{-# OPTIONS_GHC -Wno-orphans #-}---- |--- Module      : Streamly.Internal.Data.Stream.Common--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Low level functions using StreamK as the intermediate stream type. These--- functions are used in other stream modules to implement their instances.----module Streamly.Internal.Data.Stream.Common-    (-    -- * Conversion operations-      fromList-    , toList--    -- * Fold operations-    , foldr-    , foldl'-    , fold--    -- * Zip style operations-    , eqBy-    , cmpBy-    )-where--#include "inline.hs"--import Streamly.Internal.Data.Fold.Type (Fold (..))--import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D--import Prelude hiding (foldr, repeat)----------------------------------------------------------------------------------- Conversions----------------------------------------------------------------------------------- |--- @--- fromList = 'Prelude.foldr' 'K.cons' 'K.nil'--- @------ Construct a stream from a list of pure values. This is more efficient than--- 'K.fromFoldable' for serial streams.----{-# INLINE_EARLY fromList #-}-fromList :: Monad m => [a] -> K.StreamK m a-fromList = D.toStreamK . D.fromList-{-# RULES "fromList fallback to StreamK" [1]-    forall a. D.toStreamK (D.fromList a) = K.fromFoldable a #-}---- | Convert a stream into a list in the underlying monad.----{-# INLINE toList #-}-toList :: Monad m => K.StreamK m a -> m [a]-toList m = D.toList $ D.fromStreamK m----------------------------------------------------------------------------------- Folds---------------------------------------------------------------------------------{-# INLINE foldrM #-}-foldrM :: Monad m => (a -> m b -> m b) -> m b -> K.StreamK m a -> m b-foldrM step acc m = D.foldrM step acc $ D.fromStreamK m--{-# INLINE foldr #-}-foldr :: Monad m => (a -> b -> b) -> b -> K.StreamK m a -> m b-foldr f z = foldrM (\a b -> f a <$> b) (return z)---- | Strict left associative fold.----{-# INLINE foldl' #-}-foldl' ::-    Monad m => (b -> a -> b) -> b -> K.StreamK m a -> m b-foldl' step begin m = D.foldl' step begin $ D.fromStreamK m---{-# INLINE fold #-}-fold :: Monad m => Fold m a b -> K.StreamK m a -> m b-fold fld m = D.fold fld $ D.fromStreamK m----------------------------------------------------------------------------------- Comparison----------------------------------------------------------------------------------- | Compare two streams for equality----{-# INLINE eqBy #-}-eqBy :: Monad m =>-    (a -> b -> Bool) -> K.StreamK m a -> K.StreamK m b -> m Bool-eqBy f m1 m2 = D.eqBy f (D.fromStreamK m1) (D.fromStreamK m2)---- | Compare two streams----{-# INLINE cmpBy #-}-cmpBy-    :: Monad m-    => (a -> b -> Ordering) -> K.StreamK m a -> K.StreamK m b -> m Ordering-cmpBy f m1 m2 = D.cmpBy f (D.fromStreamK m1) (D.fromStreamK m2)
+ src/Streamly/Internal/Data/Stream/Container.hs view
@@ -0,0 +1,302 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Stream.Container+-- Copyright   : (c) 2019 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Stream operations that require transformers or containers like Set or Map.++module Streamly.Internal.Data.Stream.Container+    (+      nub++    -- * Joins for unconstrained types+    , joinLeftGeneric+    , joinOuterGeneric++    -- * Joins with Ord constraint+    , joinInner+    , joinLeft+    , joinOuter+    )+where++#include "inline.hs"++import Control.Monad.IO.Class (MonadIO)+import Control.Monad.Trans.State.Strict (get, put)+import Data.Function ((&))+import Data.Maybe (isJust)+import Streamly.Internal.Data.Stream.Step (Step(..))+import Streamly.Internal.Data.Stream.Type+    (Stream(..), mkCross, unCross)++import qualified Data.Map.Strict as Map+import qualified Data.Set as Set+import qualified Streamly.Data.Fold as Fold+import qualified Streamly.Internal.Data.Array.Generic as Array+import qualified Streamly.Internal.Data.MutArray.Type as MA+import qualified Streamly.Internal.Data.Stream.Type as Stream+import qualified Streamly.Internal.Data.Stream.Nesting as Stream+import qualified Streamly.Internal.Data.Stream.Generate as Stream+import qualified Streamly.Internal.Data.Stream.Transform as Stream+import qualified Streamly.Internal.Data.Stream.Transformer as Stream++#include "DocTestDataStream.hs"++-- | The memory used is proportional to the number of unique elements in the+-- stream. If we want to limit the memory we can just use "take" to limit the+-- uniq elements in the stream.+{-# INLINE_NORMAL nub #-}+nub :: (Monad m, Ord a) => Stream m a -> Stream m a+nub (Stream step1 state1) = Stream step (Set.empty, state1)++    where++    step gst (set, st) = do+        r <- step1 gst st+        return+            $ case r of+                Yield x s ->+                    if Set.member x set+                    then Skip (set, s)+                    else Yield x (Set.insert x set, s)+                Skip s -> Skip (set, s)+                Stop -> Stop++-- XXX Generate error if a duplicate insertion is attempted?+toMap ::  (Monad m, Ord k) => Stream m (k, v) -> m (Map.Map k v)+toMap =+    let f = Fold.foldl' (\kv (k, b) -> Map.insert k b kv) Map.empty+     in Stream.fold f++-- If the second stream is too big it can be partitioned based on hashes and+-- then we can process one parition at a time.+--+-- XXX An IntMap may be faster when the keys are Int.+-- XXX Use hashmap instead of map?+--+-- | Like 'joinInner' but uses a 'Map' for efficiency.+--+-- If the input streams have duplicate keys, the behavior is undefined.+--+-- For space efficiency use the smaller stream as the second stream.+--+-- Space: O(n)+--+-- Time: O(m + n)+--+-- /Pre-release/+{-# INLINE joinInner #-}+joinInner :: (Monad m, Ord k) =>+    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)+joinInner s1 s2 =+    Stream.concatEffect $ do+        km <- toMap s2+        pure $ Stream.mapMaybe (joinAB km) s1++    where++    joinAB kvm (k, a) =+        case k `Map.lookup` kvm of+            Just b -> Just (k, a, b)+            Nothing -> Nothing++-- XXX We can do this concurrently.+-- XXX If the second stream is sorted and passed as an Array or a seek capable+-- stream then we could use binary search if we have an Ord instance or+-- Ordering returning function. The time complexity would then become (m x log+-- n).++-- XXX Check performance of StreamD vs StreamK++-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s+-- that are not equal to any @b@ emit @(a, Nothing)@.+--+-- The second stream is evaluated multiple times. If the stream is a+-- consume-once stream then the caller should cache it in an 'Data.Array.Array'+-- before calling this function. Caching may also improve performance if the+-- stream is expensive to evaluate.+--+-- >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)+--+-- Space: O(n) assuming the second stream is cached in memory.+--+-- Time: O(m x n)+--+-- /Unimplemented/+{-# INLINE joinLeftGeneric #-}+joinLeftGeneric :: Monad m =>+    (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)+joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do+    a <- mkCross (Stream.liftInner s1)+    -- XXX should we use StreamD monad here?+    -- XXX Is there a better way to perform some action at the end of a loop+    -- iteration?+    mkCross (Stream.fromEffect $ put False)+    let final = Stream.concatEffect $ do+            r <- get+            if r+            then pure Stream.nil+            else pure (Stream.fromPure Nothing)+    b <- mkCross (fmap Just (Stream.liftInner s2) `Stream.append` final)+    case b of+        Just b1 ->+            if a `eq` b1+            then do+                mkCross (Stream.fromEffect $ put True)+                return (a, Just b1)+            else mkCross Stream.nil+        Nothing -> return (a, Nothing)++-- XXX rename to joinLeftOrd?++-- | A more efficient 'joinLeft' using a hashmap for efficiency.+--+-- Space: O(n)+--+-- Time: O(m + n)+--+-- /Pre-release/+{-# INLINE joinLeft #-}+joinLeft :: (Ord k, Monad m) =>+    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)+joinLeft s1 s2 =+    Stream.concatEffect $ do+        km <- toMap s2+        return $ fmap (joinAB km) s1++            where++            joinAB km (k, a) =+                case k `Map.lookup` km of+                    Just b -> (k, a, Just b)+                    Nothing -> (k, a, Nothing)++-- XXX We can do this concurrently.++-- XXX Check performance of StreamD vs StreamK++-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those+-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but+-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,+-- Just b)@.+--+-- For space efficiency use the smaller stream as the second stream.+--+-- Space: O(n)+--+-- Time: O(m x n)+--+-- /Pre-release/+{-# INLINE joinOuterGeneric #-}+joinOuterGeneric :: MonadIO m =>+       (a -> b -> Bool)+    -> Stream m a+    -> Stream m b+    -> Stream m (Maybe a, Maybe b)+joinOuterGeneric eq s1 s =+    Stream.concatEffect $ do+        inputArr <- Array.fromStream s+        let len = Array.length inputArr+        foundArr <-+            Stream.fold+            (MA.writeN len)+            (Stream.fromList (Prelude.replicate len False))+        return $ go inputArr foundArr `Stream.append` leftOver inputArr foundArr++    where++    leftOver inputArr foundArr =+            let stream1 = Array.read inputArr+                stream2 = Stream.unfold MA.reader foundArr+            in Stream.filter+                    isJust+                    ( Stream.zipWith (\x y ->+                        if y+                        then Nothing+                        else Just (Nothing, Just x)+                        ) stream1 stream2+                    ) & Stream.catMaybes++    evalState = Stream.evalStateT (return False) . unCross++    go inputArr foundArr = evalState $ do+        a <- mkCross (Stream.liftInner s1)+        -- XXX should we use StreamD monad here?+        -- XXX Is there a better way to perform some action at the end of a loop+        -- iteration?+        mkCross (Stream.fromEffect $ put False)+        let final = Stream.concatEffect $ do+                r <- get+                if r+                then pure Stream.nil+                else pure (Stream.fromPure Nothing)+        (i, b) <-+            let stream = Array.read inputArr+             in mkCross+                (Stream.indexed $ fmap Just (Stream.liftInner stream) `Stream.append` final)++        case b of+            Just b1 ->+                if a `eq` b1+                then do+                    mkCross (Stream.fromEffect $ put True)+                    MA.putIndex i foundArr True+                    return (Just a, Just b1)+                else mkCross Stream.nil+            Nothing -> return (Just a, Nothing)++-- Put the b's that have been paired, in another hash or mutate the hash to set+-- a flag. At the end go through @Stream m b@ and find those that are not in that+-- hash to return (Nothing, b).++-- | Like 'joinOuter' but uses a 'Map' for efficiency.+--+-- Space: O(m + n)+--+-- Time: O(m + n)+--+-- /Pre-release/+{-# INLINE joinOuter #-}+joinOuter ::+    (Ord k, MonadIO m) =>+    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)+joinOuter s1 s2 =+    Stream.concatEffect $ do+        km1 <- kvFold s1+        km2 <- kvFold s2++        -- XXX Not sure if toList/fromList would fuse optimally. We may have to+        -- create a fused Map.toStream function.+        let res1 = fmap (joinAB km2)+                        $ Stream.fromList $ Map.toList km1+                    where+                    joinAB km (k, a) =+                        case k `Map.lookup` km of+                            Just b -> (k, Just a, Just b)+                            Nothing -> (k, Just a, Nothing)++        -- XXX We can take advantage of the lookups in the first pass above to+        -- reduce the number of lookups in this pass. If we keep mutable cells+        -- in the second Map, we can flag it in the first pass and not do any+        -- lookup in the second pass if it is flagged.+        let res2 = Stream.mapMaybe (joinAB km1)+                        $ Stream.fromList $ Map.toList km2+                    where+                    joinAB km (k, b) =+                        case k `Map.lookup` km of+                            Just _ -> Nothing+                            Nothing -> Just (k, Nothing, Just b)++        return $ Stream.append res1 res2++        where++        -- XXX Generate error if a duplicate insertion is attempted?+        kvFold =+            let f = Fold.foldl' (\kv (k, b) -> Map.insert k b kv) Map.empty+             in Stream.fold f
− src/Streamly/Internal/Data/Stream/Cross.hs
@@ -1,143 +0,0 @@-{-# LANGUAGE UndecidableInstances #-}---- |--- Module      : Streamly.Internal.Data.Stream.Cross--- Copyright   : (c) 2017 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Stream.Cross-    (-      CrossStream (..)-    )-where--import Control.Monad.Catch (MonadThrow, throwM)-import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Applicative (liftA2)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Functor.Identity (Identity(..))-import GHC.Exts (IsList(..), IsString(..))-import Streamly.Internal.Data.Stream.Type (Stream)--import qualified Streamly.Internal.Data.Stream.Type as Stream-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K---- $setup--- >>> import Streamly.Internal.Data.Stream.Cross (CrossStream(..))--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Stream as Stream----------------------------------------------------------------------------------- Stream with a cross product style monad instance----------------------------------------------------------------------------------- | A newtype wrapper for the 'Stream' type with a cross product style monad--- instance.------ Semigroup instance appends two streams.------ A 'Monad' bind behaves like a @for@ loop:------ >>> :{--- Stream.fold Fold.toList $ unCrossStream $ do---      x <- CrossStream (Stream.fromList [1,2]) -- foreach x in stream---      return x--- :}--- [1,2]------ Nested monad binds behave like nested @for@ loops:------ >>> :{--- Stream.fold Fold.toList $ unCrossStream $ do---     x <- CrossStream (Stream.fromList [1,2]) -- foreach x in stream---     y <- CrossStream (Stream.fromList [3,4]) -- foreach y in stream---     return (x, y)--- :}--- [(1,3),(1,4),(2,3),(2,4)]----newtype CrossStream m a = CrossStream {unCrossStream :: Stream m a}-        deriving (Functor, Semigroup, Monoid, Foldable)---- Pure (Identity monad) stream instances-deriving instance Traversable (CrossStream Identity)-deriving instance IsList (CrossStream Identity a)-deriving instance (a ~ Char) => IsString (CrossStream Identity a)-deriving instance Eq a => Eq (CrossStream Identity a)-deriving instance Ord a => Ord (CrossStream Identity a)-deriving instance Show a => Show (CrossStream Identity a)-deriving instance Read a => Read (CrossStream Identity a)----------------------------------------------------------------------------------- Applicative----------------------------------------------------------------------------------- Note: we need to define all the typeclass operations because we want to--- INLINE them.-instance Monad m => Applicative (CrossStream m) where-    {-# INLINE pure #-}-    pure x = CrossStream (Stream.fromPure x)--    {-# INLINE (<*>) #-}-    (CrossStream s1) <*> (CrossStream s2) =-        CrossStream (Stream.crossApply s1 s2)-    -- (<*>) = K.crossApply--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)--    {-# INLINE (*>) #-}-    (CrossStream s1) *> (CrossStream s2) =-        CrossStream (Stream.crossApplySnd s1 s2)-    -- (*>)  = K.crossApplySnd--    {-# INLINE (<*) #-}-    (CrossStream s1) <* (CrossStream s2) =-        CrossStream (Stream.crossApplyFst s1 s2)-    -- (<*)  = K.crossApplyFst----------------------------------------------------------------------------------- Monad---------------------------------------------------------------------------------instance Monad m => Monad (CrossStream m) where-    return = pure--    -- Benchmarks better with StreamD bind and pure:-    -- toList, filterAllout, *>, *<, >> (~2x)-    ---    -- pure = Stream . D.fromStreamD . D.fromPure-    -- m >>= f = D.fromStreamD $ D.concatMap (D.toStreamD . f) (D.toStreamD m)--    -- Benchmarks better with CPS bind and pure:-    -- Prime sieve (25x)-    -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)-    ---    {-# INLINE (>>=) #-}-    (>>=) (CrossStream m) f =-        CrossStream-            (Stream.fromStreamK-                $ K.bindWith-                    K.append-                    (Stream.toStreamK m)-                    (Stream.toStreamK . unCrossStream . f))--    {-# INLINE (>>) #-}-    (>>) = (*>)----------------------------------------------------------------------------------- Transformers---------------------------------------------------------------------------------instance (MonadIO m) => MonadIO (CrossStream m) where-    liftIO x = CrossStream (Stream.fromEffect $ liftIO x)--instance MonadTrans CrossStream where-    {-# INLINE lift #-}-    lift x = CrossStream (Stream.fromEffect x)--instance (MonadThrow m) => MonadThrow (CrossStream m) where-    throwM = lift . throwM
src/Streamly/Internal/Data/Stream/Eliminate.hs view
@@ -1,233 +1,167 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Data.Stream.Eliminate--- Copyright   : (c) 2017 Composewell Technologies+-- Copyright   : (c) 2018 Composewell Technologies+--               (c) Roman Leshchinskiy 2008-2010 -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC------ This module contains functions ending in the shape:------ @--- Stream m a -> m b--- @------ We call them stream folding functions, they reduce a stream @Stream m a@ to--- a monadic value @m b@. +-- A few functions in this module have been adapted from the vector package+-- (c) Roman Leshchinskiy.+-- module Streamly.Internal.Data.Stream.Eliminate     (-    -- * Running Examples-    -- $setup-     -- * Running a 'Fold'-    --  See "Streamly.Internal.Data.Fold".       fold-    , foldBreak-    , foldBreak2-    , foldEither-    , foldEither2-    , foldConcat -    -- * Builders-    , foldAdd-    , foldAddLazy--    -- * Running a 'Parser'-    -- "Streamly.Internal.Data.Parser".+    -- -- * Running a 'Parser'     , parse-    --, parseK     , parseD-    --, parseBreak+    , parseBreak     , parseBreakD      -- * Stream Deconstruction-    -- | foldr and foldl do not provide the remaining stream.  'uncons' is more-    -- general, as it can be used to implement those as well.  It allows to use-    -- the stream one element at a time, and we have the remaining stream all-    -- the time.     , uncons-    , init      -- * Right Folds     , foldrM     , foldr+    , foldrMx+    , foldr1      -- * Left Folds-    -- Lazy left folds are useful only for reversing the stream-    , foldlS+    , foldlM'+    , foldl'+    , foldlMx'+    , foldlx' -    -- * Multi-Stream folds-    -- Full equivalence+    -- * Specific Fold Functions+    , drain+    , mapM_ -- Map and Fold+    , null+    , head+    , headElse+    , tail+    , last+    , elem+    , notElem+    , all+    , any+    , maximum+    , maximumBy+    , minimum+    , minimumBy+    , lookup+    , findM+    , find+    , (!!)+    , the++    -- * To containers+    , toList+    , toListRev++    -- * Multi-Stream Folds+    -- ** Comparisons+    -- | These should probably be expressed using zipping operations.     , eqBy     , cmpBy -    -- finding subsequences+    -- ** Substreams+    -- | These should probably be expressed using parsers.     , isPrefixOf     , isInfixOf     , isSuffixOf+    , isSuffixOfUnbox     , isSubsequenceOf--    -- trimming sequences     , stripPrefix-    -- , stripInfix     , stripSuffix+    , stripSuffixUnbox     ) where  #include "inline.hs" +import Control.Exception (assert) import Control.Monad.IO.Class (MonadIO(..)) import Foreign.Storable (Storable)-import Streamly.Internal.Data.Parser (Parser (..), ParseError (..))-import Streamly.Internal.Data.Unboxed (Unbox)-import Streamly.Internal.Data.Stream.Type (Stream)+import GHC.Exts (SpecConstrAnnotation(..))+import GHC.Types (SPEC(..))+import Streamly.Internal.Data.Parser (ParseError(..))+import Streamly.Internal.Data.SVar.Type (defState)+import Streamly.Internal.Data.Unbox (Unbox) +import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))+ import qualified Streamly.Internal.Data.Array.Type as Array import qualified Streamly.Internal.Data.Fold as Fold-import qualified Streamly.Internal.Data.Parser.ParserD as PRD-import qualified Streamly.Internal.Data.Parser.ParserK.Type as PRK-import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Stream.StreamK as K--import Streamly.Internal.Data.Stream.Bottom-import Streamly.Internal.Data.Stream.Type hiding (Stream)+import qualified Streamly.Internal.Data.Parser as PR+import qualified Streamly.Internal.Data.Parser as PRD+import qualified Streamly.Internal.Data.Stream.Generate as StreamD+import qualified Streamly.Internal.Data.Stream.Nesting as Nesting+import qualified Streamly.Internal.Data.Stream.Transform as StreamD -import Prelude hiding (foldr, init, reverse)+import Prelude hiding+       ( Foldable(..), all, any, head, last, lookup, mapM, mapM_+       , notElem, splitAt, tail, (!!))+import Data.Foldable (length)+import Streamly.Internal.Data.Stream.Type --- $setup--- >>> :m--- >>> import Streamly.Internal.Data.Stream (Stream)--- >>> import qualified Streamly.Internal.Data.Stream as Stream--- >>> import qualified Streamly.Internal.Data.Parser as Parser--- >>> import qualified Streamly.Internal.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Unfold as Unfold+#include "DocTestDataStream.hs"  --------------------------------------------------------------------------------- Deconstruction+-- Elimination by Folds ------------------------------------------------------------------------------ --- | Decompose a stream into its head and tail. If the stream is empty, returns--- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is--- the head of the stream and @ma@ its tail.------ Properties:------ >>> Nothing <- Stream.uncons Stream.nil--- >>> Just ("a", t) <- Stream.uncons (Stream.cons "a" Stream.nil)------ This can be used to consume the stream in an imperative manner one element--- at a time, as it just breaks down the stream into individual elements and we--- can loop over them as we deem fit. For example, this can be used to convert--- a streamly stream into other stream types.------ All the folds in this module can be expressed in terms of 'uncons', however,--- this is generally less efficient than specific folds because it takes apart--- the stream one element at a time, therefore, does not take adavantage of--- stream fusion.------ 'foldBreak' is a more general way of consuming a stream piecemeal.------ >>> :{--- uncons xs = do---     r <- Stream.foldBreak Fold.one xs---     return $ case r of---         (Nothing, _) -> Nothing---         (Just h, t) -> Just (h, t)--- :}------ /CPS/----{-# INLINE uncons #-}-uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))-uncons m = fmap (fmap (fmap fromStreamK)) $ K.uncons (toStreamK m)---- | Extract all but the last element of the stream, if any.------ Note: This will end up buffering the entire stream.------ /Pre-release/-{-# INLINE init #-}-init :: Monad m => Stream m a -> m (Maybe (Stream m a))-init m = fmap (fmap fromStreamK) $ K.init $ toStreamK m- ------------------------------------------------------------------------------ -- Right Folds ------------------------------------------------------------------------------ --- | Right associative/lazy pull fold. @foldrM build final stream@ constructs--- an output structure using the step function @build@. @build@ is invoked with--- the next input element and the remaining (lazy) tail of the output--- structure. It builds a lazy output expression using the two. When the "tail--- structure" in the output expression is evaluated it calls @build@ again thus--- lazily consuming the input @stream@ until either the output expression built--- by @build@ is free of the "tail" or the input is exhausted in which case--- @final@ is used as the terminating case for the output structure. For more--- details see the description in the previous section.------ Example, determine if any element is 'odd' in a stream:------ >>> s = Stream.fromList (2:4:5:undefined)--- >>> step x xs = if odd x then return True else xs--- >>> Stream.foldrM step (return False) s--- True----{-# INLINE foldrM #-}-foldrM ::  Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b-foldrM step acc m = D.foldrM step acc $ toStreamD m---- | Right fold, lazy for lazy monads and pure streams, and strict for strict--- monads.------ Please avoid using this routine in strict monads like IO unless you need a--- strict right fold. This is provided only for use in lazy monads (e.g.--- Identity) or pure streams. Note that with this signature it is not possible--- to implement a lazy foldr when the monad @m@ is strict. In that case it--- would be strict in its accumulator and therefore would necessarily consume--- all its input.------ >>> foldr f z = Stream.foldrM (\a b -> f a <$> b) (return z)----{-# INLINE foldr #-}-foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b-foldr f z = foldrM (\a b -> f a <$> b) (return z)+{-# INLINE_NORMAL foldr1 #-}+foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)+foldr1 f m = do+     r <- uncons m+     case r of+         Nothing   -> return Nothing+         Just (h, t) -> fmap Just (foldr f h t)  --------------------------------------------------------------------------------- Left Folds+-- Parsers ------------------------------------------------------------------------------ --- | Lazy left fold to a stream.-{-# INLINE foldlS #-}-foldlS ::-    (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b-foldlS f z =-    fromStreamK-        . K.foldlS-            (\xs x -> toStreamK $ f (fromStreamK xs) x)-            (toStreamK z)-        . toStreamK+-- Inlined definition. Without the inline "serially/parser/take" benchmark+-- degrades and parseMany does not fuse. Even using "inline" at the callsite+-- does not help.+{-# INLINE splitAt #-}+splitAt :: Int -> [a] -> ([a],[a])+splitAt n ls+  | n <= 0 = ([], ls)+  | otherwise          = splitAt' n ls+    where+        splitAt' :: Int -> [a] -> ([a], [a])+        splitAt' _  []     = ([], [])+        splitAt' 1  (x:xs) = ([x], xs)+        splitAt' m  (x:xs) = (x:xs', xs'')+          where+            (xs', xs'') = splitAt' (m - 1) xs ---------------------------------------------------------------------------------- Running a Parser-------------------------------------------------------------------------------+-- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need+-- to make a newtype.+{-# ANN type List NoSpecConstr #-}+newtype List a = List {getList :: [a]} --- | Parse a stream using the supplied ParserD 'PRD.Parser'.------ /Internal/---+-- | Run a 'Parse' over a stream. {-# INLINE_NORMAL parseD #-}-parseD :: Monad m => PRD.Parser a m b -> Stream m a -> m (Either ParseError b)-parseD p = D.parseD p . toStreamD---- XXX Drive directly as parserK rather than converting to parserD first.---- | Parse a stream using the supplied ParserK 'PRK.Parser'.------ /Internal/---{-# INLINE parseK #-}---parseK :: Monad m => PRK.Parser a m b -> Stream m a -> m (Either ParseError b)---parseK = parse+parseD+    :: Monad m+    => PRD.Parser a m b+    -> Stream m a+    -> m (Either ParseError b)+parseD parser strm = do+    (b, _) <- parseBreakD parser strm+    return b  -- | Parse a stream using the supplied 'Parser'. --@@ -242,25 +176,486 @@ -- Note: @parse p@ is not the same as  @head . parseMany p@ on an empty stream. -- {-# INLINE [3] parse #-}-parse :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b)+parse :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b) parse = parseD +-- XXX It may be a good idea to use constant sized chunks for backtracking. We+-- can take a byte stream but when we have to backtrack we create constant+-- sized chunks. We maintain one forward list and one backward list of constant+-- sized chunks, and a last backtracking offset. That way we just need lists of+-- contents and no need to maintain start/end pointers for individual arrays,+-- reducing bookkeeping work.++-- | Run a 'Parse' over a stream and return rest of the Stream. {-# INLINE_NORMAL parseBreakD #-}-parseBreakD :: Monad m =>-    PRD.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)-parseBreakD parser strm = do-    (b, strmD) <- D.parseBreakD parser (toStreamD strm)-    return $! (b, fromStreamD strmD)+parseBreakD+    :: Monad m+    => PRD.Parser a m b+    -> Stream m a+    -> m (Either ParseError b, Stream m a)+parseBreakD (PRD.Parser pstep initial extract) stream@(Stream step state) = do+    res <- initial+    case res of+        PRD.IPartial s -> go SPEC state (List []) s+        PRD.IDone b -> return (Right b, stream)+        PRD.IError err -> return (Left (ParseError err), stream) +    where++    -- "buf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    go !_ st buf !pst = do+        r <- step defState st+        case r of+            Yield x s -> do+                pRes <- pstep pst x+                case pRes of+                    PR.Partial 0 pst1 -> go SPEC s (List []) pst1+                    PR.Partial 1 pst1 -> go1 SPEC s x pst1+                    PR.Partial n pst1 -> do+                        assert (n <= length (x:getList buf)) (return ())+                        let src0 = Prelude.take n (x:getList buf)+                            src  = Prelude.reverse src0+                        gobuf SPEC s (List []) (List src) pst1+                    PR.Continue 0 pst1 -> go SPEC s (List (x:getList buf)) pst1+                    PR.Continue 1 pst1 -> gobuf SPEC s buf (List [x]) pst1+                    PR.Continue n pst1 -> do+                        assert (n <= length (x:getList buf)) (return ())+                        let (src0, buf1) = splitAt n (x:getList buf)+                            src  = Prelude.reverse src0+                        gobuf SPEC s (List buf1) (List src) pst1+                    PR.Done 0 b -> return (Right b, Stream step s)+                    PR.Done n b -> do+                        assert (n <= length (x:getList buf)) (return ())+                        let src0 = Prelude.take n (x:getList buf)+                            src  = Prelude.reverse src0+                        -- XXX This would make it quadratic. We should probably+                        -- use StreamK if we have to append many times.+                        return+                            ( Right b,+                              Nesting.append (fromList src) (Stream step s))+                    PR.Error err -> do+                        let src = Prelude.reverse $ x:getList buf+                        return+                            ( Left (ParseError err)+                            , Nesting.append (fromList src) (Stream step s)+                            )++            Skip s -> go SPEC s buf pst+            Stop -> goStop SPEC buf pst++    go1 _ s x !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                go SPEC s (List []) pst1+            PR.Partial 1 pst1 -> do+                go1 SPEC s x pst1+            PR.Partial n _ ->+                error $ "parseBreak: parser bug, go1: Partial n = " ++ show n+            PR.Continue 0 pst1 ->+                go SPEC s (List [x]) pst1+            PR.Continue 1 pst1 ->+                go1 SPEC s x pst1+            PR.Continue n _ -> do+                error $ "parseBreak: parser bug, go1: Continue n = " ++ show n+            PR.Done 0 b -> do+                return (Right b, Stream step s)+            PR.Done 1 b -> do+                return (Right b, StreamD.cons x (Stream step s))+            PR.Done n _ -> do+                error $ "parseBreak: parser bug, go1: Done n = " ++ show n+            PR.Error err ->+                return+                    ( Left (ParseError err)+                    , Nesting.append (fromPure x) (Stream step s)+                    )++    gobuf !_ s buf (List []) !pst = go SPEC s buf pst+    gobuf !_ s buf (List (x:xs)) !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                gobuf SPEC s (List []) (List xs) pst1+            PR.Partial n pst1 -> do+                assert (n <= length (x:getList buf)) (return ())+                let src0 = Prelude.take n (x:getList buf)+                    src  = Prelude.reverse src0 ++ xs+                gobuf SPEC s (List []) (List src) pst1+            PR.Continue 0 pst1 ->+                gobuf SPEC s (List (x:getList buf)) (List xs) pst1+            PR.Continue 1 pst1 ->+                gobuf SPEC s buf (List (x:xs)) pst1+            PR.Continue n pst1 -> do+                assert (n <= length (x:getList buf)) (return ())+                let (src0, buf1) = splitAt n (x:getList buf)+                    src  = Prelude.reverse src0 ++ xs+                gobuf SPEC s (List buf1) (List src) pst1+            PR.Done n b -> do+                assert (n <= length (x:getList buf)) (return ())+                let src0 = Prelude.take n (x:getList buf)+                    src  = Prelude.reverse src0+                return (Right b, Nesting.append (fromList src) (Stream step s))+            PR.Error err -> do+                let src = (Prelude.reverse $ getList buf) ++ x:xs+                return+                    ( Left (ParseError err)+                    , Nesting.append (fromList src) (Stream step s)+                    )++    -- This is simplified gobuf+    goExtract !_ buf (List []) !pst = goStop SPEC buf pst+    goExtract !_ buf (List (x:xs)) !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                goExtract SPEC (List []) (List xs) pst1+            PR.Partial n pst1 -> do+                assert (n <= length (x:getList buf)) (return ())+                let src0 = Prelude.take n (x:getList buf)+                    src  = Prelude.reverse src0 ++ xs+                goExtract SPEC (List []) (List src) pst1+            PR.Continue 0 pst1 ->+                goExtract SPEC (List (x:getList buf)) (List xs) pst1+            PR.Continue 1 pst1 ->+                goExtract SPEC buf (List (x:xs)) pst1+            PR.Continue n pst1 -> do+                assert (n <= length (x:getList buf)) (return ())+                let (src0, buf1) = splitAt n (x:getList buf)+                    src  = Prelude.reverse src0 ++ xs+                goExtract SPEC (List buf1) (List src) pst1+            PR.Done n b -> do+                assert (n <= length (x:getList buf)) (return ())+                let src0 = Prelude.take n (x:getList buf)+                    src  = Prelude.reverse src0+                return (Right b, fromList src)+            PR.Error err -> do+                let src = (Prelude.reverse $ getList buf) ++ x:xs+                return (Left (ParseError err), fromList src)++    -- This is simplified goExtract+    -- XXX Use SPEC?+    {-# INLINE goStop #-}+    goStop _ buf pst = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"+            PR.Continue 0 pst1 -> goStop SPEC buf pst1+            PR.Continue n pst1 -> do+                assert (n <= length (getList buf)) (return ())+                let (src0, buf1) = splitAt n (getList buf)+                    src = Prelude.reverse src0+                goExtract SPEC (List buf1) (List src) pst1+            PR.Done 0 b -> return (Right b, StreamD.nil)+            PR.Done n b -> do+                assert (n <= length (getList buf)) (return ())+                let src0 = Prelude.take n (getList buf)+                    src  = Prelude.reverse src0+                return (Right b, fromList src)+            PR.Error err -> do+                let src  = Prelude.reverse $ getList buf+                return (Left (ParseError err), fromList src)+ -- | Parse a stream using the supplied 'Parser'. ----- /CPS/------{-# INLINE parseBreak #-}---parseBreak :: Monad m => Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)---parseBreak p strm = D.parseBreak p strm+{-# INLINE parseBreak #-}+parseBreak :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)+parseBreak = parseBreakD  ------------------------------------------------------------------------------+-- Specialized Folds+------------------------------------------------------------------------------++-- benchmark after dropping 1 item from stream or using unfolds+{-# INLINE_NORMAL null #-}+null :: Monad m => Stream m a -> m Bool+#ifdef USE_FOLDS_EVERYWHERE+null = fold Fold.null+#else+null = foldrM (\_ _ -> return False) (return True)+#endif++{-# INLINE_NORMAL head #-}+head :: Monad m => Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+head = fold Fold.one+#else+head = foldrM (\x _ -> return (Just x)) (return Nothing)+#endif++{-# INLINE_NORMAL headElse #-}+headElse :: Monad m => a -> Stream m a -> m a+headElse a = foldrM (\x _ -> return x) (return a)++-- Does not fuse, has the same performance as the StreamK version.+{-# INLINE_NORMAL tail #-}+tail :: Monad m => Stream m a -> m (Maybe (Stream m a))+tail (UnStream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield _ s -> return (Just $ Stream step s)+            Skip  s   -> go SPEC s+            Stop      -> return Nothing++-- XXX will it fuse? need custom impl?+{-# INLINE_NORMAL last #-}+last :: Monad m => Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+last = fold Fold.last+#else+last = foldl' (\_ y -> Just y) Nothing+#endif++-- XXX Use the foldrM based impl instead+{-# INLINE_NORMAL elem #-}+elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool+#ifdef USE_FOLDS_EVERYWHERE+elem e = fold (Fold.elem e)+#else+-- elem e m = foldrM (\x xs -> if x == e then return True else xs) (return False) m+elem e (Stream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield x s+              | x == e -> return True+              | otherwise -> go SPEC s+            Skip s -> go SPEC s+            Stop   -> return False+#endif++{-# INLINE_NORMAL notElem #-}+notElem :: (Monad m, Eq a) => a -> Stream m a -> m Bool+notElem e s = fmap not (elem e s)++{-# INLINE_NORMAL all #-}+all :: Monad m => (a -> Bool) -> Stream m a -> m Bool+#ifdef USE_FOLDS_EVERYWHERE+all p = fold (Fold.all p)+#else+-- all p m = foldrM (\x xs -> if p x then xs else return False) (return True) m+all p (Stream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield x s+              | p x -> go SPEC s+              | otherwise -> return False+            Skip s -> go SPEC s+            Stop   -> return True+#endif++{-# INLINE_NORMAL any #-}+any :: Monad m => (a -> Bool) -> Stream m a -> m Bool+#ifdef USE_FOLDS_EVERYWHERE+any p = fold (Fold.any p)+#else+-- any p m = foldrM (\x xs -> if p x then return True else xs) (return False) m+any p (Stream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield x s+              | p x -> return True+              | otherwise -> go SPEC s+            Skip s -> go SPEC s+            Stop   -> return False+#endif++{-# INLINE_NORMAL maximum #-}+maximum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+maximum = fold Fold.maximum+#else+maximum (Stream step state) = go SPEC Nothing' state+  where+    go !_ Nothing' st = do+        r <- step defState st+        case r of+            Yield x s -> go SPEC (Just' x) s+            Skip  s   -> go SPEC Nothing' s+            Stop      -> return Nothing+    go !_ (Just' acc) st = do+        r <- step defState st+        case r of+            Yield x s+              | acc <= x  -> go SPEC (Just' x) s+              | otherwise -> go SPEC (Just' acc) s+            Skip s -> go SPEC (Just' acc) s+            Stop   -> return (Just acc)+#endif++{-# INLINE_NORMAL maximumBy #-}+maximumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+maximumBy cmp = fold (Fold.maximumBy cmp)+#else+maximumBy cmp (Stream step state) = go SPEC Nothing' state+  where+    go !_ Nothing' st = do+        r <- step defState st+        case r of+            Yield x s -> go SPEC (Just' x) s+            Skip  s   -> go SPEC Nothing' s+            Stop      -> return Nothing+    go !_ (Just' acc) st = do+        r <- step defState st+        case r of+            Yield x s -> case cmp acc x of+                GT -> go SPEC (Just' acc) s+                _  -> go SPEC (Just' x) s+            Skip s -> go SPEC (Just' acc) s+            Stop   -> return (Just acc)+#endif++{-# INLINE_NORMAL minimum #-}+minimum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+minimum = fold Fold.minimum+#else+minimum (Stream step state) = go SPEC Nothing' state++    where++    go !_ Nothing' st = do+        r <- step defState st+        case r of+            Yield x s -> go SPEC (Just' x) s+            Skip  s   -> go SPEC Nothing' s+            Stop      -> return Nothing+    go !_ (Just' acc) st = do+        r <- step defState st+        case r of+            Yield x s+              | acc <= x  -> go SPEC (Just' acc) s+              | otherwise -> go SPEC (Just' x) s+            Skip s -> go SPEC (Just' acc) s+            Stop   -> return (Just acc)+#endif++{-# INLINE_NORMAL minimumBy #-}+minimumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+minimumBy cmp = fold (Fold.minimumBy cmp)+#else+minimumBy cmp (Stream step state) = go SPEC Nothing' state++    where++    go !_ Nothing' st = do+        r <- step defState st+        case r of+            Yield x s -> go SPEC (Just' x) s+            Skip  s   -> go SPEC Nothing' s+            Stop      -> return Nothing+    go !_ (Just' acc) st = do+        r <- step defState st+        case r of+            Yield x s -> case cmp acc x of+                GT -> go SPEC (Just' x) s+                _  -> go SPEC (Just' acc) s+            Skip s -> go SPEC (Just' acc) s+            Stop   -> return (Just acc)+#endif++{-# INLINE_NORMAL (!!) #-}+(!!) :: (Monad m) => Stream m a -> Int -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+stream !! i = fold (Fold.index i) stream+#else+(Stream step state) !! i = go SPEC i state++    where++    go !_ !n st = do+        r <- step defState st+        case r of+            Yield x s | n < 0 -> return Nothing+                      | n == 0 -> return $ Just x+                      | otherwise -> go SPEC (n - 1) s+            Skip s -> go SPEC n s+            Stop   -> return Nothing+#endif++{-# INLINE_NORMAL lookup #-}+lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)+#ifdef USE_FOLDS_EVERYWHERE+lookup e = fold (Fold.lookup e)+#else+lookup e = foldrM (\(a, b) xs -> if e == a then return (Just b) else xs)+                   (return Nothing)+#endif++{-# INLINE_NORMAL findM #-}+findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+findM p = fold (Fold.findM p)+#else+findM p = foldrM (\x xs -> p x >>= \r -> if r then return (Just x) else xs)+                   (return Nothing)+#endif++{-# INLINE find #-}+find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)+find p = findM (return . p)++{-# INLINE toListRev #-}+toListRev :: Monad m => Stream m a -> m [a]+#ifdef USE_FOLDS_EVERYWHERE+toListRev = fold Fold.toListRev+#else+toListRev = foldl' (flip (:)) []+#endif++------------------------------------------------------------------------------+-- Transformation comprehensions+------------------------------------------------------------------------------++{-# INLINE_NORMAL the #-}+the :: (Eq a, Monad m) => Stream m a -> m (Maybe a)+#ifdef USE_FOLDS_EVERYWHERE+the = fold Fold.the+#else+the (Stream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield x s -> go' SPEC x s+            Skip s    -> go SPEC s+            Stop      -> return Nothing+    go' !_ n st = do+        r <- step defState st+        case r of+            Yield x s | x == n -> go' SPEC n s+                      | otherwise -> return Nothing+            Skip s -> go' SPEC n s+            Stop   -> return (Just n)+#endif++------------------------------------------------------------------------------+-- Map and Fold+------------------------------------------------------------------------------++-- | Execute a monadic action for each element of the 'Stream'+{-# INLINE_NORMAL mapM_ #-}+mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()+#ifdef USE_FOLDS_EVERYWHERE+mapM_ f = fold (Fold.drainBy f)+#else+mapM_ m = drain . mapM m+#endif++------------------------------------------------------------------------------ -- Multi-stream folds ------------------------------------------------------------------------------ @@ -270,10 +665,91 @@ -- >>> Stream.isPrefixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char) -- True ---{-# INLINE isPrefixOf #-}+{-# INLINE_NORMAL isPrefixOf #-} isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isPrefixOf m1 m2 = D.isPrefixOf (toStreamD m1) (toStreamD m2)+isPrefixOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb +    where++    go !_ Nothing' sa sb = do+        r <- stepa defState sa+        case r of+            Yield x sa' -> go SPEC (Just' x) sa' sb+            Skip sa'    -> go SPEC Nothing' sa' sb+            Stop        -> return True++    go !_ (Just' x) sa sb = do+        r <- stepb defState sb+        case r of+            Yield y sb' ->+                if x == y+                    then go SPEC Nothing' sa sb'+                    else return False+            Skip sb' -> go SPEC (Just' x) sa sb'+            Stop     -> return False++-- | Returns 'True' if all the elements of the first stream occur, in order, in+-- the second stream. The elements do not have to occur consecutively. A stream+-- is a subsequence of itself.+--+-- >>> Stream.isSubsequenceOf (Stream.fromList "hlo") (Stream.fromList "hello" :: Stream IO Char)+-- True+--+{-# INLINE_NORMAL isSubsequenceOf #-}+isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool+isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb++    where++    go !_ Nothing' sa sb = do+        r <- stepa defState sa+        case r of+            Yield x sa' -> go SPEC (Just' x) sa' sb+            Skip sa' -> go SPEC Nothing' sa' sb+            Stop -> return True++    go !_ (Just' x) sa sb = do+        r <- stepb defState sb+        case r of+            Yield y sb' ->+                if x == y+                    then go SPEC Nothing' sa sb'+                    else go SPEC (Just' x) sa sb'+            Skip sb' -> go SPEC (Just' x) sa sb'+            Stop -> return False++-- | @stripPrefix prefix input@ strips the @prefix@ stream from the @input@+-- stream if it is a prefix of input. Returns 'Nothing' if the input does not+-- start with the given prefix, stripped input otherwise. Returns @Just nil@+-- when the prefix is the same as the input stream.+--+-- Space: @O(1)@+--+{-# INLINE_NORMAL stripPrefix #-}+stripPrefix+    :: (Monad m, Eq a)+    => Stream m a -> Stream m a -> m (Maybe (Stream m a))+stripPrefix (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb++    where++    go !_ Nothing' sa sb = do+        r <- stepa defState sa+        case r of+            Yield x sa' -> go SPEC (Just' x) sa' sb+            Skip sa'    -> go SPEC Nothing' sa' sb+            Stop        -> return $ Just (Stream stepb sb)++    go !_ (Just' x) sa sb = do+        r <- stepb defState sb+        case r of+            Yield y sb' ->+                if x == y+                    then go SPEC Nothing' sa sb'+                    else return Nothing+            Skip sb' -> go SPEC (Just' x) sa sb'+            Stop     -> return Nothing+ -- | Returns 'True' if the first stream is an infix of the second. A stream is -- considered an infix of itself. --@@ -293,7 +769,7 @@ isInfixOf infx stream = do     arr <- fold Array.write infx     -- XXX can use breakOnSeq instead (when available)-    r <- D.null $ D.drop 1 $ D.splitOnSeq arr Fold.drain $ toStreamD stream+    r <- null $ StreamD.drop 1 $ Nesting.splitOnSeq arr Fold.drain stream     return (not r)  -- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf@@ -326,36 +802,15 @@ -- {-# INLINE isSuffixOf #-} isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isSuffixOf suffix stream = reverse suffix `isPrefixOf` reverse stream---- | Returns 'True' if all the elements of the first stream occur, in order, in--- the second stream. The elements do not have to occur consecutively. A stream--- is a subsequence of itself.------ >>> Stream.isSubsequenceOf (Stream.fromList "hlo") (Stream.fromList "hello" :: Stream IO Char)--- True----{-# INLINE isSubsequenceOf #-}-isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isSubsequenceOf m1 m2 = D.isSubsequenceOf (toStreamD m1) (toStreamD m2)---- Note: If we want to return a Maybe value to know whether the--- suffix/infix was present or not along with the stripped stream then--- we need to buffer the whole input stream.+isSuffixOf suffix stream =+    StreamD.reverse suffix `isPrefixOf` StreamD.reverse stream --- | @stripPrefix prefix input@ strips the @prefix@ stream from the @input@--- stream if it is a prefix of input. Returns 'Nothing' if the input does not--- start with the given prefix, stripped input otherwise. Returns @Just nil@--- when the prefix is the same as the input stream.------ Space: @O(1)@----{-# INLINE stripPrefix #-}-stripPrefix-    :: (Monad m, Eq a)-    => Stream m a -> Stream m a -> m (Maybe (Stream m a))-stripPrefix m1 m2 = fmap fromStreamD <$>-    D.stripPrefix (toStreamD m1) (toStreamD m2)+-- | Much faster than 'isSuffixOf'.+{-# INLINE isSuffixOfUnbox #-}+isSuffixOfUnbox :: (MonadIO m, Eq a, Unbox a) =>+    Stream m a -> Stream m a -> m Bool+isSuffixOfUnbox suffix stream =+    StreamD.reverseUnbox suffix `isPrefixOf` StreamD.reverseUnbox stream  -- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does -- not end with the given suffix. Returns @Just nil@ when the suffix is the@@ -365,8 +820,6 @@ -- stripSuffix on that especially if the elements have a Storable or Prim -- instance. ----- See also "Streamly.Internal.Data.Stream.Reduce.dropSuffix".--- -- Space: @O(n)@, buffers the entire input stream as well as the suffix -- -- /Pre-release/@@ -374,4 +827,15 @@ stripSuffix     :: (Monad m, Eq a)     => Stream m a -> Stream m a -> m (Maybe (Stream m a))-stripSuffix m1 m2 = fmap reverse <$> stripPrefix (reverse m1) (reverse m2)+stripSuffix m1 m2 =+    fmap StreamD.reverse+        <$> stripPrefix (StreamD.reverse m1) (StreamD.reverse m2)++-- | Much faster than 'stripSuffix'.+{-# INLINE stripSuffixUnbox #-}+stripSuffixUnbox+    :: (MonadIO m, Eq a, Unbox a)+    => Stream m a -> Stream m a -> m (Maybe (Stream m a))+stripSuffixUnbox m1 m2 =+    fmap StreamD.reverseUnbox+        <$> stripPrefix (StreamD.reverseUnbox m1) (StreamD.reverseUnbox m2)
− src/Streamly/Internal/Data/Stream/Enumerate.hs
@@ -1,560 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.Enumerate--- Copyright   : (c) 2018 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ The functions defined in this module should be rarely needed for direct use,--- try to use the operations from the 'Enumerable' type class--- instances instead.------ This module provides an 'Enumerable' type class to enumerate 'Enum' types--- into a stream. The operations in this type class correspond to similar--- perations in the 'Enum' type class, the only difference is that they produce--- a stream instead of a list. These operations cannot be defined generically--- based on the 'Enum' type class. We provide instances for commonly used--- types. If instances for other types are needed convenience functions defined--- in this module can be used to define them. Alternatively, these functions--- can be used directly.---- XXX The Unfold.Enumeration module is more modular, check the differences and--- reconcile the two.--module Streamly.Internal.Data.Stream.Enumerate-    (-      Enumerable (..)--    -- ** Enumerating 'Bounded' 'Enum' Types-    , enumerate-    , enumerateTo-    , enumerateFromBounded--    -- ** Enumerating 'Enum' Types not larger than 'Int'-    , enumerateFromToSmall-    , enumerateFromThenToSmall-    , enumerateFromThenSmallBounded--    -- ** Enumerating 'Bounded' 'Integral' Types-    , enumerateFromIntegral-    , enumerateFromThenIntegral--    -- ** Enumerating 'Integral' Types-    , enumerateFromToIntegral-    , enumerateFromThenToIntegral--    -- ** Enumerating unbounded 'Integral' Types-    , enumerateFromStepIntegral--    -- ** Enumerating 'Fractional' Types-    , enumerateFromFractional-    , enumerateFromToFractional-    , enumerateFromThenFractional-    , enumerateFromThenToFractional-    )-where--import Data.Fixed-import Data.Int-import Data.Ratio-import Data.Word-import Numeric.Natural-import Data.Functor.Identity (Identity(..))--import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD)--import qualified Streamly.Internal.Data.Stream.StreamD.Generate as D---- $setup--- >>> import Streamly.Data.Fold as Fold--- >>> import Streamly.Internal.Data.Stream as Stream--- >>> import Streamly.Internal.Data.Stream.Enumerate as Stream------------------------------------------------------------------------------------ Enumeration of Integral types-------------------------------------------------------------------------------------- | @enumerateFromStepIntegral from step@ generates an infinite stream whose--- first element is @from@ and the successive elements are in increments of--- @step@.------ CAUTION: This function is not safe for finite integral types. It does not--- check for overflow, underflow or bounds.------ @--- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)--- [0,-2,-4]------ @----{-# INLINE enumerateFromStepIntegral #-}-enumerateFromStepIntegral-    :: (Monad m, Integral a)-    => a -> a -> Stream m a-enumerateFromStepIntegral from stride =-    fromStreamD $ D.enumerateFromStepIntegral from stride---- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a--- stream whose first element is @from@ and the successive elements are in--- increments of @1@. The stream is bounded by the size of the 'Integral' type.------ @--- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)--- [0,1,2,3]------ @----{-# INLINE enumerateFromIntegral #-}-enumerateFromIntegral-    :: (Monad m, Integral a, Bounded a)-    => a -> Stream m a-enumerateFromIntegral from = fromStreamD $ D.enumerateFromIntegral from---- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from--- then@ generates a stream whose first element is @from@, the second element--- is @then@ and the successive elements are in increments of @then - from@.--- The stream is bounded by the size of the 'Integral' type.------ @--- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)--- [0,-2,-4,-6]------ @----{-# INLINE enumerateFromThenIntegral #-}-enumerateFromThenIntegral-    :: (Monad m, Integral a, Bounded a)-    => a -> a -> Stream m a-enumerateFromThenIntegral from next =-    fromStreamD $ D.enumerateFromThenIntegral from next---- | Enumerate an 'Integral' type up to a given limit.--- @enumerateFromToIntegral from to@ generates a finite stream whose first--- element is @from@ and successive elements are in increments of @1@ up to--- @to@.------ @--- >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4--- [0,1,2,3,4]------ @----{-# INLINE enumerateFromToIntegral #-}-enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a-enumerateFromToIntegral from to =-    fromStreamD $ D.enumerateFromToIntegral from to---- | Enumerate an 'Integral' type in steps up to a given limit.--- @enumerateFromThenToIntegral from then to@ generates a finite stream whose--- first element is @from@, the second element is @then@ and the successive--- elements are in increments of @then - from@ up to @to@.------ @--- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)--- [0,-2,-4,-6]------ @----{-# INLINE enumerateFromThenToIntegral #-}-enumerateFromThenToIntegral-    :: (Monad m, Integral a)-    => a -> a -> a -> Stream m a-enumerateFromThenToIntegral from next to =-    fromStreamD $ D.enumerateFromThenToIntegral from next to------------------------------------------------------------------------------------ Enumeration of Fractional types-------------------------------------------------------------------------------------- Even though the underlying implementation of enumerateFromFractional and--- enumerateFromThenFractional works for any 'Num' we have restricted these to--- 'Fractional' because these do not perform any bounds check, in contrast to--- integral versions and are therefore not equivalent substitutes for those.------ | Numerically stable enumeration from a 'Fractional' number in steps of size--- @1@. @enumerateFromFractional from@ generates a stream whose first element--- is @from@ and the successive elements are in increments of @1@.  No overflow--- or underflow checks are performed.------ This is the equivalent to 'enumFrom' for 'Fractional' types. For example:------ @--- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1--- [1.1,2.1,3.1,4.1]------ @-------{-# INLINE enumerateFromFractional #-}-enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a-enumerateFromFractional from = fromStreamD $ D.enumerateFromNum from---- | Numerically stable enumeration from a 'Fractional' number in steps.--- @enumerateFromThenFractional from then@ generates a stream whose first--- element is @from@, the second element is @then@ and the successive elements--- are in increments of @then - from@.  No overflow or underflow checks are--- performed.------ This is the equivalent of 'enumFromThen' for 'Fractional' types. For--- example:------ @--- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1--- [1.1,2.1,3.1,4.1]------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)--- [1.1,-2.1,-5.300000000000001,-8.500000000000002]------ @----{-# INLINE enumerateFromThenFractional #-}-enumerateFromThenFractional-    :: (Monad m, Fractional a)-    => a -> a -> Stream m a-enumerateFromThenFractional from next = fromStreamD $ D.enumerateFromThenNum from next---- | Numerically stable enumeration from a 'Fractional' number to a given--- limit.  @enumerateFromToFractional from to@ generates a finite stream whose--- first element is @from@ and successive elements are in increments of @1@ up--- to @to@.------ This is the equivalent of 'enumFromTo' for 'Fractional' types. For--- example:------ @--- >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4--- [1.1,2.1,3.1,4.1]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6--- [1.1,2.1,3.1,4.1,5.1]------ @------ Notice that the last element is equal to the specified @to@ value after--- rounding to the nearest integer.----{-# INLINE enumerateFromToFractional #-}-enumerateFromToFractional-    :: (Monad m, Fractional a, Ord a)-    => a -> a -> Stream m a-enumerateFromToFractional from to =-    fromStreamD $ D.enumerateFromToFractional from to---- | Numerically stable enumeration from a 'Fractional' number in steps up to a--- given limit.  @enumerateFromThenToFractional from then to@ generates a--- finite stream whose first element is @from@, the second element is @then@--- and the successive elements are in increments of @then - from@ up to @to@.------ This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For--- example:------ @--- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6--- [0.1,2.0,3.9,5.799999999999999]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)--- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]------ @-------{-# INLINE enumerateFromThenToFractional #-}-enumerateFromThenToFractional-    :: (Monad m, Fractional a, Ord a)-    => a -> a -> a -> Stream m a-enumerateFromThenToFractional from next to =-    fromStreamD $ D.enumerateFromThenToFractional from next to------------------------------------------------------------------------------------ Enumeration of Enum types not larger than Int-------------------------------------------------------------------------------------- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.----{-# INLINE enumerateFromToSmall #-}-enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a-enumerateFromToSmall from to =-      fmap toEnum-    $ enumerateFromToIntegral (fromEnum from) (fromEnum to)---- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.----{-# INLINE enumerateFromThenToSmall #-}-enumerateFromThenToSmall :: (Monad m, Enum a)-    => a -> a -> a -> Stream m a-enumerateFromThenToSmall from next to =-          fmap toEnum-        $ enumerateFromThenToIntegral-            (fromEnum from) (fromEnum next) (fromEnum to)---- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.------ Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a--- type is bounded but does not have a 'Bounded' instance then we can go on--- enumerating it beyond the legal values of the type, resulting in the failure--- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'--- instance for this function to be safely used.----{-# INLINE enumerateFromThenSmallBounded #-}-enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)-    => a -> a -> Stream m a-enumerateFromThenSmallBounded from next =-    if fromEnum next >= fromEnum from-    then enumerateFromThenTo from next maxBound-    else enumerateFromThenTo from next minBound------------------------------------------------------------------------------------ Enumerable type class-------------------------------------------------------------------------------------- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream--- enumerations like this:------ {-# RULES "fromList enumFrom" [1]---     forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}------ But this does not work because enumFrom is a class method and GHC rewrites--- it quickly, so we do not get a chance to have our rule fired.---- | Types that can be enumerated as a stream. The operations in this type--- class are equivalent to those in the 'Enum' type class, except that these--- generate a stream instead of a list. Use the functions in--- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.----class Enum a => Enumerable a where-    -- | @enumerateFrom from@ generates a stream starting with the element-    -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or-    -- generating an infinite stream when the type is not 'Bounded'.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)-    -- [0,1,2,3]-    ---    -- @-    ---    -- For 'Fractional' types, enumeration is numerically stable. However, no-    -- overflow or underflow checks are performed.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1-    -- [1.1,2.1,3.1,4.1]-    ---    -- @-    ---    enumerateFrom :: (Monad m) => a -> Stream m a--    -- | Generate a finite stream starting with the element @from@, enumerating-    -- the type up to the value @to@. If @to@ is smaller than @from@ then an-    -- empty stream is returned.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4-    -- [0,1,2,3,4]-    ---    -- @-    ---    -- For 'Fractional' types, the last element is equal to the specified @to@-    -- value after rounding to the nearest integral value.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4-    -- [1.1,2.1,3.1,4.1]-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6-    -- [1.1,2.1,3.1,4.1,5.1]-    ---    -- @-    ---    enumerateFromTo :: (Monad m) => a -> a -> Stream m a--    -- | @enumerateFromThen from then@ generates a stream whose first element-    -- is @from@, the second element is @then@ and the successive elements are-    -- in increments of @then - from@.  Enumeration can occur downwards or-    -- upwards depending on whether @then@ comes before or after @from@. For-    -- 'Bounded' types the stream ends when 'maxBound' is reached, for-    -- unbounded types it keeps enumerating infinitely.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2-    -- [0,2,4,6]-    ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)-    -- [0,-2,-4,-6]-    ---    -- @-    ---    enumerateFromThen :: (Monad m) => a -> a -> Stream m a--    -- | @enumerateFromThenTo from then to@ generates a finite stream whose-    -- first element is @from@, the second element is @then@ and the successive-    -- elements are in increments of @then - from@ up to @to@. Enumeration can-    -- occur downwards or upwards depending on whether @then@ comes before or-    -- after @from@.-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6-    -- [0,2,4,6]-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)-    -- [0,-2,-4,-6]-    ---    -- @-    ---    enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a---- MAYBE: Sometimes it is more convenient to know the count rather then the--- ending or starting element. For those cases we can define the folllowing--- APIs. All of these will work only for bounded types if we represent the--- count by Int.------ enumerateN--- enumerateFromN--- enumerateToN--- enumerateFromStep--- enumerateFromStepN------------------------------------------------------------------------------------ Convenient functions for bounded types-------------------------------------------------------------------------------------- |--- > enumerate = enumerateFrom minBound------ Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'----{-# INLINE enumerate #-}-enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a-enumerate = enumerateFrom minBound---- |--- > enumerateTo = enumerateFromTo minBound------ Enumerate a 'Bounded' type from its 'minBound' to specified value.----{-# INLINE enumerateTo #-}-enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a-enumerateTo = enumerateFromTo minBound---- |--- > enumerateFromBounded = enumerateFromTo from maxBound------ 'enumerateFrom' for 'Bounded' 'Enum' types.----{-# INLINE enumerateFromBounded #-}-enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)-    => a -> Stream m a-enumerateFromBounded from = enumerateFromTo from maxBound------------------------------------------------------------------------------------ Enumerable Instances-------------------------------------------------------------------------------------- For Enum types smaller than or equal to Int size.-#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE)           \-instance Enumerable SMALL_TYPE where {                 \-    {-# INLINE enumerateFrom #-};                      \-    enumerateFrom = enumerateFromBounded;              \-    {-# INLINE enumerateFromThen #-};                  \-    enumerateFromThen = enumerateFromThenSmallBounded; \-    {-# INLINE enumerateFromTo #-};                    \-    enumerateFromTo = enumerateFromToSmall;            \-    {-# INLINE enumerateFromThenTo #-};                \-    enumerateFromThenTo = enumerateFromThenToSmall }---ENUMERABLE_BOUNDED_SMALL(())-ENUMERABLE_BOUNDED_SMALL(Bool)-ENUMERABLE_BOUNDED_SMALL(Ordering)-ENUMERABLE_BOUNDED_SMALL(Char)---- For bounded Integral Enum types, may be larger than Int.-#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE)  \-instance Enumerable INTEGRAL_TYPE where {           \-    {-# INLINE enumerateFrom #-};                   \-    enumerateFrom = enumerateFromIntegral;          \-    {-# INLINE enumerateFromThen #-};               \-    enumerateFromThen = enumerateFromThenIntegral;  \-    {-# INLINE enumerateFromTo #-};                 \-    enumerateFromTo = enumerateFromToIntegral;      \-    {-# INLINE enumerateFromThenTo #-};             \-    enumerateFromThenTo = enumerateFromThenToIntegral }--ENUMERABLE_BOUNDED_INTEGRAL(Int)-ENUMERABLE_BOUNDED_INTEGRAL(Int8)-ENUMERABLE_BOUNDED_INTEGRAL(Int16)-ENUMERABLE_BOUNDED_INTEGRAL(Int32)-ENUMERABLE_BOUNDED_INTEGRAL(Int64)-ENUMERABLE_BOUNDED_INTEGRAL(Word)-ENUMERABLE_BOUNDED_INTEGRAL(Word8)-ENUMERABLE_BOUNDED_INTEGRAL(Word16)-ENUMERABLE_BOUNDED_INTEGRAL(Word32)-ENUMERABLE_BOUNDED_INTEGRAL(Word64)---- For unbounded Integral Enum types.-#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE)              \-instance Enumerable INTEGRAL_TYPE where {                         \-    {-# INLINE enumerateFrom #-};                                 \-    enumerateFrom from = enumerateFromStepIntegral from 1;        \-    {-# INLINE enumerateFromThen #-};                             \-    enumerateFromThen from next =                                 \-        enumerateFromStepIntegral from (next - from);             \-    {-# INLINE enumerateFromTo #-};                               \-    enumerateFromTo = enumerateFromToIntegral;                    \-    {-# INLINE enumerateFromThenTo #-};                           \-    enumerateFromThenTo = enumerateFromThenToIntegral }--ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)-ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)--#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT)         \-instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where {     \-    {-# INLINE enumerateFrom #-};                                 \-    enumerateFrom = enumerateFromFractional;                      \-    {-# INLINE enumerateFromThen #-};                             \-    enumerateFromThen = enumerateFromThenFractional;              \-    {-# INLINE enumerateFromTo #-};                               \-    enumerateFromTo = enumerateFromToFractional;                  \-    {-# INLINE enumerateFromThenTo #-};                           \-    enumerateFromThenTo = enumerateFromThenToFractional }--ENUMERABLE_FRACTIONAL(Float,)-ENUMERABLE_FRACTIONAL(Double,)-ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)-ENUMERABLE_FRACTIONAL((Ratio a),Integral a)--instance Enumerable a => Enumerable (Identity a) where-    {-# INLINE enumerateFrom #-}-    enumerateFrom (Identity from) =-        fmap Identity $ enumerateFrom from-    {-# INLINE enumerateFromThen #-}-    enumerateFromThen (Identity from) (Identity next) =-        fmap Identity $ enumerateFromThen from next-    {-# INLINE enumerateFromTo #-}-    enumerateFromTo (Identity from) (Identity to) =-        fmap Identity $ enumerateFromTo from to-    {-# INLINE enumerateFromThenTo #-}-    enumerateFromThenTo (Identity from) (Identity next) (Identity to) =-          fmap Identity-        $ enumerateFromThenTo from next to---- TODO-{--instance Enumerable a => Enumerable (Last a)-instance Enumerable a => Enumerable (First a)-instance Enumerable a => Enumerable (Max a)-instance Enumerable a => Enumerable (Min a)-instance Enumerable a => Enumerable (Const a b)-instance Enumerable (f a) => Enumerable (Alt f a)-instance Enumerable (f a) => Enumerable (Ap f a)--}
src/Streamly/Internal/Data/Stream/Exception.hs view
@@ -1,6 +1,7 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Data.Stream.Exception--- Copyright   : (c) 2019 Composewell Technologies+-- Copyright   : (c) 2020 Composewell Technologies and Contributors -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental@@ -8,12 +9,14 @@  module Streamly.Internal.Data.Stream.Exception     (-      before+      gbracket_+    , gbracket+    , before     , afterUnsafe     , afterIO     , bracketUnsafe-    , bracketIO     , bracketIO3+    , bracketIO     , onException     , finallyUnsafe     , finallyIO@@ -22,21 +25,154 @@     ) where -import Control.Exception (Exception)+#include "inline.hs"++import Control.Monad.IO.Class (MonadIO(..))+import Control.Exception (Exception, SomeException, mask_) import Control.Monad.Catch (MonadCatch)-import Control.Monad.IO.Class (MonadIO)-import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)+import GHC.Exts (inline)+import Streamly.Internal.Data.IOFinalizer+    (newIOFinalizer, runIOFinalizer, clearingIOFinalizer) -import qualified Streamly.Internal.Data.Stream.StreamD as D+import qualified Control.Monad.Catch as MC --- $setup--- >>> :m--- >>> import qualified Streamly.Internal.Data.Stream as Stream+import Streamly.Internal.Data.Stream.Type ---------------------------------------------------------------------------------- Exceptions-------------------------------------------------------------------------------+#include "DocTestDataStream.hs" +data GbracketState s1 s2 v+    = GBracketInit+    | GBracketNormal s1 v+    | GBracketException s2++-- | Like 'gbracket' but with following differences:+--+-- * alloc action @m c@ runs with async exceptions enabled+-- * cleanup action @c -> m d@ won't run if the stream is garbage collected+--   after partial evaluation.+--+-- /Inhibits stream fusion/+--+-- /Pre-release/+--+{-# INLINE_NORMAL gbracket_ #-}+gbracket_+    :: Monad m+    => m c                                  -- ^ before+    -> (c -> m d)                           -- ^ after, on normal stop+    -> (c -> e -> Stream m b -> m (Stream m b)) -- ^ on exception+    -> (forall s. m s -> m (Either e s))    -- ^ try (exception handling)+    -> (c -> Stream m b)                    -- ^ stream generator+    -> Stream m b+gbracket_ bef aft onExc ftry action =+    Stream step GBracketInit++    where++    {-# INLINE_LATE step #-}+    step _ GBracketInit = do+        r <- bef+        return $ Skip $ GBracketNormal (action r) r++    step gst (GBracketNormal (UnStream step1 st) v) = do+        res <- ftry $ step1 gst st+        case res of+            Right r -> case r of+                Yield x s ->+                    return $ Yield x (GBracketNormal (Stream step1 s) v)+                Skip s -> return $ Skip (GBracketNormal (Stream step1 s) v)+                Stop -> aft v >> return Stop+            -- XXX Do not handle async exceptions, just rethrow them.+            Left e -> do+                strm <- onExc v e (UnStream step1 st)+                return $ Skip (GBracketException strm)+    step gst (GBracketException (UnStream step1 st)) = do+        res <- step1 gst st+        case res of+            Yield x s -> return $ Yield x (GBracketException (Stream step1 s))+            Skip s    -> return $ Skip (GBracketException (Stream step1 s))+            Stop      -> return Stop++data GbracketIOState s1 s2 v wref+    = GBracketIOInit+    | GBracketIONormal s1 v wref+    | GBracketIOException s2++-- | Run the alloc action @m c@ with async exceptions disabled but keeping+-- blocking operations interruptible (see 'Control.Exception.mask').  Use the+-- output @c@ as input to @c -> Stream m b@ to generate an output stream. When+-- generating the stream use the supplied @try@ operation @forall s. m s -> m+-- (Either e s)@ to catch synchronous exceptions. If an exception occurs run+-- the exception handler @c -> e -> Stream m b -> m (Stream m b)@. Note that+-- 'gbracket' does not rethrow the exception, it has to be done by the+-- exception handler if desired.+--+-- The cleanup action @c -> m d@, runs whenever the stream ends normally, due+-- to a sync or async exception or if it gets garbage collected after a partial+-- lazy evaluation.  See 'bracket' for the semantics of the cleanup action.+--+-- 'gbracket' can express all other exception handling combinators.+--+-- /Inhibits stream fusion/+--+-- /Pre-release/+{-# INLINE_NORMAL gbracket #-}+gbracket+    :: MonadIO m+    => IO c -- ^ before+    -> (c -> IO d1) -- ^ on normal stop+    -> (c -> e -> Stream m b -> IO (Stream m b)) -- ^ on exception+    -> (c -> IO d2) -- ^ on GC without normal stop or exception+    -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)+    -> (c -> Stream m b) -- ^ stream generator+    -> Stream m b+gbracket bef aft onExc onGC ftry action =+    Stream step GBracketIOInit++    where++    -- If the stream is never evaluated the "aft" action will never be+    -- called. For that to occur we will need the user of this API to pass a+    -- weak pointer to us.+    {-# INLINE_LATE step #-}+    step _ GBracketIOInit = do+        -- We mask asynchronous exceptions to make the execution+        -- of 'bef' and the registration of 'aft' atomic.+        -- A similar thing is done in the resourcet package: https://git.io/JvKV3+        -- Tutorial: https://markkarpov.com/tutorial/exceptions.html+        (r, ref) <- liftIO $ mask_ $ do+            r <- bef+            ref <- newIOFinalizer (onGC r)+            return (r, ref)+        return $ Skip $ GBracketIONormal (action r) r ref++    step gst (GBracketIONormal (UnStream step1 st) v ref) = do+        res <- ftry $ step1 gst st+        case res of+            Right r -> case r of+                Yield x s ->+                    return $ Yield x (GBracketIONormal (Stream step1 s) v ref)+                Skip s ->+                    return $ Skip (GBracketIONormal (Stream step1 s) v ref)+                Stop ->+                    liftIO (clearingIOFinalizer ref (aft v)) >> return Stop+            -- XXX Do not handle async exceptions, just rethrow them.+            Left e -> do+                -- Clearing of finalizer and running of exception handler must+                -- 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.+                stream <-+                    liftIO (clearingIOFinalizer ref (onExc v e (UnStream step1 st)))+                return $ Skip (GBracketIOException stream)+    step gst (GBracketIOException (UnStream step1 st)) = do+        res <- step1 gst st+        case res of+            Yield x s ->+                return $ Yield x (GBracketIOException (Stream step1 s))+            Skip s    -> return $ Skip (GBracketIOException (Stream step1 s))+            Stop      -> return Stop+ -- | Run the action @m b@ before the stream yields its first element. -- -- Same as the following but more efficient due to fusion:@@ -44,10 +180,22 @@ -- >>> before action xs = Stream.nilM action <> xs -- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action) ---{-# INLINE before #-}+{-# INLINE_NORMAL before #-} before :: Monad m => m b -> Stream m a -> Stream m a-before action xs = fromStreamD $ D.before action $ toStreamD xs+before action (Stream step state) = Stream step' Nothing +    where++    {-# INLINE_LATE step' #-}+    step' _ Nothing = action >> return (Skip (Just state))++    step' gst (Just st) = do+        res <- step gst st+        case res of+            Yield x s -> return $ Yield x (Just s)+            Skip s    -> return $ Skip (Just s)+            Stop      -> return Stop+ -- | Like 'after', with following differences: -- -- * action @m b@ won't run if the stream is garbage collected@@ -61,10 +209,20 @@ -- -- /Pre-release/ ---{-# INLINE afterUnsafe #-}+{-# INLINE_NORMAL afterUnsafe #-} afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a-afterUnsafe action xs = fromStreamD $ D.afterUnsafe action $ toStreamD xs+afterUnsafe action (Stream step state) = Stream step' state +    where++    {-# INLINE_LATE step' #-}+    step' gst st = do+        res <- step gst st+        case res of+            Yield x s -> return $ Yield x s+            Skip s    -> return $ Skip s+            Stop      -> action >> return Stop+ -- | Run the action @IO b@ whenever the stream is evaluated to completion, or -- if it is garbage collected after a partial lazy evaluation. --@@ -73,51 +231,61 @@ -- -- /See also 'afterUnsafe'/ ---{-# INLINE afterIO #-}-afterIO :: MonadIO m => IO b -> Stream m a -> Stream m a-afterIO action xs = fromStreamD $ D.afterIO action $ toStreamD xs+{-# INLINE_NORMAL afterIO #-}+afterIO :: MonadIO m+    => IO b -> Stream m a -> Stream m a+afterIO action (Stream step state) = Stream step' Nothing +    where++    {-# INLINE_LATE step' #-}+    step' _ Nothing = do+        ref <- liftIO $ newIOFinalizer action+        return $ Skip $ Just (state, ref)+    step' gst (Just (st, ref)) = do+        res <- step gst st+        case res of+            Yield x s -> return $ Yield x (Just (s, ref))+            Skip s    -> return $ Skip (Just (s, ref))+            Stop      -> do+                runIOFinalizer ref+                return Stop++-- XXX For high performance error checks in busy streams we may need another+-- Error constructor in step.+ -- | Run the action @m b@ if the stream evaluation is aborted due to an -- exception. The exception is not caught, simply rethrown. --+-- Observes exceptions only in the stream generation, and not in stream+-- consumers.+-- -- /Inhibits stream fusion/ ---{-# INLINE onException #-}+{-# INLINE_NORMAL onException #-} onException :: MonadCatch m => m b -> Stream m a -> Stream m a-onException action xs = fromStreamD $ D.onException action $ toStreamD xs+onException action stream =+    gbracket_+        (return ()) -- before+        return      -- after+        (\_ (e :: MC.SomeException) _ -> action >> MC.throwM e)+        (inline MC.try)+        (const stream) --- | Like 'finally' with following differences:------ * action @m b@ won't run if the stream is garbage collected---   after partial evaluation.--- * has slightly better performance than 'finallyIO'.------ /Inhibits stream fusion/------ /Pre-release/----{-# INLINE finallyUnsafe #-}-finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a-finallyUnsafe action xs = fromStreamD $ D.finallyUnsafe action $ toStreamD xs+{-# INLINE_NORMAL _onException #-}+_onException :: MonadCatch m => m b -> Stream m a -> Stream m a+_onException action (Stream step state) = Stream step' state --- | Run the action @IO b@ whenever the stream stream stops normally, aborts--- due to an exception or if it is garbage collected after a partial lazy--- evaluation.------ The semantics of running the action @IO b@ are similar to the cleanup action--- semantics described in 'bracketIO'.------ >>> finallyIO release = Stream.bracketIO (return ()) (const release)------ /See also 'finallyUnsafe'/------ /Inhibits stream fusion/----{-# INLINE finallyIO #-}-finallyIO :: (MonadIO m, MonadCatch m) =>-    IO b -> Stream m a -> Stream m a-finallyIO action xs = fromStreamD $ D.finallyIO action $ toStreamD xs+    where +    {-# INLINE_LATE step' #-}+    step' gst st = do+        res <- step gst st `MC.onException` action+        case res of+            Yield x s -> return $ Yield x s+            Skip s    -> return $ Skip s+            Stop      -> return Stop+ -- | Like 'bracket' but with following differences: -- -- * alloc action @m b@ runs with async exceptions enabled@@ -129,19 +297,63 @@ -- -- /Pre-release/ ---{-# INLINE bracketUnsafe #-}+{-# INLINE_NORMAL bracketUnsafe #-} bracketUnsafe :: MonadCatch m     => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a-bracketUnsafe bef aft bet = fromStreamD $ D.bracketUnsafe bef aft (toStreamD . bet)+bracketUnsafe bef aft =+    gbracket_+        bef+        aft+        (\a (e :: SomeException) _ -> aft a >> MC.throwM e)+        (inline MC.try) +-- For a use case of this see the "streamly-process" package. It needs to kill+-- the process in case of exception or garbage collection, but waits for the+-- process to terminate in normal cases.++-- | Like 'bracketIO' but can use 3 separate cleanup actions depending on the+-- mode of termination:+--+-- 1. When the stream stops normally+-- 2. When the stream is garbage collected+-- 3. When the stream encounters an exception+--+-- @bracketIO3 before onStop onGC onException action@ runs @action@ using the+-- result of @before@. If the stream stops, @onStop@ action is executed, if the+-- stream is abandoned @onGC@ is executed, if the stream encounters an+-- exception @onException@ is executed.+--+-- The exception is not caught, it is rethrown.+--+-- /Inhibits stream fusion/+--+-- /Pre-release/+{-# INLINE_NORMAL bracketIO3 #-}+bracketIO3 :: (MonadIO m, MonadCatch m) =>+       IO b+    -> (b -> IO c)+    -> (b -> IO d)+    -> (b -> IO e)+    -> (b -> Stream m a)+    -> Stream m a+bracketIO3 bef aft onExc onGC =+    gbracket+        bef+        aft+        (\a (e :: SomeException) _ -> onExc a >> MC.throwM e)+        onGC+        (inline MC.try)+ -- | Run the alloc action @IO b@ with async exceptions disabled but keeping -- blocking operations interruptible (see 'Control.Exception.mask').  Use the--- output @b@ as input to @b -> Stream m a@ to generate an output stream.+-- output @b@ of the IO action as input to the function @b -> Stream m a@ to+-- generate an output stream. -- -- @b@ is usually a resource under the IO monad, e.g. a file handle, that -- requires a cleanup after use. The cleanup action @b -> IO c@, runs whenever--- the stream ends normally, due to a sync or async exception or if it gets--- garbage collected after a partial lazy evaluation.+-- (1) the stream ends normally, (2) due to a sync or async exception or, (3)+-- if it gets garbage collected after a partial lazy evaluation. The exception+-- is not caught, it is rethrown. -- -- 'bracketIO' only guarantees that the cleanup action runs, and it runs with -- async exceptions enabled. The action must ensure that it can successfully@@ -150,7 +362,13 @@ -- When the stream ends normally or on a sync exception, cleanup action runs -- immediately in the current thread context, whereas in other cases it runs in -- the GC context, therefore, cleanup may be delayed until the GC gets to run.+-- An example where GC based cleanup happens is when a stream is being folded+-- but the fold terminates without draining the entire stream or if the+-- consumer of the stream encounters an exception. --+-- Observes exceptions only in the stream generation, and not in stream+-- consumers.+-- -- /See also: 'bracketUnsafe'/ -- -- /Inhibits stream fusion/@@ -160,36 +378,64 @@     => IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a bracketIO bef aft = bracketIO3 bef aft aft aft --- For a use case of this see the "streamly-process" package. It needs to kill--- the process in case of exception or garbage collection, but waits for the--- process to terminate in normal cases.+data BracketState s v = BracketInit | BracketRun s v --- | Like 'bracketIO' but can use 3 separate cleanup actions depending on the--- mode of termination:+-- | Alternate (custom) implementation of 'bracket'. ----- 1. When the stream stops normally--- 2. When the stream is garbage collected--- 3. When the stream encounters an exception+{-# INLINE_NORMAL _bracket #-}+_bracket :: MonadCatch m+    => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a+_bracket bef aft bet = Stream step' BracketInit++    where++    {-# INLINE_LATE step' #-}+    step' _ BracketInit = bef >>= \x -> return (Skip (BracketRun (bet x) x))++    -- NOTE: It is important to use UnStream instead of the Stream pattern+    -- here, otherwise we get huge perf degradation, see note in concatMap.+    step' gst (BracketRun (UnStream step state) v) = do+        -- res <- step gst state `MC.onException` aft v+        res <- inline MC.try $ step gst state+        case res of+            Left (e :: SomeException) -> aft v >> MC.throwM e >> return Stop+            Right r -> case r of+                Yield x s -> return $ Yield x (BracketRun (Stream step s) v)+                Skip s    -> return $ Skip (BracketRun (Stream step s) v)+                Stop      -> aft v >> return Stop++-- | Like 'finally' with following differences: ----- @bracketIO3 before onStop onGC onException action@ runs @action@ using the--- result of @before@. If the stream stops, @onStop@ action is executed, if the--- stream is abandoned @onGC@ is executed, if the stream encounters an--- exception @onException@ is executed.+-- * action @m b@ won't run if the stream is garbage collected+--   after partial evaluation.+-- * has slightly better performance than 'finallyIO'. -- -- /Inhibits stream fusion/ -- -- /Pre-release/-{-# INLINE bracketIO3 #-}-bracketIO3 :: (MonadIO m, MonadCatch m)-    => IO b-    -> (b -> IO c)-    -> (b -> IO d)-    -> (b -> IO e)-    -> (b -> Stream m a)-    -> Stream m a-bracketIO3 bef aft gc exc bet = fromStreamD $-    D.bracketIO3 bef aft exc gc (toStreamD . bet)+--+{-# INLINE finallyUnsafe #-}+finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a+finallyUnsafe action xs = bracketUnsafe (return ()) (const action) (const xs) +-- | Run the action @IO b@ whenever the stream stream stops normally, aborts+-- due to an exception or if it is garbage collected after a partial lazy+-- evaluation.+--+-- The semantics of running the action @IO b@ are similar to the cleanup action+-- semantics described in 'bracketIO'.+--+-- >>> finallyIO release = Stream.bracketIO (return ()) (const release)+--+-- /See also 'finallyUnsafe'/+--+-- /Inhibits stream fusion/+--+{-# INLINE finallyIO #-}+finallyIO :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a+finallyIO action xs = bracketIO3 (return ()) act act act (const xs)+    where act _ = action+ -- | Like 'handle' but the exception handler is also provided with the stream -- that generated the exception as input. The exception handler can thus -- re-evaluate the stream to retry the action that failed. The exception@@ -202,21 +448,51 @@ -- -- /Pre-release/ ---{-# INLINE ghandle #-}+{-# INLINE_NORMAL ghandle #-} ghandle :: (MonadCatch m, Exception e)-    => (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a-ghandle handler =-      fromStreamD-    . D.ghandle (\e xs -> toStreamD $ handler e (fromStreamD xs))-    . toStreamD+    => (e -> Stream m a -> m (Stream m a)) -> Stream m a -> Stream m a+ghandle f stream =+    gbracket_ (return ()) return (const f) (inline MC.try) (const stream)  -- | When evaluating a stream if an exception occurs, stream evaluation aborts -- and the specified exception handler is run with the exception as argument.+-- The exception is caught and handled unless the handler decides to rethrow+-- it. Note that exception handling is not applied to the stream returned by+-- the exception handler. --+-- Observes exceptions only in the stream generation, and not in stream+-- consumers.+-- -- /Inhibits stream fusion/ ---{-# INLINE handle #-}+{-# INLINE_NORMAL handle #-} handle :: (MonadCatch m, Exception e)+    => (e -> m (Stream m a)) -> Stream m a -> Stream m a+handle f stream =+    gbracket_ (return ()) return (\_ e _ -> f e) (inline MC.try) (const stream)++-- | Alternate (custom) implementation of 'handle'.+--+{-# INLINE_NORMAL _handle #-}+_handle :: (MonadCatch m, Exception e)     => (e -> Stream m a) -> Stream m a -> Stream m a-handle handler xs =-    fromStreamD $ D.handle (toStreamD . handler) $ toStreamD xs+_handle f (Stream step state) = Stream step' (Left state)++    where++    {-# INLINE_LATE step' #-}+    step' gst (Left st) = do+        res <- inline MC.try $ step gst st+        case res of+            Left e -> return $ Skip $ Right (f e)+            Right r -> case r of+                Yield x s -> return $ Yield x (Left s)+                Skip s    -> return $ Skip (Left s)+                Stop      -> return Stop++    step' gst (Right (UnStream step1 st)) = do+        res <- step1 gst st+        case res of+            Yield x s -> return $ Yield x (Right (Stream step1 s))+            Skip s    -> return $ Skip (Right (Stream step1 s))+            Stop      -> return Stop
− src/Streamly/Internal/Data/Stream/Expand.hs
@@ -1,893 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.Expand--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Expand a stream by combining two or more streams or by combining streams--- with unfolds.--module Streamly.Internal.Data.Stream.Expand-    (-    -- * Binary Combinators (Linear)-    -- | Functions ending in the shape:-    ---    -- @Stream m a -> Stream m a -> Stream m a@.-    ---    -- The functions in this section have a linear or flat n-ary combining-    -- characterstics. It means that when combined @n@ times (e.g. @a `serial`-    -- b `serial` c ...@) the resulting expression will have an @O(n)@-    -- complexity (instead O(n^2) for pair wise combinators described in the-    -- next section. These functions can be used efficiently with-    -- 'concatMapWith' et. al.  combinators that combine streams in a linear-    -- fashion (contrast with 'mergeMapWith' which combines streams as a-    -- binary tree).--      append-    -- * Binary Combinators (Pair Wise)-    -- | Like the functions in the section above these functions also combine-    -- two streams into a single stream but when used @n@ times linearly they-    -- exhibit O(n^2) complexity. They are best combined in a binary tree-    -- fashion using 'mergeMapWith' giving a @n * log n@ complexity.  Avoid-    -- using these with 'concatMapWith' when combining a large or infinite-    -- number of streams.--    -- ** Append-    , append2--    -- ** Interleave-    , interleave-    , interleave2-    , interleaveFst-    , interleaveFst2-    , interleaveFstSuffix2-    , interleaveMin-    , interleaveMin2--    -- ** Round Robin-    , roundrobin--    -- ** Merge-    , mergeBy-    , mergeByM-    , mergeByM2-    , mergeMinBy-    , mergeFstBy--    -- ** Zip-    , zipWith-    , zipWithM--    -- * Combine Streams and Unfolds-    -- |-    -- Expand a stream by repeatedly using an unfold and merging the resulting-    -- streams.  Functions generally ending in the shape:-    ---    -- @Unfold m a b -> Stream m a -> Stream m b@--    -- ** Unfold and combine streams-    -- | Unfold and flatten streams.-    , unfoldMany -- XXX Rename to unfoldAppend-    , unfoldInterleave-    , unfoldRoundRobin--    -- ** Interpose-    -- | Insert effects between streams. Like unfoldMany but intersperses an-    -- effect between the streams. A special case of gintercalate.-    , interpose-    , interposeSuffix-    -- , interposeBy--    -- ** Intercalate-    -- | Insert Streams between Streams.-    -- Like unfoldMany but intersperses streams from another source between-    -- the streams from the first source.-    , intercalate-    , intercalateSuffix-    , gintercalate-    , gintercalateSuffix--    -- * Combine Streams of Streams-    -- | Map and serially append streams. 'concatMapM' is a generalization of-    -- the binary append operation to append many streams.-    , concatMapM-    , concatMap-    , concatEffect-    , concat--    -- * ConcatMapWith-    -- | Map and flatten a stream like 'concatMap' but using a custom binary-    -- stream merging combinator instead of just appending the streams.  The-    -- merging occurs sequentially, it works efficiently for 'serial', 'async',-    -- 'ahead' like merge operations where we consume one stream before the-    -- next or in case of 'wAsync' or 'parallel' where we consume all streams-    -- simultaneously anyway.-    ---    -- However, in cases where the merging consumes streams in a round robin-    -- fashion, a pair wise merging using 'mergeMapWith' would be more-    -- efficient. These cases include operations like 'mergeBy' or 'zipWith'.--    , concatMapWith-    , bindWith-    , concatSmapMWith--    -- * MergeMapWith-    -- | See the notes about suitable merge functions in the 'concatMapWith'-    -- section.-    , mergeMapWith--    -- * Iterate-    -- | Map and flatten Trees of Streams-    , unfoldIterateDfs-    , unfoldIterateBfs-    , unfoldIterateBfsRev--    , concatIterateWith-    , mergeIterateWith--    , concatIterateDfs-    , concatIterateBfs--    -- More experimental ops-    , concatIterateBfsRev-    , concatIterateLeftsWith-    , concatIterateScanWith-    , concatIterateScan-    )-where--#include "inline.hs"--import Streamly.Internal.Data.Stream.Bottom-    ( concatEffect, concatMapM, concatMap, smapM, zipWith, zipWithM)-import Streamly.Internal.Data.Stream.Type-    ( Stream, fromStreamD, fromStreamK, toStreamD, toStreamK-    , bindWith, concatMapWith, cons, nil)-import Streamly.Internal.Data.Unfold.Type (Unfold)--import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamK as K (mergeBy, mergeByM)-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K--import Prelude hiding (concat, concatMap, zipWith)---- $setup--- >>> :m--- >>> import Data.Either (either)--- >>> import Data.IORef--- >>> import Streamly.Internal.Data.Stream (Stream)--- >>> import Prelude hiding (zipWith, concatMap, concat)--- >>> import qualified Streamly.Data.Array as Array--- >>> import qualified Streamly.Internal.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Stream as Stream--- >>> import qualified Streamly.Internal.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Parser as Parser--- >>> import qualified Streamly.Internal.FileSystem.Dir as Dir-------------------------------------------------------------------------------------- Appending---------------------------------------------------------------------------------infixr 6 `append2`---- | This is fused version of 'append'. It could be up to 100x faster than--- 'append' when combining two fusible streams. However, it slows down--- quadratically with the number of streams being appended. Therefore, it is--- suitable for ad-hoc append of a few streams, and should not be used with--- 'concatMapWith' or 'mergeMapWith'.------ /Fused/----{-# INLINE append2 #-}-append2 ::Monad m => Stream m b -> Stream m b -> Stream m b-append2 m1 m2 = fromStreamD $ D.append (toStreamD m1) (toStreamD m2)--infixr 6 `append`---- | Appends two streams sequentially, yielding all elements from the first--- stream, and then all elements from the second stream.------ >>> s1 = Stream.fromList [1,2]--- >>> s2 = Stream.fromList [3,4]--- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2--- [1,2,3,4]------ This has O(n) append performance where @n@ is the number of streams. It can--- be used to efficiently fold an infinite lazy container of streams--- 'concatMapWith' et. al.------ See 'append2' for a fusible alternative.------ /CPS/-{-# INLINE append #-}-append :: Stream m a -> Stream m a -> Stream m a-append = (<>)----------------------------------------------------------------------------------- Interleaving---------------------------------------------------------------------------------infixr 6 `interleave`---- | Interleaves two streams, yielding one element from each stream--- alternately.  When one stream stops the rest of the other stream is used in--- the output stream.------ When joining many streams in a left associative manner earlier streams will--- get exponential priority than the ones joining later. Because of exponential--- weighting it can be used with 'concatMapWith' even on a large number of--- streams.------ See 'interleave2' for a fusible alternative.------ /CPS/-{-# INLINE interleave #-}-interleave :: Stream m a -> Stream m a -> Stream m a-interleave s1 s2 = fromStreamK $ K.interleave (toStreamK s1) (toStreamK s2)--{-# INLINE interleave2 #-}-interleave2 :: Monad m => Stream m a -> Stream m a -> Stream m a-interleave2 s1 s2 = fromStreamD $ D.interleave (toStreamD s1) (toStreamD s2)---- | Like `interleave` but stops interleaving as soon as the first stream--- stops.------ See 'interleaveFst2' for a fusible alternative.------ /CPS/-{-# INLINE interleaveFst #-}-interleaveFst :: Stream m a -> Stream m a -> Stream m a-interleaveFst s1 s2 =-    fromStreamK $ K.interleaveFst (toStreamK s1) (toStreamK s2)---- | Like `interleave` but stops interleaving as soon as any of the two streams--- stops.------ See 'interleaveMin2' for a fusible alternative.------ /CPS/-{-# INLINE interleaveMin #-}-interleaveMin :: Stream m a -> Stream m a -> Stream m a-interleaveMin s1 s2 =-    fromStreamK $ K.interleaveMin (toStreamK s1) (toStreamK s2)--{-# INLINE interleaveMin2 #-}-interleaveMin2 :: Monad m => Stream m a -> Stream m a -> Stream m a-interleaveMin2 s1 s2 =-    fromStreamD $ D.interleaveMin (toStreamD s1) (toStreamD s2)---- | Interleaves the outputs of two streams, yielding elements from each stream--- alternately, starting from the first stream. As soon as the first stream--- finishes, the output stops, discarding the remaining part of the second--- stream. In this case, the last element in the resulting stream would be from--- the second stream. If the second stream finishes early then the first stream--- still continues to yield elements until it finishes.------ >>> :set -XOverloadedStrings--- >>> import Data.Functor.Identity (Identity)--- >>> Stream.interleaveFstSuffix2 "abc" ",,,," :: Stream Identity Char--- fromList "a,b,c,"--- >>> Stream.interleaveFstSuffix2 "abc" "," :: Stream Identity Char--- fromList "a,bc"------ 'interleaveFstSuffix2' is a dual of 'interleaveFst2'.------ Do not use at scale in concatMapWith.------ /Pre-release/-{-# INLINE interleaveFstSuffix2 #-}-interleaveFstSuffix2 :: Monad m => Stream m b -> Stream m b -> Stream m b-interleaveFstSuffix2 m1 m2 =-    fromStreamD $ D.interleaveFstSuffix (toStreamD m1) (toStreamD m2)---- | Interleaves the outputs of two streams, yielding elements from each stream--- alternately, starting from the first stream and ending at the first stream.--- If the second stream is longer than the first, elements from the second--- stream are infixed with elements from the first stream. If the first stream--- is longer then it continues yielding elements even after the second stream--- has finished.------ >>> :set -XOverloadedStrings--- >>> import Data.Functor.Identity (Identity)--- >>> Stream.interleaveFst2 "abc" ",,,," :: Stream Identity Char--- fromList "a,b,c"--- >>> Stream.interleaveFst2 "abc" "," :: Stream Identity Char--- fromList "a,bc"------ 'interleaveFst2' is a dual of 'interleaveFstSuffix2'.------ Do not use at scale in concatMapWith.------ /Pre-release/-{-# INLINE interleaveFst2 #-}-interleaveFst2 :: Monad m => Stream m b -> Stream m b -> Stream m b-interleaveFst2 m1 m2 =-    fromStreamD $ D.interleaveFst (toStreamD m1) (toStreamD m2)----------------------------------------------------------------------------------- Scheduling----------------------------------------------------------------------------------- | Schedule the execution of two streams in a fair round-robin manner,--- executing each stream once, alternately. Execution of a stream may not--- necessarily result in an output, a stream may chose to @Skip@ producing an--- element until later giving the other stream a chance to run. Therefore, this--- combinator fairly interleaves the execution of two streams rather than--- fairly interleaving the output of the two streams. This can be useful in--- co-operative multitasking without using explicit threads. This can be used--- as an alternative to `async`.------ Do not use at scale in concatMapWith.------ /Pre-release/-{-# INLINE roundrobin #-}-roundrobin :: Monad m => Stream m b -> Stream m b -> Stream m b-roundrobin m1 m2 = fromStreamD $ D.roundRobin (toStreamD m1) (toStreamD m2)----------------------------------------------------------------------------------- Merging (sorted streams)----------------------------------------------------------------------------------- | Merge two streams using a comparison function. The head elements of both--- the streams are compared and the smaller of the two elements is emitted, if--- both elements are equal then the element from the first stream is used--- first.------ If the streams are sorted in ascending order, the resulting stream would--- also remain sorted in ascending order.------ >>> s1 = Stream.fromList [1,3,5]--- >>> s2 = Stream.fromList [2,4,6,8]--- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2--- [1,2,3,4,5,6,8]------ See 'mergeByM2' for a fusible alternative.------ /CPS/-{-# INLINE mergeBy #-}-mergeBy :: (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeBy f m1 m2 = fromStreamK $ K.mergeBy f (toStreamK m1) (toStreamK m2)---- | Like 'mergeBy' but with a monadic comparison function.------ Merge two streams randomly:------ @--- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT--- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])--- [2,1,2,2,2,1,1,1]--- @------ Merge two streams in a proportion of 2:1:------ >>> :{--- do---  let s1 = Stream.fromList [1,1,1,1,1,1]---      s2 = Stream.fromList [2,2,2]---  let proportionately m n = do---       ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]---       return $ \_ _ -> do---          r <- readIORef ref---          writeIORef ref $ Prelude.tail r---          return $ Prelude.head r---  f <- proportionately 2 1---  xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2---  print xs--- :}--- [1,1,2,1,1,2,1,1,2]------ See 'mergeByM2' for a fusible alternative.------ /CPS/-{-# INLINE mergeByM #-}-mergeByM-    :: Monad m-    => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeByM f m1 m2 = fromStreamK $ K.mergeByM f (toStreamK m1) (toStreamK m2)---- | Like 'mergeByM' but much faster, works best when merging statically known--- number of streams. When merging more than two streams try to merge pairs and--- pair of pairs in a tree like structure.'mergeByM' works better with variable--- number of streams being merged using 'mergeMapWith'.------ /Internal/-{-# INLINE mergeByM2 #-}-mergeByM2-    :: Monad m-    => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeByM2 f m1 m2 =-    fromStreamD $ D.mergeByM f (toStreamD m1) (toStreamD m2)---- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.------ /Unimplemented/-{-# INLINABLE mergeMinBy #-}-mergeMinBy :: -- Monad m =>-    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeMinBy _f _m1 _m2 = undefined-    -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)---- | Like 'mergeByM' but stops merging as soon as the first stream stops.------ /Unimplemented/-{-# INLINABLE mergeFstBy #-}-mergeFstBy :: -- Monad m =>-    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeFstBy _f _m1 _m2 = undefined-    -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)----------------------------------------------------------------------------------- Combine N Streams - unfoldMany----------------------------------------------------------------------------------- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike--- 'concatMap' this can fuse the 'Unfold' code with the inner loop and--- therefore provide many times better performance.----{-# INLINE unfoldMany #-}-unfoldMany ::Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldMany u m = fromStreamD $ D.unfoldMany u (toStreamD m)---- | This does not pair streams like mergeMapWith, instead, it goes through--- each stream one by one and yields one element from each stream. After it--- goes to the last stream it reverses the traversal to come back to the first--- stream yielding elements from each stream on its way back to the first--- stream and so on.------ >>> lists = Stream.fromList [[1,1],[2,2],[3,3],[4,4],[5,5]]--- >>> interleaved = Stream.unfoldInterleave Unfold.fromList lists--- >>> Stream.fold Fold.toList interleaved--- [1,2,3,4,5,5,4,3,2,1]------ Note that this is order of magnitude more efficient than "mergeMapWith--- wSerial" because of fusion.------ /Fused/-{-# INLINE unfoldInterleave #-}-unfoldInterleave ::Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldInterleave u m =-    fromStreamD $ D.unfoldInterleave u (toStreamD m)---- | 'unfoldInterleave' switches to the next stream whenever a value from a--- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps--- skipping for long time other streams won't get a chance to run.--- 'unfoldRoundRobin' switches on Skip as well. So it basically schedules each--- stream fairly irrespective of whether it produces a value or not.----{-# INLINE unfoldRoundRobin #-}-unfoldRoundRobin ::Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldRoundRobin u m =-    fromStreamD $ D.unfoldRoundRobin u (toStreamD m)----------------------------------------------------------------------------------- Combine N Streams - interpose----------------------------------------------------------------------------------- > interpose x unf str = gintercalate unf str UF.identity (repeat x)---- | Unfold the elements of a stream, intersperse the given element between the--- unfolded streams and then concat them into a single stream.------ >>> unwords = Stream.interpose ' '------ /Pre-release/-{-# INLINE interpose #-}-interpose :: Monad m-    => c -> Unfold m b c -> Stream m b -> Stream m c-interpose x unf str =-    fromStreamD $ D.interpose x unf (toStreamD str)---- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)---- | Unfold the elements of a stream, append the given element after each--- unfolded stream and then concat them into a single stream.------ >>> unlines = Stream.interposeSuffix '\n'------ /Pre-release/-{-# INLINE interposeSuffix #-}-interposeSuffix :: Monad m-    => c -> Unfold m b c -> Stream m b -> Stream m c-interposeSuffix x unf str =-    fromStreamD $ D.interposeSuffix x unf (toStreamD str)----------------------------------------------------------------------------------- Combine N Streams - intercalate----------------------------------------------------------------------------------- XXX we can swap the order of arguments to gintercalate so that the--- definition of unfoldMany becomes simpler? The first stream should be--- infixed inside the second one. However, if we change the order in--- "interleave" as well similarly, then that will make it a bit unintuitive.------ > unfoldMany unf str =--- >     gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())---- | 'interleaveFst' followed by unfold and concat.------ /Pre-release/-{-# INLINE gintercalate #-}-gintercalate-    :: Monad m-    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c-gintercalate unf1 str1 unf2 str2 =-    fromStreamD $ D.gintercalate-        unf1 (toStreamD str1)-        unf2 (toStreamD str2)---- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)---- | 'intersperse' followed by unfold and concat.------ >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a--- >>> intersperse = Stream.intercalate Unfold.identity--- >>> unwords = Stream.intercalate Unfold.fromList " "------ >>> input = Stream.fromList ["abc", "def", "ghi"]--- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input--- "abc def ghi"----{-# INLINE intercalate #-}-intercalate :: Monad m-    => Unfold m b c -> b -> Stream m b -> Stream m c-intercalate unf seed str = fromStreamD $-    D.unfoldMany unf $ D.intersperse seed (toStreamD str)---- | 'interleaveFstSuffix2' followed by unfold and concat.------ /Pre-release/-{-# INLINE gintercalateSuffix #-}-gintercalateSuffix-    :: Monad m-    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c-gintercalateSuffix unf1 str1 unf2 str2 =-    fromStreamD $ D.gintercalateSuffix-        unf1 (toStreamD str1)-        unf2 (toStreamD str2)---- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)---- | 'intersperseMSuffix' followed by unfold and concat.------ >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseMSuffix a--- >>> intersperseMSuffix = Stream.intercalateSuffix Unfold.identity--- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"------ >>> input = Stream.fromList ["abc", "def", "ghi"]--- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input--- "abc\ndef\nghi\n"----{-# INLINE intercalateSuffix #-}-intercalateSuffix :: Monad m-    => Unfold m b c -> b -> Stream m b -> Stream m c-intercalateSuffix unf seed =-    fromStreamD . D.intercalateSuffix unf seed . toStreamD----------------------------------------------------------------------------------- Combine N Streams - concatMap----------------------------------------------------------------------------------- | Flatten a stream of streams to a single stream.------ >>> concat = Stream.concatMap id------ /Pre-release/-{-# INLINE concat #-}-concat :: Monad m => Stream m (Stream m a) -> Stream m a-concat = concatMap id----------------------------------------------------------------------------------- Combine N Streams - concatMap----------------------------------------------------------------------------------- | Like 'concatMapWith' but carries a state which can be used to share--- information across multiple steps of concat.------ >>> concatSmapMWith combine f initial = Stream.concatMapWith combine id . Stream.smapM f initial------ /Pre-release/----{-# INLINE concatSmapMWith #-}-concatSmapMWith-    :: Monad m-    => (Stream m b -> Stream m b -> Stream m b)-    -> (s -> a -> m (s, Stream m b))-    -> m s-    -> Stream m a-    -> Stream m b-concatSmapMWith combine f initial =-    concatMapWith combine id . smapM f initial---- XXX Implement a StreamD version for fusion.---- | Combine streams in pairs using a binary combinator, the resulting streams--- are then combined again in pairs recursively until we get to a single--- combined stream. The composition would thus form a binary tree.------ For example, you can sort a stream using merge sort like this:------ >>> s = Stream.fromList [5,1,7,9,2]--- >>> generate = Stream.fromPure--- >>> combine = Stream.mergeBy compare--- >>> Stream.fold Fold.toList $ Stream.mergeMapWith combine generate s--- [1,2,5,7,9]------ Note that if the stream length is not a power of 2, the binary tree composed--- by mergeMapWith would not be balanced, which may or may not be important--- depending on what you are trying to achieve.------ /Caution: the stream of streams must be finite/------ /CPS/------ /Pre-release/----{-# INLINE mergeMapWith #-}-mergeMapWith ::-       (Stream m b -> Stream m b -> Stream m b)-    -> (a -> Stream m b)-    -> Stream m a-    -> Stream m b-mergeMapWith par f m =-    fromStreamK-        $ K.mergeMapWith-            (\s1 s2 -> toStreamK $ fromStreamK s1 `par` fromStreamK s2)-            (toStreamK . f)-            (toStreamK m)----------------------------------------------------------------------------------- concatIterate - Map and flatten Trees of Streams----------------------------------------------------------------------------------- | Yield an input element in the output stream, map a stream generator on it--- and repeat the process on the resulting stream. Resulting streams are--- flattened using the 'concatMapWith' combinator. This can be used for a depth--- first style (DFS) traversal of a tree like structure.------ Example, list a directory tree using DFS:------ >>> f = either Dir.readEitherPaths (const Stream.nil)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateWith Stream.append f input------ Note that 'iterateM' is a special case of 'concatIterateWith':------ >>> iterateM f = Stream.concatIterateWith Stream.append (Stream.fromEffect . f) . Stream.fromEffect------ /CPS/------ /Pre-release/----{-# INLINE concatIterateWith #-}-concatIterateWith ::-       (Stream m a -> Stream m a -> Stream m a)-    -> (a -> Stream m a)-    -> Stream m a-    -> Stream m a-concatIterateWith combine f = iterateStream--    where--    iterateStream = concatMapWith combine generate--    generate x = x `cons` iterateStream (f x)---- | Traverse the stream in depth first style (DFS). Map each element in the--- input stream to a stream and flatten, recursively map the resulting elements--- as well to a stream and flatten until no more streams are generated.------ Example, list a directory tree using DFS:------ >>> f = either (Just . Dir.readEitherPaths) (const Nothing)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateDfs f input------ This is equivalent to using @concatIterateWith Stream.append@.------ /Pre-release/-{-# INLINE concatIterateDfs #-}-concatIterateDfs :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateDfs f stream =-    fromStreamD-        $ D.concatIterateDfs (fmap toStreamD . f ) (toStreamD stream)---- | Similar to 'concatIterateDfs' except that it traverses the stream in--- breadth first style (BFS). First, all the elements in the input stream are--- emitted, and then their traversals are emitted.------ Example, list a directory tree using BFS:------ >>> f = either (Just . Dir.readEitherPaths) (const Nothing)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateBfs f input------ /Pre-release/-{-# INLINE concatIterateBfs #-}-concatIterateBfs :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateBfs f stream =-    fromStreamD-        $ D.concatIterateBfs (fmap toStreamD . f ) (toStreamD stream)---- | Same as 'concatIterateBfs' except that the traversal of the last--- element on a level is emitted first and then going backwards up to the first--- element (reversed ordering). This may be slightly faster than--- 'concatIterateBfs'.----{-# INLINE concatIterateBfsRev #-}-concatIterateBfsRev :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateBfsRev f stream =-    fromStreamD-        $ D.concatIterateBfsRev (fmap toStreamD . f ) (toStreamD stream)---- | Like 'concatIterateWith' but uses the pairwise flattening combinator--- 'mergeMapWith' for flattening the resulting streams. This can be used for a--- balanced traversal of a tree like structure.------ Example, list a directory tree using balanced traversal:------ >>> f = either Dir.readEitherPaths (const Stream.nil)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.mergeIterateWith Stream.interleave f input------ /CPS/------ /Pre-release/----{-# INLINE mergeIterateWith #-}-mergeIterateWith ::-       (Stream m a -> Stream m a -> Stream m a)-    -> (a -> Stream m a)-    -> Stream m a-    -> Stream m a-mergeIterateWith combine f = iterateStream--    where--    iterateStream = mergeMapWith combine generate--    generate x = x `cons` iterateStream (f x)---- | Same as @concatIterateDfs@ but more efficient due to stream fusion.------ Example, list a directory tree using DFS:------ >>> f = Unfold.either Dir.eitherReaderPaths Unfold.nil--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.unfoldIterateDfs f input------ /Pre-release/-{-# INLINE unfoldIterateDfs #-}-unfoldIterateDfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a-unfoldIterateDfs u = fromStreamD . D.unfoldIterateDfs u . toStreamD---- | Like 'unfoldIterateDfs' but uses breadth first style traversal.------ /Pre-release/-{-# INLINE unfoldIterateBfs #-}-unfoldIterateBfs :: Monad m => Unfold m a a -> Stream m a -> Stream m a-unfoldIterateBfs u = fromStreamD . D.unfoldIterateBfs u . toStreamD---- | Like 'unfoldIterateBfs' but processes the children in reverse order,--- therefore, may be slightly faster.------ /Pre-release/-{-# INLINE unfoldIterateBfsRev #-}-unfoldIterateBfsRev :: Monad m => Unfold m a a -> Stream m a -> Stream m a-unfoldIterateBfsRev u =-    fromStreamD . D.unfoldIterateBfsRev u . toStreamD----------------------------------------------------------------------------------- Flattening Graphs----------------------------------------------------------------------------------- To traverse graphs we need a state to be carried around in the traversal.--- For example, we can use a hashmap to store the visited status of nodes.---- | Like 'iterateMap' but carries a state in the stream generation function.--- This can be used to traverse graph like structures, we can remember the--- visited nodes in the state to avoid cycles.------ Note that a combination of 'iterateMap' and 'usingState' can also be used to--- traverse graphs. However, this function provides a more localized state--- instead of using a global state.------ See also: 'mfix'------ /Pre-release/----{-# INLINE concatIterateScanWith #-}-concatIterateScanWith-    :: Monad m-    => (Stream m a -> Stream m a -> Stream m a)-    -> (b -> a -> m (b, Stream m a))-    -> m b-    -> Stream m a-    -> Stream m a-concatIterateScanWith combine f initial stream =-    concatEffect $ do-        b <- initial-        iterateStream (b, stream)--    where--    iterateStream (b, s) = pure $ concatMapWith combine (generate b) s--    generate b a = a `cons` feedback b a--    feedback b a = concatEffect $ f b a >>= iterateStream---- Next stream is to be generated by the return value of the previous stream. A--- general intuitive way of doing that could be to use an appending monad--- instance for streams where the result of the previous stream is used to--- generate the next one. In the first pass we can just emit the values in the--- stream and keep building a buffered list/stream, once done we can then--- process the buffered stream.--{-# INLINE concatIterateScan #-}-concatIterateScan-    :: Monad m-    => (b -> a -> m b)-    -> (b -> m (Maybe (b, Stream m a)))-    -> b-    -> Stream m a-concatIterateScan scanner generate initial =-    fromStreamD-        $ D.concatIterateScan-            scanner (fmap (fmap (fmap toStreamD)) . generate) initial----------------------------------------------------------------------------------- Either streams----------------------------------------------------------------------------------- Keep concating either streams as long as rights are generated, stop as soon--- as a left is generated and concat the left stream.------ See also: 'handle'------ /Unimplemented/----{--concatMapEitherWith-    :: (forall x. t m x -> t m x -> t m x)-    -> (a -> t m (Either (Stream m b) b))-    -> Stream m a-    -> Stream m b-concatMapEitherWith = undefined--}---- XXX We should prefer using the Maybe stream returning signatures over this.--- This API should perhaps be removed in favor of those.---- | In an 'Either' stream iterate on 'Left's.  This is a special case of--- 'concatIterateWith':------ >>> concatIterateLeftsWith combine f = Stream.concatIterateWith combine (either f (const Stream.nil))------ To traverse a directory tree:------ >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateLeftsWith Stream.append Dir.readEither input------ /Pre-release/----{-# INLINE concatIterateLeftsWith #-}-concatIterateLeftsWith-    :: (b ~ Either a c)-    => (Stream m b -> Stream m b -> Stream m b)-    -> (a -> Stream m b)-    -> Stream m b-    -> Stream m b-concatIterateLeftsWith combine f =-    concatIterateWith combine (either f (const nil))
src/Streamly/Internal/Data/Stream/Generate.hs view
@@ -1,460 +1,1201 @@-{-# OPTIONS_GHC -Wno-orphans #-}---- |--- Module      : Streamly.Internal.Data.Stream.Generate--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Stream.Generate-    (-    -- * Primitives-      Stream.nil-    , Stream.nilM-    , Stream.cons-    , Stream.consM--    -- * From 'Unfold'-    , unfold--    -- * Unfolding-    , unfoldr-    , unfoldrM--    -- * From Values-    , Stream.fromPure-    , Stream.fromEffect-    , repeat-    , repeatM-    , replicate-    , replicateM--    -- * Enumeration-    , Enumerable (..)-    , enumerate-    , enumerateTo--    -- * Time Enumeration-    , times-    , timesWith-    , absTimes-    , absTimesWith-    , relTimes-    , relTimesWith-    , durations-    , timeout--    -- * Iteration-    , iterate-    , iterateM--    -- * Cyclic Elements-    , mfix--    -- * From Containers-    , Bottom.fromList-    , fromFoldable--    -- * From memory-    , fromPtr-    , fromPtrN-    , fromByteStr#- -- , fromByteArray#-    , fromUnboxedIORef-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO)-import Data.Word (Word8)-import Foreign.Storable (Storable)-import GHC.Exts (Addr#, Ptr (Ptr))-import Streamly.Internal.Data.Stream.Bottom-    (absTimesWith, relTimesWith, timesWith)-import Streamly.Internal.Data.Stream.Enumerate-    (Enumerable(..), enumerate, enumerateTo)-import Streamly.Internal.Data.Stream.Type-    (Stream, fromStreamD, fromStreamK, toStreamK)-import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64, addToAbsTime64)-import Streamly.Internal.Data.Unboxed (Unbox)-import Streamly.Internal.Data.Unfold.Type (Unfold)--import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed-import qualified Streamly.Internal.Data.Stream.Bottom as Bottom-import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-import qualified Streamly.Internal.Data.Stream.Type as Stream-import qualified Streamly.Internal.Data.Stream.Transform as Stream (sequence)--import Prelude hiding (iterate, replicate, repeat, take)---- $setup--- >>> :m--- >>> import Control.Concurrent (threadDelay)--- >>> import Data.Function (fix, (&))--- >>> import Data.Semigroup (cycle1)--- >>> import Streamly.Internal.Data.Stream.Cross (CrossStream(..))--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Stream as Stream--- >>> import GHC.Exts (Ptr (Ptr))----------------------------------------------------------------------------------- From Unfold----------------------------------------------------------------------------------- | Convert an 'Unfold' into a stream by supplying it an input seed.------ >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello")--- >>> Stream.fold Fold.drain s--- hello--- hello--- hello----{-# INLINE unfold #-}-unfold :: Monad m => Unfold m a b -> a -> Stream m b-unfold unf = Stream.fromStreamD . D.unfold unf----------------------------------------------------------------------------------- Generation by Unfolding----------------------------------------------------------------------------------- |--- >>> :{--- unfoldr step s =---     case step s of---         Nothing -> Stream.nil---         Just (a, b) -> a `Stream.cons` unfoldr step b--- :}------ Build a stream by unfolding a /pure/ step function @step@ starting from a--- seed @s@.  The step function returns the next element in the stream and the--- next seed value. When it is done it returns 'Nothing' and the stream ends.--- For example,------ >>> :{--- let f b =---         if b > 2---         then Nothing---         else Just (b, b + 1)--- in Stream.fold Fold.toList $ Stream.unfoldr f 0--- :}--- [0,1,2]----{-# INLINE_EARLY unfoldr #-}-unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a-unfoldr step seed = fromStreamD (D.unfoldr step seed)-{-# RULES "unfoldr fallback to StreamK" [1]-    forall a b. D.toStreamK (D.unfoldr a b) = K.unfoldr a b #-}---- | Build a stream by unfolding a /monadic/ step function starting from a--- seed.  The step function returns the next element in the stream and the next--- seed value. When it is done it returns 'Nothing' and the stream ends. For--- example,------ >>> :{--- let f b =---         if b > 2---         then return Nothing---         else return (Just (b, b + 1))--- in Stream.fold Fold.toList $ Stream.unfoldrM f 0--- :}--- [0,1,2]----{-# INLINE unfoldrM #-}-unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> Stream m a-unfoldrM step = fromStreamD . D.unfoldrM step----------------------------------------------------------------------------------- From Values----------------------------------------------------------------------------------- |--- Generate an infinite stream by repeating a pure value.----{-# INLINE_NORMAL repeat #-}-repeat :: Monad m => a -> Stream m a-repeat = fromStreamD . D.repeat---- |--- >>> repeatM = Stream.sequence . Stream.repeat--- >>> repeatM = fix . Stream.consM--- >>> repeatM = cycle1 . Stream.fromEffect------ Generate a stream by repeatedly executing a monadic action forever.------ >>> :{--- repeatAction =---        Stream.repeatM (threadDelay 1000000 >> print 1)---      & Stream.take 10---      & Stream.fold Fold.drain--- :}----{-# INLINE_NORMAL repeatM #-}-repeatM :: Monad m => m a -> Stream m a-repeatM = Stream.sequence . repeat---- |--- >>> replicate n = Stream.take n . Stream.repeat------ Generate a stream of length @n@ by repeating a value @n@ times.----{-# INLINE_NORMAL replicate #-}-replicate :: Monad m => Int -> a -> Stream m a-replicate n = fromStreamD . D.replicate n---- |--- >>> replicateM n = Stream.sequence . Stream.replicate n------ Generate a stream by performing a monadic action @n@ times.-{-# INLINE_NORMAL replicateM #-}-replicateM :: Monad m => Int -> m a -> Stream m a-replicateM n = Stream.sequence . replicate n----------------------------------------------------------------------------------- Time Enumeration----------------------------------------------------------------------------------- | @times@ returns a stream of time value tuples with clock of 10 ms--- granularity. The first component of the tuple is an absolute time reference--- (epoch) denoting the start of the stream and the second component is a time--- relative to the reference.------ >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)--- >>> Stream.fold f $ Stream.take 3 $ Stream.times--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE times #-}-times :: MonadIO m => Stream m (AbsTime, RelTime64)-times = timesWith 0.01---- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms--- granularity.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE absTimes #-}-absTimes :: MonadIO m => Stream m AbsTime-absTimes = fmap (uncurry addToAbsTime64) times---- | @relTimes@ returns a stream of relative time values starting from 0,--- using a clock of granularity 10 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE relTimes #-}-relTimes ::  MonadIO m => Stream m RelTime64-relTimes = fmap snd times---- | @durations g@ returns a stream of relative time values measuring the time--- elapsed since the immediate predecessor element of the stream was generated.--- The first element of the stream is always 0. @durations@ uses a clock of--- granularity @g@ specified in seconds. A low granularity clock is more--- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.--- Durations lower than 1 ms will be 0.------ Note: This API is not safe on 32-bit machines.------ /Unimplemented/----{-# INLINE durations #-}-durations :: -- Monad m =>-    Double -> t m RelTime64-durations = undefined---- | Generate a singleton event at or after the specified absolute time. Note--- that this is different from a threadDelay, a threadDelay starts from the--- time when the action is evaluated, whereas if we use AbsTime based timeout--- it will immediately expire if the action is evaluated too late.------ /Unimplemented/----{-# INLINE timeout #-}-timeout :: -- Monad m =>-    AbsTime -> t m ()-timeout = undefined----------------------------------------------------------------------------------- Iterating functions----------------------------------------------------------------------------------- |--- >>> iterate f x = x `Stream.cons` iterate f x------ Generate an infinite stream with @x@ as the first element and each--- successive element derived by applying the function @f@ on the previous--- element.------ >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1--- [1,2,3,4,5]----{-# INLINE_NORMAL iterate #-}-iterate :: Monad m => (a -> a) -> a -> Stream m a-iterate step = fromStreamD . D.iterate step---- |--- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)------ Generate an infinite stream with the first element generated by the action--- @m@ and each successive element derived by applying the monadic function--- @f@ on the previous element.------ >>> :{--- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)---     & Stream.take 3---     & Stream.fold Fold.toList--- :}--- 0--- 1--- [0,1,2]----{-# INLINE iterateM #-}-iterateM :: Monad m => (a -> m a) -> m a -> Stream m a-iterateM step = fromStreamD . D.iterateM step---- | We can define cyclic structures using @let@:------ >>> let (a, b) = ([1, b], head a) in (a, b)--- ([1,1],1)------ The function @fix@ defined as:------ >>> fix f = let x = f x in x------ ensures that the argument of a function and its output refer to the same--- lazy value @x@ i.e.  the same location in memory.  Thus @x@ can be defined--- in terms of itself, creating structures with cyclic references.------ >>> f ~(a, b) = ([1, b], head a)--- >>> fix f--- ([1,1],1)------ 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic--- values.------ Using 'mfix' for streams we can construct a stream in which each element of--- the stream is defined in a cyclic fashion. The argument of the function--- being fixed represents the current element of the stream which is being--- returned by the stream monad. Thus, we can use the argument to construct--- itself.------ In the following example, the argument @action@ of the function @f@--- represents the tuple @(x,y)@ returned by it in a given iteration. We define--- the first element of the tuple in terms of the second.------ >>> import Streamly.Internal.Data.Stream as Stream--- >>> import System.IO.Unsafe (unsafeInterleaveIO)------ >>> :{--- main = Stream.fold (Fold.drainMapM print) $ Stream.mfix f---     where---     f action = unCrossStream $ do---         let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act---         x <- CrossStream (Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action])---         y <- CrossStream (Stream.fromList [4,5])---         return (x, y)--- :}------ Note: you cannot achieve this by just changing the order of the monad--- statements because that would change the order in which the stream elements--- are generated.------ Note that the function @f@ must be lazy in its argument, that's why we use--- 'unsafeInterleaveIO' on @action@ because IO monad is strict.------ /CPS/------ /Pre-release/-{-# INLINE mfix #-}-mfix :: Monad m => (m a -> Stream m a) -> Stream m a-mfix f = fromStreamK $ K.mfix (toStreamK . f)----------------------------------------------------------------------------------- Conversions----------------------------------------------------------------------------------- |--- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil------ Construct a stream from a 'Foldable' containing pure values:------ /CPS/----{-# INLINE fromFoldable #-}-fromFoldable :: Foldable f => f a -> Stream m a-fromFoldable = fromStreamK . K.fromFoldable----------------------------------------------------------------------------------- From pointers----------------------------------------------------------------------------------- | Keep reading 'Storable' elements from 'Ptr' onwards.------ /Unsafe:/ The caller is responsible for safe addressing.------ /Pre-release/-{-# INLINE fromPtr #-}-fromPtr :: (MonadIO m, Storable a) => Ptr a -> Stream m a-fromPtr = Stream.fromStreamD . D.fromPtr---- | Take @n@ 'Storable' elements starting from 'Ptr' onwards.------ >>> fromPtrN n = Stream.take n . Stream.fromPtr------ /Unsafe:/ The caller is responsible for safe addressing.------ /Pre-release/-{-# INLINE fromPtrN #-}-fromPtrN :: (MonadIO m, Storable a) => Int -> Ptr a -> Stream m a-fromPtrN n = Stream.fromStreamD . D.take n . D.fromPtr---- | Read bytes from an 'Addr#' until a 0 byte is encountered, the 0 byte is--- not included in the stream.------ >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr------ /Unsafe:/ The caller is responsible for safe addressing.------ Note that this is completely safe when reading from Haskell string--- literals because they are guaranteed to be NULL terminated:------ >>> Stream.fold Fold.toList $ Stream.fromByteStr# "\1\2\3\0"#--- [1,2,3]----{-# INLINE fromByteStr# #-}-fromByteStr# :: MonadIO m => Addr# -> Stream m Word8-fromByteStr# addr =-    Stream.fromStreamD $ D.takeWhile (/= 0) $ D.fromPtr $ Ptr addr---- | Construct a stream by reading an 'Unboxed' 'IORef' repeatedly.------ /Pre-release/----{-# INLINE fromUnboxedIORef #-}-fromUnboxedIORef :: (MonadIO m, Unbox a) => Unboxed.IORef a -> Stream m a-fromUnboxedIORef = fromStreamD . Unboxed.toStreamD+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Stream.Generate+-- Copyright   : (c) 2020 Composewell Technologies and Contributors+--               (c) Roman Leshchinskiy 2008-2010+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--++-- A few combinators in this module have been adapted from the vector package+-- (c) Roman Leshchinskiy. See the notes in specific combinators.+--+module Streamly.Internal.Data.Stream.Generate+  (+    -- * Primitives+      nil+    , nilM+    , cons+    , consM++    -- * From 'Unfold'+    , unfold++    -- * Unfolding+    , unfoldr+    , unfoldrM++    -- * From Values+    , fromPure+    , fromEffect+    , repeat+    , repeatM+    , replicate+    , replicateM++    -- * Enumeration+    -- ** Enumerating 'Num' Types+    , enumerateFromStepNum+    , enumerateFromNum+    , enumerateFromThenNum++    -- ** Enumerating 'Bounded' 'Enum' Types+    , enumerate+    , enumerateTo+    , enumerateFromBounded++    -- ** Enumerating 'Enum' Types not larger than 'Int'+    , enumerateFromToSmall+    , enumerateFromThenToSmall+    , enumerateFromThenSmallBounded++    -- ** Enumerating 'Bounded' 'Integral' Types+    , enumerateFromIntegral+    , enumerateFromThenIntegral++    -- ** Enumerating 'Integral' Types+    , enumerateFromToIntegral+    , enumerateFromThenToIntegral++    -- ** Enumerating unbounded 'Integral' Types+    , enumerateFromStepIntegral++    -- ** Enumerating 'Fractional' Types+    , enumerateFromFractional+    , enumerateFromToFractional+    , enumerateFromThenFractional+    , enumerateFromThenToFractional++    -- ** Enumerable Type Class+    , Enumerable(..)++    -- * Time Enumeration+    , times+    , timesWith+    , absTimes+    , absTimesWith+    , relTimes+    , relTimesWith+    , durations+    , timeout++    -- * From Generators+    -- | Generate a monadic stream from a seed.+    , fromIndices+    , fromIndicesM+    , generate+    , generateM++    -- * Iteration+    , iterate+    , iterateM++    -- * From Containers+    -- | Transform an input structure into a stream.++    , fromList+    , fromListM+    , fromFoldable+    , fromFoldableM++    -- * From Pointers+    , fromPtr+    , fromPtrN+    , fromByteStr#++    -- * Conversions+    , fromStreamK+    , toStreamK+    )+where++#include "inline.hs"+#include "ArrayMacros.h"++import Control.Monad.IO.Class (MonadIO(..))+import Data.Functor.Identity (Identity(..))+import Foreign.Ptr (Ptr, plusPtr)+import Foreign.Storable (Storable (peek), sizeOf)+import GHC.Exts (Addr#, Ptr (Ptr))+import Streamly.Internal.Data.Time.Clock+    (Clock(Monotonic), asyncClock, readClock)+import Streamly.Internal.Data.Time.Units+    (toAbsTime, AbsTime, toRelTime64, RelTime64, addToAbsTime64)+import Streamly.Internal.System.IO (unsafeInlineIO)++#ifdef USE_UNFOLDS_EVERYWHERE+import qualified Streamly.Internal.Data.Unfold as Unfold+#endif++import Data.Fixed+import Data.Int+import Data.Ratio+import Data.Word+import Numeric.Natural+import Prelude hiding (iterate, repeat, replicate, take, takeWhile)+import Streamly.Internal.Data.Stream.Type++#include "DocTestDataStream.hs"++------------------------------------------------------------------------------+-- Primitives+------------------------------------------------------------------------------++-- XXX implement in terms of nilM?++-- | A stream that terminates without producing any output or side effect.+--+-- >>> Stream.toList Stream.nil+-- []+--+{-# INLINE_NORMAL nil #-}+nil :: Applicative m => Stream m a+nil = Stream (\_ _ -> pure Stop) ()++-- XXX implement in terms of consM?+-- cons x = consM (return x)+-- From an implementation perspective, StreamK.cons translates into a+-- functional call whereas fused cons translates into a conditional branch+-- (jump). However, the overhead of the function call in StreamK.cons only+-- occurs once, while the overhead of the conditional branch in fused cons is+-- incurred for each subsequent element in the stream. As a result,+-- StreamK.cons has a time complexity of O(n), while fused cons has a time+-- complexity of O(n^2), where @n@ represents the number of 'cons' used.++-- When composing a few elements together statically, a balanced tree composed+-- using 'cons' and 'append' is more efficient than a right associated one+-- composed using 'cons':+--+-- >>> s1 = 1 `Stream.cons` 2 `Stream.cons` Stream.nil+-- >>> s2 = 2 `Stream.cons` 3 `Stream.cons` Stream.nil+-- >>> s = s1 `Stream.append` s2+--+-- However, generating a stream using a case statement or indexing into a+-- static literal array would be the best. Check if the case statement+-- translates to a look up table or a binary search.++-- | WARNING! O(n^2) time complexity wrt number of elements. Use the O(n)+-- complexity StreamK.'Streamly.Data.StreamK.cons' unless you want to+-- statically fuse just a few elements.+--+-- Fuse a pure value at the head of an existing stream::+--+-- >>> s = 1 `Stream.cons` Stream.fromList [2,3]+-- >>> Stream.toList s+-- [1,2,3]+--+-- Definition:+--+-- >>> cons x xs = return x `Stream.consM` xs+--+{-# INLINE_NORMAL cons #-}+cons :: Applicative m => a -> Stream m a -> Stream m a+cons x (Stream step state) = Stream step1 Nothing+    where+    {-# INLINE_LATE step1 #-}+    step1 _ Nothing = pure $ Yield x (Just state)+    step1 gst (Just st) = do+          (\case+            Yield a s -> Yield a (Just s)+            Skip  s   -> Skip (Just s)+            Stop      -> Stop) <$> step gst st++------------------------------------------------------------------------------+-- Unfolding+------------------------------------------------------------------------------++-- Adapted from vector package++-- | Build a stream by unfolding a /monadic/ step function starting from a+-- seed.  The step function returns the next element in the stream and the next+-- seed value. When it is done it returns 'Nothing' and the stream ends. For+-- example,+--+-- >>> :{+-- let f b =+--         if b > 2+--         then return Nothing+--         else return (Just (b, b + 1))+-- in Stream.toList $ Stream.unfoldrM f 0+-- :}+-- [0,1,2]+--+{-# INLINE_NORMAL unfoldrM #-}+unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+unfoldrM next = unfold (Unfold.unfoldrM next)+#else+unfoldrM next = Stream step+  where+    {-# INLINE_LATE step #-}+    step _ st = do+        r <- next st+        return $ case r of+            Just (x, s) -> Yield x s+            Nothing     -> Stop+#endif++-- | Build a stream by unfolding a /pure/ step function @step@ starting from a+-- seed @s@.  The step function returns the next element in the stream and the+-- next seed value. When it is done it returns 'Nothing' and the stream ends.+-- For example,+--+-- >>> :{+-- let f b =+--         if b > 2+--         then Nothing+--         else Just (b, b + 1)+-- in Stream.toList $ Stream.unfoldr f 0+-- :}+-- [0,1,2]+--+{-# INLINE_LATE unfoldr #-}+unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a+unfoldr f = unfoldrM (return . f)++------------------------------------------------------------------------------+-- From values+------------------------------------------------------------------------------++-- |+-- >>> repeatM = Stream.sequence . Stream.repeat+--+-- Generate a stream by repeatedly executing a monadic action forever.+--+-- >>> :{+-- repeatAction =+--        Stream.repeatM (threadDelay 1000000 >> print 1)+--      & Stream.take 10+--      & Stream.fold Fold.drain+-- :}+--+{-# INLINE_NORMAL repeatM #-}+repeatM :: Monad m => m a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+repeatM = unfold Unfold.repeatM+#else+repeatM x = Stream (\_ _ -> x >>= \r -> return $ Yield r ()) ()+#endif++-- |+-- Generate an infinite stream by repeating a pure value.+--+-- >>> repeat x = Stream.repeatM (pure x)+--+{-# INLINE_NORMAL repeat #-}+repeat :: Monad m => a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+repeat x = repeatM (pure x)+#else+repeat x = Stream (\_ _ -> return $ Yield x ()) ()+#endif++-- Adapted from the vector package++-- |+-- >>> replicateM n = Stream.sequence . Stream.replicate n+--+-- Generate a stream by performing a monadic action @n@ times.+{-# INLINE_NORMAL replicateM #-}+replicateM :: Monad m => Int -> m a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+replicateM n p = unfold Unfold.replicateM (n, p)+#else+replicateM n p = Stream step n+  where+    {-# INLINE_LATE step #-}+    step _ (i :: Int)+      | i <= 0    = return Stop+      | otherwise = do+          x <- p+          return $ Yield x (i - 1)+#endif++-- |+-- >>> replicate n = Stream.take n . Stream.repeat+-- >>> replicate n x = Stream.replicateM n (pure x)+--+-- Generate a stream of length @n@ by repeating a value @n@ times.+--+{-# INLINE_NORMAL replicate #-}+replicate :: Monad m => Int -> a -> Stream m a+replicate n x = replicateM n (return x)++------------------------------------------------------------------------------+-- Enumeration of Num+------------------------------------------------------------------------------++-- | For floating point numbers if the increment is less than the precision then+-- it just gets lost. Therefore we cannot always increment it correctly by just+-- repeated addition.+-- 9007199254740992 + 1 + 1 :: Double => 9.007199254740992e15+-- 9007199254740992 + 2     :: Double => 9.007199254740994e15+--+-- Instead we accumulate the increment counter and compute the increment+-- every time before adding it to the starting number.+--+-- This works for Integrals as well as floating point numbers, but+-- enumerateFromStepIntegral is faster for integrals.+{-# INLINE_NORMAL enumerateFromStepNum #-}+enumerateFromStepNum :: (Monad m, Num a) => a -> a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+enumerateFromStepNum from stride =+    unfold Unfold.enumerateFromStepNum (from, stride)+#else+enumerateFromStepNum from stride = Stream step 0+    where+    {-# INLINE_LATE step #-}+    step _ !i = return $ (Yield $! (from + i * stride)) $! (i + 1)+#endif++{-# INLINE_NORMAL enumerateFromNum #-}+enumerateFromNum :: (Monad m, Num a) => a -> Stream m a+enumerateFromNum from = enumerateFromStepNum from 1++{-# INLINE_NORMAL enumerateFromThenNum #-}+enumerateFromThenNum :: (Monad m, Num a) => a -> a -> Stream m a+enumerateFromThenNum from next = enumerateFromStepNum from (next - from)++------------------------------------------------------------------------------+-- Enumeration of Integrals+------------------------------------------------------------------------------++#ifndef USE_UNFOLDS_EVERYWHERE+data EnumState a = EnumInit | EnumYield a a a | EnumStop++{-# INLINE_NORMAL enumerateFromThenToIntegralUp #-}+enumerateFromThenToIntegralUp+    :: (Monad m, Integral a)+    => a -> a -> a -> Stream m a+enumerateFromThenToIntegralUp from next to = Stream step EnumInit+    where+    {-# INLINE_LATE step #-}+    step _ EnumInit =+        return $+            if to < next+            then if to < from+                 then Stop+                 else Yield from EnumStop+            else -- from <= next <= to+                let stride = next - from+                in Skip $ EnumYield from stride (to - stride)++    step _ (EnumYield x stride toMinus) =+        return $+            if x > toMinus+            then Yield x EnumStop+            else Yield x $ EnumYield (x + stride) stride toMinus++    step _ EnumStop = return Stop++{-# INLINE_NORMAL enumerateFromThenToIntegralDn #-}+enumerateFromThenToIntegralDn+    :: (Monad m, Integral a)+    => a -> a -> a -> Stream m a+enumerateFromThenToIntegralDn from next to = Stream step EnumInit+    where+    {-# INLINE_LATE step #-}+    step _ EnumInit =+        return $ if to > next+            then if to > from+                 then Stop+                 else Yield from EnumStop+            else -- from >= next >= to+                let stride = next - from+                in Skip $ EnumYield from stride (to - stride)++    step _ (EnumYield x stride toMinus) =+        return $+            if x < toMinus+            then Yield x EnumStop+            else Yield x $ EnumYield (x + stride) stride toMinus++    step _ EnumStop = return Stop+#endif++-- XXX This can perhaps be simplified and written in terms of+-- enumeratFromStepIntegral as we have done in unfolds.++-- | Enumerate an 'Integral' type in steps up to a given limit.+-- @enumerateFromThenToIntegral from then to@ generates a finite stream whose+-- first element is @from@, the second element is @then@ and the successive+-- elements are in increments of @then - from@ up to @to@.+--+-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 2 6+-- [0,2,4,6]+--+-- >>> Stream.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)+-- [0,-2,-4,-6]+--+{-# INLINE_NORMAL enumerateFromThenToIntegral #-}+enumerateFromThenToIntegral+    :: (Monad m, Integral a)+    => a -> a -> a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+enumerateFromThenToIntegral from next to =+    unfold Unfold.enumerateFromThenToIntegral (from, next, to)+#else+enumerateFromThenToIntegral from next to+    | next >= from = enumerateFromThenToIntegralUp from next to+    | otherwise    = enumerateFromThenToIntegralDn from next to+#endif++-- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from+-- then@ generates a stream whose first element is @from@, the second element+-- is @then@ and the successive elements are in increments of @then - from@.+-- The stream is bounded by the size of the 'Integral' type.+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2+-- [0,2,4,6]+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)+-- [0,-2,-4,-6]+--+{-# INLINE_NORMAL enumerateFromThenIntegral #-}+enumerateFromThenIntegral+    :: (Monad m, Integral a, Bounded a)+    => a -> a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+enumerateFromThenIntegral from next =+    unfold Unfold.enumerateFromThenIntegralBounded (from, next)+#else+enumerateFromThenIntegral from next =+    if next > from+    then enumerateFromThenToIntegralUp from next maxBound+    else enumerateFromThenToIntegralDn from next minBound+#endif++-- | @enumerateFromStepIntegral from step@ generates an infinite stream whose+-- first element is @from@ and the successive elements are in increments of+-- @step@.+--+-- CAUTION: This function is not safe for finite integral types. It does not+-- check for overflow, underflow or bounds.+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2+-- [0,2,4,6]+--+-- >>> Stream.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)+-- [0,-2,-4]+--+{-# INLINE_NORMAL enumerateFromStepIntegral #-}+enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+enumerateFromStepIntegral from stride =+    unfold Unfold.enumerateFromStepIntegral (from, stride)+#else+enumerateFromStepIntegral from stride =+    from `seq` stride `seq` Stream step from+    where+        {-# INLINE_LATE step #-}+        step _ !x = return $ Yield x $! (x + stride)+#endif++-- | Enumerate an 'Integral' type up to a given limit.+-- @enumerateFromToIntegral from to@ generates a finite stream whose first+-- element is @from@ and successive elements are in increments of @1@ up to+-- @to@.+--+-- >>> Stream.toList $ Stream.enumerateFromToIntegral 0 4+-- [0,1,2,3,4]+--+{-# INLINE enumerateFromToIntegral #-}+enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a+enumerateFromToIntegral from to =+    takeWhile (<= to) $ enumerateFromStepIntegral from 1++-- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a+-- stream whose first element is @from@ and the successive elements are in+-- increments of @1@. The stream is bounded by the size of the 'Integral' type.+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)+-- [0,1,2,3]+--+{-# INLINE enumerateFromIntegral #-}+enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a+enumerateFromIntegral from = enumerateFromToIntegral from maxBound++------------------------------------------------------------------------------+-- Enumeration of Fractionals+------------------------------------------------------------------------------++-- We cannot write a general function for Num.  The only way to write code+-- portable between the two is to use a 'Real' constraint and convert between+-- Fractional and Integral using fromRational which is horribly slow.++-- Even though the underlying implementation of enumerateFromFractional and+-- enumerateFromThenFractional works for any 'Num' we have restricted these to+-- 'Fractional' because these do not perform any bounds check, in contrast to+-- integral versions and are therefore not equivalent substitutes for those.++-- | Numerically stable enumeration from a 'Fractional' number in steps of size+-- @1@. @enumerateFromFractional from@ generates a stream whose first element+-- is @from@ and the successive elements are in increments of @1@.  No overflow+-- or underflow checks are performed.+--+-- This is the equivalent to 'enumFrom' for 'Fractional' types. For example:+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1+-- [1.1,2.1,3.1,4.1]+--+{-# INLINE enumerateFromFractional #-}+enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a+enumerateFromFractional = enumerateFromNum++-- | Numerically stable enumeration from a 'Fractional' number in steps.+-- @enumerateFromThenFractional from then@ generates a stream whose first+-- element is @from@, the second element is @then@ and the successive elements+-- are in increments of @then - from@.  No overflow or underflow checks are+-- performed.+--+-- This is the equivalent of 'enumFromThen' for 'Fractional' types. For+-- example:+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1+-- [1.1,2.1,3.1,4.1]+--+-- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)+-- [1.1,-2.1,-5.300000000000001,-8.500000000000002]+--+{-# INLINE enumerateFromThenFractional #-}+enumerateFromThenFractional+    :: (Monad m, Fractional a)+    => a -> a -> Stream m a+enumerateFromThenFractional = enumerateFromThenNum++-- | Numerically stable enumeration from a 'Fractional' number to a given+-- limit.  @enumerateFromToFractional from to@ generates a finite stream whose+-- first element is @from@ and successive elements are in increments of @1@ up+-- to @to@.+--+-- This is the equivalent of 'enumFromTo' for 'Fractional' types. For+-- example:+--+-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4+-- [1.1,2.1,3.1,4.1]+--+-- >>> Stream.toList $ Stream.enumerateFromToFractional 1.1 4.6+-- [1.1,2.1,3.1,4.1,5.1]+--+-- Notice that the last element is equal to the specified @to@ value after+-- rounding to the nearest integer.+--+{-# INLINE_NORMAL enumerateFromToFractional #-}+enumerateFromToFractional+    :: (Monad m, Fractional a, Ord a)+    => a -> a -> Stream m a+enumerateFromToFractional from to =+    takeWhile (<= to + 1 / 2) $ enumerateFromStepNum from 1++-- | Numerically stable enumeration from a 'Fractional' number in steps up to a+-- given limit.  @enumerateFromThenToFractional from then to@ generates a+-- finite stream whose first element is @from@, the second element is @then@+-- and the successive elements are in increments of @then - from@ up to @to@.+--+-- This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For+-- example:+--+-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 2 6+-- [0.1,2.0,3.9,5.799999999999999]+--+-- >>> Stream.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)+-- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]+--+{-# INLINE_NORMAL enumerateFromThenToFractional #-}+enumerateFromThenToFractional+    :: (Monad m, Fractional a, Ord a)+    => a -> a -> a -> Stream m a+enumerateFromThenToFractional from next to =+    takeWhile predicate $ enumerateFromThenFractional from next+    where+    mid = (next - from) / 2+    predicate | next >= from  = (<= to + mid)+              | otherwise     = (>= to + mid)++-------------------------------------------------------------------------------+-- Enumeration of Enum types not larger than Int+-------------------------------------------------------------------------------+--+-- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.+--+{-# INLINE enumerateFromToSmall #-}+enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a+enumerateFromToSmall from to =+      fmap toEnum+    $ enumerateFromToIntegral (fromEnum from) (fromEnum to)++-- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.+--+{-# INLINE enumerateFromThenToSmall #-}+enumerateFromThenToSmall :: (Monad m, Enum a)+    => a -> a -> a -> Stream m a+enumerateFromThenToSmall from next to =+          fmap toEnum+        $ enumerateFromThenToIntegral+            (fromEnum from) (fromEnum next) (fromEnum to)++-- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.+--+-- Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a+-- type is bounded but does not have a 'Bounded' instance then we can go on+-- enumerating it beyond the legal values of the type, resulting in the failure+-- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'+-- instance for this function to be safely used.+--+{-# INLINE enumerateFromThenSmallBounded #-}+enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)+    => a -> a -> Stream m a+enumerateFromThenSmallBounded from next =+    if fromEnum next >= fromEnum from+    then enumerateFromThenTo from next maxBound+    else enumerateFromThenTo from next minBound++-------------------------------------------------------------------------------+-- Enumerable type class+-------------------------------------------------------------------------------+--+-- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream+-- enumerations like this:+--+-- {-# RULES "fromList enumFrom" [1]+--     forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}+--+-- But this does not work because enumFrom is a class method and GHC rewrites+-- it quickly, so we do not get a chance to have our rule fired.++-- | Types that can be enumerated as a stream. The operations in this type+-- class are equivalent to those in the 'Enum' type class, except that these+-- generate a stream instead of a list. Use the functions in+-- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.+--+class Enum a => Enumerable a where+    -- | @enumerateFrom from@ generates a stream starting with the element+    -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or+    -- generating an infinite stream when the type is not 'Bounded'.+    --+    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)+    -- [0,1,2,3]+    --+    -- For 'Fractional' types, enumeration is numerically stable. However, no+    -- overflow or underflow checks are performed.+    --+    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1+    -- [1.1,2.1,3.1,4.1]+    --+    enumerateFrom :: (Monad m) => a -> Stream m a++    -- | Generate a finite stream starting with the element @from@, enumerating+    -- the type up to the value @to@. If @to@ is smaller than @from@ then an+    -- empty stream is returned.+    --+    -- >>> Stream.toList $ Stream.enumerateFromTo 0 4+    -- [0,1,2,3,4]+    --+    -- For 'Fractional' types, the last element is equal to the specified @to@+    -- value after rounding to the nearest integral value.+    --+    -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4+    -- [1.1,2.1,3.1,4.1]+    --+    -- >>> Stream.toList $ Stream.enumerateFromTo 1.1 4.6+    -- [1.1,2.1,3.1,4.1,5.1]+    --+    enumerateFromTo :: (Monad m) => a -> a -> Stream m a++    -- | @enumerateFromThen from then@ generates a stream whose first element+    -- is @from@, the second element is @then@ and the successive elements are+    -- in increments of @then - from@.  Enumeration can occur downwards or+    -- upwards depending on whether @then@ comes before or after @from@. For+    -- 'Bounded' types the stream ends when 'maxBound' is reached, for+    -- unbounded types it keeps enumerating infinitely.+    --+    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2+    -- [0,2,4,6]+    --+    -- >>> Stream.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)+    -- [0,-2,-4,-6]+    --+    enumerateFromThen :: (Monad m) => a -> a -> Stream m a++    -- | @enumerateFromThenTo from then to@ generates a finite stream whose+    -- first element is @from@, the second element is @then@ and the successive+    -- elements are in increments of @then - from@ up to @to@. Enumeration can+    -- occur downwards or upwards depending on whether @then@ comes before or+    -- after @from@.+    --+    -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 2 6+    -- [0,2,4,6]+    --+    -- >>> Stream.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)+    -- [0,-2,-4,-6]+    --+    enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a++-- MAYBE: Sometimes it is more convenient to know the count rather then the+-- ending or starting element. For those cases we can define the folllowing+-- APIs. All of these will work only for bounded types if we represent the+-- count by Int.+--+-- enumerateN+-- enumerateFromN+-- enumerateToN+-- enumerateFromStep+-- enumerateFromStepN++-------------------------------------------------------------------------------+-- Convenient functions for bounded types+-------------------------------------------------------------------------------+--+-- |+-- > enumerate = enumerateFrom minBound+--+-- Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'+--+{-# INLINE enumerate #-}+enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a+enumerate = enumerateFrom minBound++-- |+-- >>> enumerateTo = Stream.enumerateFromTo minBound+--+-- Enumerate a 'Bounded' type from its 'minBound' to specified value.+--+{-# INLINE enumerateTo #-}+enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a+enumerateTo = enumerateFromTo minBound++-- |+-- >>> enumerateFromBounded from = Stream.enumerateFromTo from maxBound+--+-- 'enumerateFrom' for 'Bounded' 'Enum' types.+--+{-# INLINE enumerateFromBounded #-}+enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)+    => a -> Stream m a+enumerateFromBounded from = enumerateFromTo from maxBound++-------------------------------------------------------------------------------+-- Enumerable Instances+-------------------------------------------------------------------------------+--+-- For Enum types smaller than or equal to Int size.+#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE)           \+instance Enumerable SMALL_TYPE where {                 \+    {-# INLINE enumerateFrom #-};                      \+    enumerateFrom = enumerateFromBounded;              \+    {-# INLINE enumerateFromThen #-};                  \+    enumerateFromThen = enumerateFromThenSmallBounded; \+    {-# INLINE enumerateFromTo #-};                    \+    enumerateFromTo = enumerateFromToSmall;            \+    {-# INLINE enumerateFromThenTo #-};                \+    enumerateFromThenTo = enumerateFromThenToSmall }++ENUMERABLE_BOUNDED_SMALL(())+ENUMERABLE_BOUNDED_SMALL(Bool)+ENUMERABLE_BOUNDED_SMALL(Ordering)+ENUMERABLE_BOUNDED_SMALL(Char)++-- For bounded Integral Enum types, may be larger than Int.+#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE)  \+instance Enumerable INTEGRAL_TYPE where {           \+    {-# INLINE enumerateFrom #-};                   \+    enumerateFrom = enumerateFromIntegral;          \+    {-# INLINE enumerateFromThen #-};               \+    enumerateFromThen = enumerateFromThenIntegral;  \+    {-# INLINE enumerateFromTo #-};                 \+    enumerateFromTo = enumerateFromToIntegral;      \+    {-# INLINE enumerateFromThenTo #-};             \+    enumerateFromThenTo = enumerateFromThenToIntegral }++ENUMERABLE_BOUNDED_INTEGRAL(Int)+ENUMERABLE_BOUNDED_INTEGRAL(Int8)+ENUMERABLE_BOUNDED_INTEGRAL(Int16)+ENUMERABLE_BOUNDED_INTEGRAL(Int32)+ENUMERABLE_BOUNDED_INTEGRAL(Int64)+ENUMERABLE_BOUNDED_INTEGRAL(Word)+ENUMERABLE_BOUNDED_INTEGRAL(Word8)+ENUMERABLE_BOUNDED_INTEGRAL(Word16)+ENUMERABLE_BOUNDED_INTEGRAL(Word32)+ENUMERABLE_BOUNDED_INTEGRAL(Word64)++-- For unbounded Integral Enum types.+#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE)              \+instance Enumerable INTEGRAL_TYPE where {                         \+    {-# INLINE enumerateFrom #-};                                 \+    enumerateFrom from = enumerateFromStepIntegral from 1;        \+    {-# INLINE enumerateFromThen #-};                             \+    enumerateFromThen from next =                                 \+        enumerateFromStepIntegral from (next - from);             \+    {-# INLINE enumerateFromTo #-};                               \+    enumerateFromTo = enumerateFromToIntegral;                    \+    {-# INLINE enumerateFromThenTo #-};                           \+    enumerateFromThenTo = enumerateFromThenToIntegral }++ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)+ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)++#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT)         \+instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where {     \+    {-# INLINE enumerateFrom #-};                                 \+    enumerateFrom = enumerateFromFractional;                      \+    {-# INLINE enumerateFromThen #-};                             \+    enumerateFromThen = enumerateFromThenFractional;              \+    {-# INLINE enumerateFromTo #-};                               \+    enumerateFromTo = enumerateFromToFractional;                  \+    {-# INLINE enumerateFromThenTo #-};                           \+    enumerateFromThenTo = enumerateFromThenToFractional }++ENUMERABLE_FRACTIONAL(Float,)+ENUMERABLE_FRACTIONAL(Double,)+ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)+ENUMERABLE_FRACTIONAL((Ratio a),Integral a)++instance Enumerable a => Enumerable (Identity a) where+    {-# INLINE enumerateFrom #-}+    enumerateFrom (Identity from) =+        fmap Identity $ enumerateFrom from+    {-# INLINE enumerateFromThen #-}+    enumerateFromThen (Identity from) (Identity next) =+        fmap Identity $ enumerateFromThen from next+    {-# INLINE enumerateFromTo #-}+    enumerateFromTo (Identity from) (Identity to) =+        fmap Identity $ enumerateFromTo from to+    {-# INLINE enumerateFromThenTo #-}+    enumerateFromThenTo (Identity from) (Identity next) (Identity to) =+          fmap Identity+        $ enumerateFromThenTo from next to++-- TODO+{-+instance Enumerable a => Enumerable (Last a)+instance Enumerable a => Enumerable (First a)+instance Enumerable a => Enumerable (Max a)+instance Enumerable a => Enumerable (Min a)+instance Enumerable a => Enumerable (Const a b)+instance Enumerable (f a) => Enumerable (Alt f a)+instance Enumerable (f a) => Enumerable (Ap f a)+-}+------------------------------------------------------------------------------+-- Time Enumeration+------------------------------------------------------------------------------++-- | @timesWith g@ returns a stream of time value tuples. The first component+-- of the tuple is an absolute time reference (epoch) denoting the start of the+-- stream and the second component is a time relative to the reference.+--+-- The argument @g@ specifies the granularity of the relative time in seconds.+-- A lower granularity clock gives higher precision but is more expensive in+-- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.+--+-- >>> import Control.Concurrent (threadDelay)+-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)+-- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE_NORMAL timesWith #-}+timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)+timesWith g = Stream step Nothing++    where++    {-# INLINE_LATE step #-}+    step _ Nothing = do+        clock <- liftIO $ asyncClock Monotonic g+        a <- liftIO $ readClock clock+        return $ Skip $ Just (clock, a)++    step _ s@(Just (clock, t0)) = do+        a <- liftIO $ readClock clock+        -- XXX we can perhaps use an AbsTime64 using a 64 bit Int for+        -- efficiency.  or maybe we can use a representation using Double for+        -- floating precision time+        return $ Yield (toAbsTime t0, toRelTime64 (a - t0)) s++-- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of+-- granularity @g@ specified in seconds. A low granularity clock is more+-- expensive in terms of CPU usage.  Any granularity lower than 1 ms is treated+-- as 1 ms.+--+-- >>> f = Fold.drainMapM print+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE absTimesWith #-}+absTimesWith :: MonadIO m => Double -> Stream m AbsTime+absTimesWith = fmap (uncurry addToAbsTime64) . timesWith++-- | @relTimesWith g@ returns a stream of relative time values starting from 0,+-- using a clock of granularity @g@ specified in seconds. A low granularity+-- clock is more expensive in terms of CPU usage.  Any granularity lower than 1+-- ms is treated as 1 ms.+--+-- >>> f = Fold.drainMapM print+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01+-- RelTime64 (NanoSecond64 ...)+-- RelTime64 (NanoSecond64 ...)+-- RelTime64 (NanoSecond64 ...)+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE relTimesWith #-}+relTimesWith :: MonadIO m => Double -> Stream m RelTime64+relTimesWith = fmap snd . timesWith++-- | @times@ returns a stream of time value tuples with clock of 10 ms+-- granularity. The first component of the tuple is an absolute time reference+-- (epoch) denoting the start of the stream and the second component is a time+-- relative to the reference.+--+-- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)+-- >>> Stream.fold f $ Stream.take 3 $ Stream.times+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+-- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE times #-}+times :: MonadIO m => Stream m (AbsTime, RelTime64)+times = timesWith 0.01++-- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms+-- granularity.+--+-- >>> f = Fold.drainMapM print+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+-- AbsTime (TimeSpec {sec = ..., nsec = ...})+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE absTimes #-}+absTimes :: MonadIO m => Stream m AbsTime+absTimes = fmap (uncurry addToAbsTime64) times++-- | @relTimes@ returns a stream of relative time values starting from 0,+-- using a clock of granularity 10 ms.+--+-- >>> f = Fold.drainMapM print+-- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes+-- RelTime64 (NanoSecond64 ...)+-- RelTime64 (NanoSecond64 ...)+-- RelTime64 (NanoSecond64 ...)+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Pre-release/+--+{-# INLINE relTimes #-}+relTimes ::  MonadIO m => Stream m RelTime64+relTimes = fmap snd times++-- | @durations g@ returns a stream of relative time values measuring the time+-- elapsed since the immediate predecessor element of the stream was generated.+-- The first element of the stream is always 0. @durations@ uses a clock of+-- granularity @g@ specified in seconds. A low granularity clock is more+-- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.+-- Durations lower than 1 ms will be 0.+--+-- Note: This API is not safe on 32-bit machines.+--+-- /Unimplemented/+--+{-# INLINE durations #-}+durations :: -- Monad m =>+    Double -> t m RelTime64+durations = undefined++-- | Generate a singleton event at or after the specified absolute time. Note+-- that this is different from a threadDelay, a threadDelay starts from the+-- time when the action is evaluated, whereas if we use AbsTime based timeout+-- it will immediately expire if the action is evaluated too late.+--+-- /Unimplemented/+--+{-# INLINE timeout #-}+timeout :: -- Monad m =>+    AbsTime -> t m ()+timeout = undefined++-------------------------------------------------------------------------------+-- From Generators+-------------------------------------------------------------------------------++{-# INLINE_NORMAL fromIndicesM #-}+fromIndicesM :: Monad m => (Int -> m a) -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+fromIndicesM gen = unfold (Unfold.fromIndicesM gen) 0+#else+fromIndicesM gen = Stream step 0+  where+    {-# INLINE_LATE step #-}+    step _ i = do+       x <- gen i+       return $ Yield x (i + 1)+#endif++{-# INLINE fromIndices #-}+fromIndices :: Monad m => (Int -> a) -> Stream m a+fromIndices gen = fromIndicesM (return . gen)++-- Adapted from the vector package+{-# INLINE_NORMAL generateM #-}+generateM :: Monad m => Int -> (Int -> m a) -> Stream m a+generateM n gen = n `seq` Stream step 0+  where+    {-# INLINE_LATE step #-}+    step _ i | i < n     = do+                           x <- gen i+                           return $ Yield x (i + 1)+             | otherwise = return Stop++{-# INLINE generate #-}+generate :: Monad m => Int -> (Int -> a) -> Stream m a+generate n gen = generateM n (return . gen)++-------------------------------------------------------------------------------+-- Iteration+-------------------------------------------------------------------------------++-- | Generate an infinite stream with the first element generated by the action+-- @m@ and each successive element derived by applying the monadic function @f@+-- on the previous element.+--+-- >>> :{+-- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)+--     & Stream.take 3+--     & Stream.toList+-- :}+-- 0+-- 1+-- [0,1,2]+--+{-# INLINE_NORMAL iterateM #-}+iterateM :: Monad m => (a -> m a) -> m a -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+iterateM step = unfold (Unfold.iterateM step)+#else+iterateM step = Stream (\_ st -> st >>= \(!x) -> return $ Yield x (step x))+#endif++-- | Generate an infinite stream with @x@ as the first element and each+-- successive element derived by applying the function @f@ on the previous+-- element.+--+-- >>> Stream.toList $ Stream.take 5 $ Stream.iterate (+1) 1+-- [1,2,3,4,5]+--+{-# INLINE_NORMAL iterate #-}+iterate :: Monad m => (a -> a) -> a -> Stream m a+iterate step st = iterateM (return . step) (return st)++-------------------------------------------------------------------------------+-- From containers+-------------------------------------------------------------------------------++-- | Convert a list of monadic actions to a 'Stream'+{-# INLINE_LATE fromListM #-}+fromListM :: Monad m => [m a] -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+fromListM = unfold Unfold.fromListM+#else+fromListM = Stream step+  where+    {-# INLINE_LATE step #-}+    step _ (m:ms) = m >>= \x -> return $ Yield x ms+    step _ []     = return Stop+#endif++-- |+-- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil+--+-- Construct a stream from a 'Foldable' containing pure values:+--+-- /WARNING: O(n^2), suitable only for a small number of+-- elements in the stream/+--+{-# INLINE fromFoldable #-}+fromFoldable :: (Monad m, Foldable f) => f a -> Stream m a+fromFoldable = Prelude.foldr cons nil++-- |+-- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil+--+-- Construct a stream from a 'Foldable' containing pure values:+--+-- /WARNING: O(n^2), suitable only for a small number of+-- elements in the stream/+--+{-# INLINE fromFoldableM #-}+fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a+fromFoldableM = Prelude.foldr consM nil++-------------------------------------------------------------------------------+-- From pointers+-------------------------------------------------------------------------------++-- | Keep reading 'Storable' elements from an immutable 'Ptr' onwards.+--+-- /Unsafe:/ The caller is responsible for safe addressing.+--+-- /Pre-release/+{-# INLINE fromPtr #-}+fromPtr :: forall m a. (Monad m, Storable a) => Ptr a -> Stream m a+fromPtr = Stream step++    where++    {-# INLINE_LATE step #-}+    step _ p = do+        let !x = unsafeInlineIO $ peek p+        return $ Yield x (PTR_NEXT(p, a))++-- | Take @n@ 'Storable' elements starting from an immutable 'Ptr' onwards.+--+-- >>> fromPtrN n = Stream.take n . Stream.fromPtr+--+-- /Unsafe:/ The caller is responsible for safe addressing.+--+-- /Pre-release/+{-# INLINE fromPtrN #-}+fromPtrN :: (Monad m, Storable a) => Int -> Ptr a -> Stream m a+fromPtrN n = take n . fromPtr++-- | Read bytes from an immutable 'Addr#' until a 0 byte is encountered, the 0+-- byte is not included in the stream.+--+-- >>> :set -XMagicHash+-- >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr+--+-- /Unsafe:/ The caller is responsible for safe addressing.+--+-- Note that this is completely safe when reading from Haskell string+-- literals because they are guaranteed to be NULL terminated:+--+-- >>> Stream.toList $ Stream.fromByteStr# "\1\2\3\0"#+-- [1,2,3]+--+{-# INLINE fromByteStr# #-}+fromByteStr# :: Monad m => Addr# -> Stream m Word8+fromByteStr# addr = takeWhile (/= 0) $ fromPtr $ Ptr addr
src/Streamly/Internal/Data/Stream/Lift.hs view
@@ -1,10 +1,13 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Data.Stream.Lift--- Copyright   : (c) 2019 Composewell Technologies+-- Copyright   : (c) 2018 Composewell Technologies -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC+--+-- Transform the underlying monad of a stream.  module Streamly.Internal.Data.Stream.Lift     (@@ -19,22 +22,19 @@     ) where -import Data.Functor.Identity (Identity (..))-import Streamly.Internal.Data.Stream.Type-    (Stream, fromStreamD, toStreamD, fromStreamK, toStreamK)+#include "inline.hs" -import qualified Streamly.Internal.Data.Stream.StreamD as D-import qualified Streamly.Internal.Data.Stream.StreamK as K+import Data.Functor.Identity (Identity(..))+import Streamly.Internal.Data.SVar.Type (adaptState) --- $setup--- >>> :m--- >>> import Data.Functor.Identity (runIdentity)--- >>> import Streamly.Internal.Data.Stream as Stream+import Streamly.Internal.Data.Stream.Type ---------------------------------------------------------------------------------- Generalize the underlying monad-------------------------------------------------------------------------------+#include "DocTestDataStream.hs" +-------------------------------------------------------------------------------+-- Generalize Inner Monad+-------------------------------------------------------------------------------+ -- | Transform the inner monad of a stream using a natural transformation. -- -- Example, generalize the inner monad from Identity to any other:@@ -43,11 +43,17 @@ -- -- Also known as hoist. ----- /CPS/-{-# INLINE morphInner #-}-morphInner :: (Monad m, Monad n)-    => (forall x. m x -> n x) -> Stream m a -> Stream n a-morphInner f xs = fromStreamK $ K.hoist f (toStreamK xs)+{-# INLINE_NORMAL morphInner #-}+morphInner :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a+morphInner f (Stream step state) = Stream step' state+    where+    {-# INLINE_LATE step' #-}+    step' gst st = do+        r <- f $ step (adaptState gst) st+        return $ case r of+            Yield x s -> Yield x s+            Skip  s   -> Skip s+            Stop      -> Stop  -- | Generalize the inner monad of the stream from 'Identity' to any monad. --@@ -55,41 +61,69 @@ -- -- >>> generalizeInner = Stream.morphInner (return . runIdentity) ----- /CPS/--- {-# INLINE generalizeInner #-} generalizeInner :: Monad m => Stream Identity a -> Stream m a generalizeInner = morphInner (return . runIdentity)-    -- fromStreamK $ K.hoist (return . runIdentity) (toStreamK xs) ---------------------------------------------------------------------------------- Add and remove a monad transformer-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-- Transform Inner Monad+-------------------------------------------------------------------------------  -- | Lift the inner monad @m@ of a stream @Stream m a@ to @t m@ using the -- supplied lift function. ---{-# INLINE liftInnerWith #-}-liftInnerWith :: (Monad m, Monad (t m))-    => (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a-liftInnerWith lift xs = fromStreamD $ D.liftInnerWith lift (toStreamD xs)+{-# INLINE_NORMAL liftInnerWith #-}+liftInnerWith :: (Monad (t m)) =>+    (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a+liftInnerWith lift (Stream step state) = Stream step1 state +    where++    {-# INLINE_LATE step1 #-}+    step1 gst st = do+        r <- lift $ step (adaptState gst) st+        return $ case r of+            Yield x s -> Yield x s+            Skip s    -> Skip s+            Stop      -> Stop+ -- | Evaluate the inner monad of a stream using the supplied runner function. ---{-# INLINE runInnerWith #-}-runInnerWith :: (Monad m, Applicative (t m)) =>+{-# INLINE_NORMAL runInnerWith #-}+runInnerWith :: Monad m =>     (forall b. t m b -> m b) -> Stream (t m) a -> Stream m a-runInnerWith run xs = fromStreamD $ D.runInnerWith run (toStreamD xs)+runInnerWith run (Stream step state) = Stream step1 state +    where++    {-# INLINE_LATE step1 #-}+    step1 gst st = do+        r <- run $ step (adaptState gst) st+        return $ case r of+            Yield x s -> Yield x s+            Skip s -> Skip s+            Stop -> Stop+ -- | Evaluate the inner monad of a stream using the supplied stateful runner -- function and the initial state. The state returned by an invocation of the -- runner is supplied as input state to the next invocation. ---{-# INLINE runInnerWithState #-}-runInnerWithState :: (Monad m, Applicative (t m)) =>-       (forall b. s -> t m b -> m (b, s))+{-# INLINE_NORMAL runInnerWithState #-}+runInnerWithState :: Monad m =>+    (forall b. s -> t m b -> m (b, s))     -> m s     -> Stream (t m) a     -> Stream m (s, a)-runInnerWithState run initial xs =-    fromStreamD $ D.runInnerWithState run initial (toStreamD xs)+runInnerWithState run initial (Stream step state) =+    Stream step1 (state, initial)++    where++    {-# INLINE_LATE step1 #-}+    step1 gst (st, action) = do+        sv <- action+        (r, !sv1) <- run sv (step (adaptState gst) st)+        return $ case r of+            Yield x s -> Yield (sv1, x) (s, return sv1)+            Skip s -> Skip (s, return sv1)+            Stop -> Stop
+ src/Streamly/Internal/Data/Stream/Nesting.hs view
@@ -0,0 +1,3151 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Stream.Nesting+-- Copyright   : (c) 2018 Composewell Technologies+--               (c) Roman Leshchinskiy 2008-2010+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- This module contains transformations involving multiple streams, unfolds or+-- folds. There are two types of transformations generational or eliminational.+-- Generational transformations are like the "Generate" module but they+-- generate a stream by combining streams instead of elements. Eliminational+-- transformations are like the "Eliminate" module but they transform a stream+-- by eliminating parts of the stream instead of eliminating the whole stream.+--+-- These combinators involve transformation, generation, elimination so can be+-- classified under any of those.+--+-- Ultimately these operations should be supported by Unfolds, Pipes and Folds,+-- and this module may become redundant.++-- The zipWithM combinator in this module has been adapted from the vector+-- package (c) Roman Leshchinskiy.+--+module Streamly.Internal.Data.Stream.Nesting+    (+    -- * Generate+    -- | Combining streams to generate streams.++    -- ** Combine Two Streams+    -- | Functions ending in the shape:+    --+    -- @t m a -> t m a -> t m a@.++    -- *** Appending+    -- | Append a stream after another. A special case of concatMap or+    -- unfoldMany.+      AppendState(..)+    , append++    -- *** Interleaving+    -- | Interleave elements from two streams alternately. A special case of+    -- unfoldInterleave.+    , InterleaveState(..)+    , interleave+    , interleaveMin+    , interleaveFst+    , interleaveFstSuffix++    -- *** Scheduling+    -- | Execute streams alternately irrespective of whether they generate+    -- elements or not. Note 'interleave' would execute a stream until it+    -- yields an element. A special case of unfoldRoundRobin.+    , roundRobin -- interleaveFair?/ParallelFair++    -- *** Zipping+    -- | Zip corresponding elements of two streams.+    , zipWith+    , zipWithM++    -- *** Merging+    -- | Interleave elements from two streams based on a condition.+    , mergeBy+    , mergeByM+    , mergeMinBy+    , mergeFstBy++    -- ** Combine N Streams+    -- | Functions generally ending in these shapes:+    --+    -- @+    -- concat: f (t m a) -> t m a+    -- concatMap: (a -> t m b) -> t m a -> t m b+    -- unfoldMany: Unfold m a b -> t m a -> t m b+    -- @++    -- *** ConcatMap+    -- | Generate streams by mapping a stream generator on each element of an+    -- input stream, append the resulting streams and flatten.+    , concatMap+    , concatMapM++    -- *** ConcatUnfold+    -- | Generate streams by using an unfold on each element of an input+    -- stream, append the resulting streams and flatten. A special case of+    -- gintercalate.+    , unfoldMany+    , ConcatUnfoldInterleaveState (..)+    , unfoldInterleave+    , unfoldRoundRobin++    -- *** Interpose+    -- | Like unfoldMany but intersperses an effect between the streams. A+    -- special case of gintercalate.+    , interpose+    , interposeM+    , interposeSuffix+    , interposeSuffixM++    -- *** Intercalate+    -- | Like unfoldMany but intersperses streams from another source between+    -- the streams from the first source.+    , gintercalate+    , gintercalateSuffix+    , intercalate+    , intercalateSuffix++    -- * Eliminate+    -- | Folding and Parsing chunks of streams to eliminate nested streams.+    -- Functions generally ending in these shapes:+    --+    -- @+    -- f (Fold m a b) -> t m a -> t m b+    -- f (Parser a m b) -> t m a -> t m b+    -- @++    -- ** Folding+    -- | Apply folds on a stream.+    , foldMany+    , refoldMany+    , foldSequence+    , foldIterateM+    , refoldIterateM++    -- ** Parsing+    -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or+    -- unfoldMany. concatMap generates a stream from single values in a+    -- stream and flattens, parseMany does the opposite of flattening by+    -- splitting the stream and then folds each such split to single value in+    -- the output stream.+    , parseMany+    , parseManyD+    , parseSequence+    , parseManyTill+    , parseIterate+    , parseIterateD++    -- ** Grouping+    -- | Group segments of a stream and fold. Special case of parsing.+    , groupsOf+    , groupsBy+    , groupsWhile+    , groupsRollingBy++    -- ** Splitting+    -- | A special case of parsing.+    , wordsBy+    , splitOnSeq+    , splitOnSuffixSeq+    , sliceOnSuffix++    -- XXX Implement these as folds or parsers instead.+    , splitOnSuffixSeqAny+    , splitOnPrefix+    , splitOnAny++    -- * Transform (Nested Containers)+    -- | Opposite to compact in ArrayStream+    , splitInnerBy+    , splitInnerBySuffix+    , intersectBySorted++    -- * Reduce By Streams+    , dropPrefix+    , dropInfix+    , dropSuffix+    )+where++#include "inline.hs"+#include "ArrayMacros.h"++import Control.Exception (assert)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Bits (shiftR, shiftL, (.|.), (.&.))+import Data.Proxy (Proxy(..))+import Data.Word (Word32)+import Foreign.Storable (Storable, peek)+import Fusion.Plugin.Types (Fuse(..))+import GHC.Types (SPEC(..))++import Streamly.Internal.Data.Array.Type (Array(..))+import Streamly.Internal.Data.Fold.Step (Step(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Parser (ParseError(..))+import Streamly.Internal.Data.Refold.Type (Refold(..))+import Streamly.Internal.Data.SVar.Type (adaptState)+import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))+import Streamly.Internal.Data.Unbox (Unbox, sizeOf)+import Streamly.Internal.Data.Unfold.Type (Unfold(..))++import qualified Streamly.Internal.Data.Array.Type as A+import qualified Streamly.Internal.Data.Fold as FL+import qualified Streamly.Internal.Data.Parser as PR+import qualified Streamly.Internal.Data.Parser as PRD+import qualified Streamly.Internal.Data.Ring as RB++import Streamly.Internal.Data.Stream.Transform+    (intersperse, intersperseMSuffix)+import Streamly.Internal.Data.Stream.Type++import Prelude hiding (concatMap, mapM, zipWith)++#include "DocTestDataStream.hs"++------------------------------------------------------------------------------+-- Appending+------------------------------------------------------------------------------++data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2++-- From an implementation perspective, StreamK.'Streamly.Data.StreamK.append'+-- translates into a function call whereas Stream.'append' translates into a+-- conditional branch (jump). However, the overhead of the function call in+-- StreamK.append is incurred only once, while the overhead of the conditional+-- branch in fused append is incurred for each element in the stream. As a+-- result, StreamK.append has a linear time complexity of O(n), while fused+-- append has a quadratic time complexity of O(n^2), where @n@ represents the+-- number of 'append's used.++-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for+-- statically fusing a small number of streams. Use the O(n) complexity+-- StreamK.'Streamly.Data.StreamK.append' otherwise.+--+-- Fuses two streams sequentially, yielding all elements from the first+-- stream, and then all elements from the second stream.+--+-- >>> s1 = Stream.fromList [1,2]+-- >>> s2 = Stream.fromList [3,4]+-- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2+-- [1,2,3,4]+--+{-# INLINE_NORMAL append #-}+append :: Monad m => Stream m a -> Stream m a -> Stream m a+append (Stream step1 state1) (Stream step2 state2) =+    Stream step (AppendFirst state1)++    where++    {-# INLINE_LATE step #-}+    step gst (AppendFirst st) = do+        r <- step1 gst st+        return $ case r of+            Yield a s -> Yield a (AppendFirst s)+            Skip s -> Skip (AppendFirst s)+            Stop -> Skip (AppendSecond state2)++    step gst (AppendSecond st) = do+        r <- step2 gst st+        return $ case r of+            Yield a s -> Yield a (AppendSecond s)+            Skip s -> Skip (AppendSecond s)+            Stop -> Stop++------------------------------------------------------------------------------+-- Interleaving+------------------------------------------------------------------------------++data InterleaveState s1 s2 = InterleaveFirst s1 s2 | InterleaveSecond s1 s2+    | InterleaveSecondOnly s2 | InterleaveFirstOnly s1++-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for+-- statically fusing a small number of streams. Use the O(n) complexity+-- StreamK.'Streamly.Data.StreamK.interleave' otherwise.+--+-- Interleaves two streams, yielding one element from each stream alternately.+-- When one stream stops the rest of the other stream is used in the output+-- stream.+--+{-# INLINE_NORMAL interleave #-}+interleave :: Monad m => Stream m a -> Stream m a -> Stream m a+interleave (Stream step1 state1) (Stream step2 state2) =+    Stream step (InterleaveFirst state1 state2)++    where++    {-# INLINE_LATE step #-}+    step gst (InterleaveFirst st1 st2) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveSecond s st2)+            Skip s -> Skip (InterleaveFirst s st2)+            Stop -> Skip (InterleaveSecondOnly st2)++    step gst (InterleaveSecond st1 st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveFirst st1 s)+            Skip s -> Skip (InterleaveSecond st1 s)+            Stop -> Skip (InterleaveFirstOnly st1)++    step gst (InterleaveFirstOnly st1) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveFirstOnly s)+            Skip s -> Skip (InterleaveFirstOnly s)+            Stop -> Stop++    step gst (InterleaveSecondOnly st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveSecondOnly s)+            Skip s -> Skip (InterleaveSecondOnly s)+            Stop -> Stop++-- | Like `interleave` but stops interleaving as soon as any of the two streams+-- stops.+--+{-# INLINE_NORMAL interleaveMin #-}+interleaveMin :: Monad m => Stream m a -> Stream m a -> Stream m a+interleaveMin (Stream step1 state1) (Stream step2 state2) =+    Stream step (InterleaveFirst state1 state2)++    where++    {-# INLINE_LATE step #-}+    step gst (InterleaveFirst st1 st2) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveSecond s st2)+            Skip s -> Skip (InterleaveFirst s st2)+            Stop -> Stop++    step gst (InterleaveSecond st1 st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveFirst st1 s)+            Skip s -> Skip (InterleaveSecond st1 s)+            Stop -> Stop++    step _ (InterleaveFirstOnly _) =  undefined+    step _ (InterleaveSecondOnly _) =  undefined++-- | Interleaves the outputs of two streams, yielding elements from each stream+-- alternately, starting from the first stream. As soon as the first stream+-- finishes, the output stops, discarding the remaining part of the second+-- stream. In this case, the last element in the resulting stream would be from+-- the second stream. If the second stream finishes early then the first stream+-- still continues to yield elements until it finishes.+--+-- >>> :set -XOverloadedStrings+-- >>> import Data.Functor.Identity (Identity)+-- >>> Stream.interleaveFstSuffix "abc" ",,,," :: Stream Identity Char+-- fromList "a,b,c,"+-- >>> Stream.interleaveFstSuffix "abc" "," :: Stream Identity Char+-- fromList "a,bc"+--+-- 'interleaveFstSuffix' is a dual of 'interleaveFst'.+--+-- Do not use dynamically.+--+-- /Pre-release/+{-# INLINE_NORMAL interleaveFstSuffix #-}+interleaveFstSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a+interleaveFstSuffix (Stream step1 state1) (Stream step2 state2) =+    Stream step (InterleaveFirst state1 state2)++    where++    {-# INLINE_LATE step #-}+    step gst (InterleaveFirst st1 st2) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveSecond s st2)+            Skip s -> Skip (InterleaveFirst s st2)+            Stop -> Stop++    step gst (InterleaveSecond st1 st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveFirst st1 s)+            Skip s -> Skip (InterleaveSecond st1 s)+            Stop -> Skip (InterleaveFirstOnly st1)++    step gst (InterleaveFirstOnly st1) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveFirstOnly s)+            Skip s -> Skip (InterleaveFirstOnly s)+            Stop -> Stop++    step _ (InterleaveSecondOnly _) =  undefined++data InterleaveInfixState s1 s2 a+    = InterleaveInfixFirst s1 s2+    | InterleaveInfixSecondBuf s1 s2+    | InterleaveInfixSecondYield s1 s2 a+    | InterleaveInfixFirstYield s1 s2 a+    | InterleaveInfixFirstOnly s1++-- | Interleaves the outputs of two streams, yielding elements from each stream+-- alternately, starting from the first stream and ending at the first stream.+-- If the second stream is longer than the first, elements from the second+-- stream are infixed with elements from the first stream. If the first stream+-- is longer then it continues yielding elements even after the second stream+-- has finished.+--+-- >>> :set -XOverloadedStrings+-- >>> import Data.Functor.Identity (Identity)+-- >>> Stream.interleaveFst "abc" ",,,," :: Stream Identity Char+-- fromList "a,b,c"+-- >>> Stream.interleaveFst "abc" "," :: Stream Identity Char+-- fromList "a,bc"+--+-- 'interleaveFst' is a dual of 'interleaveFstSuffix'.+--+-- Do not use dynamically.+--+-- /Pre-release/+{-# INLINE_NORMAL interleaveFst #-}+interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a+interleaveFst (Stream step1 state1) (Stream step2 state2) =+    Stream step (InterleaveInfixFirst state1 state2)++    where++    {-# INLINE_LATE step #-}+    step gst (InterleaveInfixFirst st1 st2) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveInfixSecondBuf s st2)+            Skip s -> Skip (InterleaveInfixFirst s st2)+            Stop -> Stop++    step gst (InterleaveInfixSecondBuf st1 st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Skip (InterleaveInfixSecondYield st1 s a)+            Skip s -> Skip (InterleaveInfixSecondBuf st1 s)+            Stop -> Skip (InterleaveInfixFirstOnly st1)++    step gst (InterleaveInfixSecondYield st1 st2 x) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield x (InterleaveInfixFirstYield s st2 a)+            Skip s -> Skip (InterleaveInfixSecondYield s st2 x)+            Stop -> Stop++    step _ (InterleaveInfixFirstYield st1 st2 x) = do+        return $ Yield x (InterleaveInfixSecondBuf st1 st2)++    step gst (InterleaveInfixFirstOnly st1) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveInfixFirstOnly s)+            Skip s -> Skip (InterleaveInfixFirstOnly s)+            Stop -> Stop++------------------------------------------------------------------------------+-- Scheduling+------------------------------------------------------------------------------++-- | Schedule the execution of two streams in a fair round-robin manner,+-- executing each stream once, alternately. Execution of a stream may not+-- necessarily result in an output, a stream may choose to @Skip@ producing an+-- element until later giving the other stream a chance to run. Therefore, this+-- combinator fairly interleaves the execution of two streams rather than+-- fairly interleaving the output of the two streams. This can be useful in+-- co-operative multitasking without using explicit threads. This can be used+-- as an alternative to `async`.+--+-- Do not use dynamically.+--+-- /Pre-release/+{-# INLINE_NORMAL roundRobin #-}+roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a+roundRobin (Stream step1 state1) (Stream step2 state2) =+    Stream step (InterleaveFirst state1 state2)++    where++    {-# INLINE_LATE step #-}+    step gst (InterleaveFirst st1 st2) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveSecond s st2)+            Skip s -> Skip (InterleaveSecond s st2)+            Stop -> Skip (InterleaveSecondOnly st2)++    step gst (InterleaveSecond st1 st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveFirst st1 s)+            Skip s -> Skip (InterleaveFirst st1 s)+            Stop -> Skip (InterleaveFirstOnly st1)++    step gst (InterleaveSecondOnly st2) = do+        r <- step2 gst st2+        return $ case r of+            Yield a s -> Yield a (InterleaveSecondOnly s)+            Skip s -> Skip (InterleaveSecondOnly s)+            Stop -> Stop++    step gst (InterleaveFirstOnly st1) = do+        r <- step1 gst st1+        return $ case r of+            Yield a s -> Yield a (InterleaveFirstOnly s)+            Skip s -> Skip (InterleaveFirstOnly s)+            Stop -> Stop++------------------------------------------------------------------------------+-- Merging+------------------------------------------------------------------------------++-- | Like 'mergeBy' but with a monadic comparison function.+--+-- Example, to merge two streams randomly:+--+-- @+-- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT+-- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])+-- [2,1,2,2,2,1,1,1]+-- @+--+-- Example, merge two streams in a proportion of 2:1:+--+-- >>> :{+-- do+--  let s1 = Stream.fromList [1,1,1,1,1,1]+--      s2 = Stream.fromList [2,2,2]+--  let proportionately m n = do+--       ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]+--       return $ \_ _ -> do+--          r <- readIORef ref+--          writeIORef ref $ Prelude.tail r+--          return $ Prelude.head r+--  f <- proportionately 2 1+--  xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2+--  print xs+-- :}+-- [1,1,2,1,1,2,1,1,2]+--+{-# INLINE_NORMAL mergeByM #-}+mergeByM+    :: (Monad m)+    => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+mergeByM cmp (Stream stepa ta) (Stream stepb tb) =+    Stream step (Just ta, Just tb, Nothing, Nothing)+  where+    {-# INLINE_LATE step #-}++    -- one of the values is missing, and the corresponding stream is running+    step gst (Just sa, sb, Nothing, b) = do+        r <- stepa gst sa+        return $ case r of+            Yield a sa' -> Skip (Just sa', sb, Just a, b)+            Skip sa'    -> Skip (Just sa', sb, Nothing, b)+            Stop        -> Skip (Nothing, sb, Nothing, b)++    step gst (sa, Just sb, a, Nothing) = do+        r <- stepb gst sb+        return $ case r of+            Yield b sb' -> Skip (sa, Just sb', a, Just b)+            Skip sb'    -> Skip (sa, Just sb', a, Nothing)+            Stop        -> Skip (sa, Nothing, a, Nothing)++    -- both the values are available+    step _ (sa, sb, Just a, Just b) = do+        res <- cmp a b+        return $ case res of+            GT -> Yield b (sa, sb, Just a, Nothing)+            _  -> Yield a (sa, sb, Nothing, Just b)++    -- one of the values is missing, corresponding stream is done+    step _ (Nothing, sb, Nothing, Just b) =+            return $ Yield b (Nothing, sb, Nothing, Nothing)++    step _ (sa, Nothing, Just a, Nothing) =+            return $ Yield a (sa, Nothing, Nothing, Nothing)++    step _ (Nothing, Nothing, Nothing, Nothing) = return Stop++-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for+-- statically fusing a small number of streams. Use the O(n) complexity+-- StreamK.'Streamly.Data.StreamK.mergeBy' otherwise.+--+-- Merge two streams using a comparison function. The head elements of both+-- the streams are compared and the smaller of the two elements is emitted, if+-- both elements are equal then the element from the first stream is used+-- first.+--+-- If the streams are sorted in ascending order, the resulting stream would+-- also remain sorted in ascending order.+--+-- >>> s1 = Stream.fromList [1,3,5]+-- >>> s2 = Stream.fromList [2,4,6,8]+-- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2+-- [1,2,3,4,5,6,8]+--+{-# INLINE mergeBy #-}+mergeBy+    :: (Monad m)+    => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+mergeBy cmp = mergeByM (\a b -> return $ cmp a b)++-- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.+--+-- /Unimplemented/+{-# INLINABLE mergeMinBy #-}+mergeMinBy :: -- Monad m =>+    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+mergeMinBy _f _m1 _m2 = undefined+    -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)++-- | Like 'mergeByM' but stops merging as soon as the first stream stops.+--+-- /Unimplemented/+{-# INLINABLE mergeFstBy #-}+mergeFstBy :: -- Monad m =>+    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a+mergeFstBy _f _m1 _m2 = undefined+    -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)++-------------------------------------------------------------------------------+-- Intersection of sorted streams+-------------------------------------------------------------------------------++-- Assuming the streams are sorted in ascending order+{-# INLINE_NORMAL intersectBySorted #-}+intersectBySorted :: Monad m+    => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =+    Stream step+        ( ta -- left stream state+        , tb -- right stream state+        , Nothing -- left value+        , Nothing -- right value+        )++    where++    {-# INLINE_LATE step #-}+    -- step 1, fetch the first value+    step gst (sa, sb, Nothing, b) = do+        r <- stepa gst sa+        return $ case r of+            Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3+            Skip sa'    -> Skip (sa', sb, Nothing, b)+            Stop        -> Stop++    -- step 2, fetch the second value+    step gst (sa, sb, a@(Just _), Nothing) = do+        r <- stepb gst sb+        return $ case r of+            Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3+            Skip sb'    -> Skip (sa, sb', a, Nothing)+            Stop        -> Stop++    -- step 3, compare the two values+    step _ (sa, sb, Just a, Just b) = do+        let res = cmp a b+        return $ case res of+            GT -> Skip (sa, sb, Just a, Nothing) -- step 2+            LT -> Skip (sa, sb, Nothing, Just b) -- step 1+            EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1++------------------------------------------------------------------------------+-- Combine N Streams - unfoldMany+------------------------------------------------------------------------------++data ConcatUnfoldInterleaveState o i =+      ConcatUnfoldInterleaveOuter o [i]+    | ConcatUnfoldInterleaveInner o [i]+    | ConcatUnfoldInterleaveInnerL [i] [i]+    | ConcatUnfoldInterleaveInnerR [i] [i]++-- XXX use arrays to store state instead of lists?+--+-- XXX In general we can use different scheduling strategies e.g. how to+-- schedule the outer vs inner loop or assigning weights to different streams+-- or outer and inner loops.++-- After a yield, switch to the next stream. Do not switch streams on Skip.+-- Yield from outer stream switches to the inner stream.+--+-- There are two choices here, (1) exhaust the outer stream first and then+-- start yielding from the inner streams, this is much simpler to implement,+-- (2) yield at least one element from an inner stream before going back to+-- outer stream and opening the next stream from it.+--+-- Ideally, we need some scheduling bias to inner streams vs outer stream.+-- Maybe we can configure the behavior.+--+-- XXX Instead of using "concatPairsWith wSerial" we can implement an N-way+-- interleaving CPS combinator which behaves like unfoldInterleave. Instead+-- of pairing up the streams we just need to go yielding one element from each+-- stream and storing the remaining streams and then keep doing rounds through+-- those in a round robin fashion. This would be much like wAsync.++-- | This does not pair streams like mergeMapWith, instead, it goes through+-- each stream one by one and yields one element from each stream. After it+-- goes to the last stream it reverses the traversal to come back to the first+-- stream yielding elements from each stream on its way back to the first+-- stream and so on.+--+-- >>> lists = Stream.fromList [[1,1],[2,2],[3,3],[4,4],[5,5]]+-- >>> interleaved = Stream.unfoldInterleave Unfold.fromList lists+-- >>> Stream.fold Fold.toList interleaved+-- [1,2,3,4,5,5,4,3,2,1]+--+-- Note that this is order of magnitude more efficient than "mergeMapWith+-- interleave" because of fusion.+--+{-# INLINE_NORMAL unfoldInterleave #-}+unfoldInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b+unfoldInterleave (Unfold istep inject) (Stream ostep ost) =+    Stream step (ConcatUnfoldInterleaveOuter ost [])++    where++    {-# 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 [])++    step _ (ConcatUnfoldInterleaveInner _ []) = undefined+    step _ (ConcatUnfoldInterleaveInner o (st:ls)) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveOuter o (s:ls))+            Skip s    -> Skip (ConcatUnfoldInterleaveInner o (s:ls))+            Stop      -> Skip (ConcatUnfoldInterleaveOuter o ls)++    step _ (ConcatUnfoldInterleaveInnerL [] []) = return Stop+    step _ (ConcatUnfoldInterleaveInnerL [] rs) =+        return $ Skip (ConcatUnfoldInterleaveInnerR [] rs)++    step _ (ConcatUnfoldInterleaveInnerL (st:ls) rs) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerL ls (s:rs))+            Skip s    -> Skip (ConcatUnfoldInterleaveInnerL (s:ls) rs)+            Stop      -> Skip (ConcatUnfoldInterleaveInnerL ls rs)++    step _ (ConcatUnfoldInterleaveInnerR [] []) = return Stop+    step _ (ConcatUnfoldInterleaveInnerR ls []) =+        return $ Skip (ConcatUnfoldInterleaveInnerL ls [])++    step _ (ConcatUnfoldInterleaveInnerR ls (st:rs)) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerR (s:ls) rs)+            Skip s    -> Skip (ConcatUnfoldInterleaveInnerR ls (s:rs))+            Stop      -> Skip (ConcatUnfoldInterleaveInnerR ls rs)++-- XXX In general we can use different scheduling strategies e.g. how to+-- schedule the outer vs inner loop or assigning weights to different streams+-- or outer and inner loops.+--+-- This could be inefficient if the tasks are too small.+--+-- Compared to unfoldInterleave this one switches streams on Skips.++-- | 'unfoldInterleave' switches to the next stream whenever a value from a+-- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps+-- skipping for long time other streams won't get a chance to run.+-- 'unfoldRoundRobin' switches on Skip as well. So it basically schedules each+-- stream fairly irrespective of whether it produces a value or not.+--+{-# INLINE_NORMAL unfoldRoundRobin #-}+unfoldRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b+unfoldRoundRobin (Unfold istep inject) (Stream ostep ost) =+    Stream step (ConcatUnfoldInterleaveOuter ost [])+  where+    {-# 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 (ConcatUnfoldInterleaveInner o' ls)+            Stop -> return $ Skip (ConcatUnfoldInterleaveInnerL ls [])++    step _ (ConcatUnfoldInterleaveInner o []) =+            return $ Skip (ConcatUnfoldInterleaveOuter o [])++    step _ (ConcatUnfoldInterleaveInner o (st:ls)) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveOuter o (s:ls))+            Skip s    -> Skip (ConcatUnfoldInterleaveOuter o (s:ls))+            Stop      -> Skip (ConcatUnfoldInterleaveOuter o ls)++    step _ (ConcatUnfoldInterleaveInnerL [] []) = return Stop+    step _ (ConcatUnfoldInterleaveInnerL [] rs) =+        return $ Skip (ConcatUnfoldInterleaveInnerR [] rs)++    step _ (ConcatUnfoldInterleaveInnerL (st:ls) rs) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerL ls (s:rs))+            Skip s    -> Skip (ConcatUnfoldInterleaveInnerL ls (s:rs))+            Stop      -> Skip (ConcatUnfoldInterleaveInnerL ls rs)++    step _ (ConcatUnfoldInterleaveInnerR [] []) = return Stop+    step _ (ConcatUnfoldInterleaveInnerR ls []) =+        return $ Skip (ConcatUnfoldInterleaveInnerL ls [])++    step _ (ConcatUnfoldInterleaveInnerR ls (st:rs)) = do+        r <- istep st+        return $ case r of+            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerR (s:ls) rs)+            Skip s    -> Skip (ConcatUnfoldInterleaveInnerR (s:ls) rs)+            Stop      -> Skip (ConcatUnfoldInterleaveInnerR ls rs)++------------------------------------------------------------------------------+-- Combine N Streams - interpose+------------------------------------------------------------------------------++{-# ANN type InterposeSuffixState Fuse #-}+data InterposeSuffixState s1 i1 =+      InterposeSuffixFirst s1+    -- | InterposeSuffixFirstYield s1 i1+    | InterposeSuffixFirstInner s1 i1+    | InterposeSuffixSecond s1++-- Note that if an unfolded layer turns out to be nil we still emit the+-- separator effect. An alternate behavior could be to emit the separator+-- effect only if at least one element has been yielded by the unfolding.+-- However, that becomes a bit complicated, so we have chosen the former+-- behvaior for now.+{-# INLINE_NORMAL interposeSuffixM #-}+interposeSuffixM+    :: Monad m+    => m c -> Unfold m b c -> Stream m b -> Stream m c+interposeSuffixM+    action+    (Unfold istep1 inject1) (Stream step1 state1) =+    Stream step (InterposeSuffixFirst state1)++    where++    {-# 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++    {-+    step _ (InterposeSuffixFirstYield s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')+            Skip i'    -> Skip (InterposeSuffixFirstYield s1 i')+            Stop       -> Skip (InterposeSuffixFirst s1)+    -}++    step _ (InterposeSuffixFirstInner s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')+            Skip i'    -> Skip (InterposeSuffixFirstInner s1 i')+            Stop       -> Skip (InterposeSuffixSecond s1)++    step _ (InterposeSuffixSecond s1) = do+        r <- action+        return $ Yield r (InterposeSuffixFirst s1)++-- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)++-- | Unfold the elements of a stream, append the given element after each+-- unfolded stream and then concat them into a single stream.+--+-- >>> unlines = Stream.interposeSuffix '\n'+--+-- /Pre-release/+{-# INLINE interposeSuffix #-}+interposeSuffix :: Monad m+    => c -> Unfold m b c -> Stream m b -> Stream m c+interposeSuffix x = interposeSuffixM (return x)++{-# ANN type InterposeState Fuse #-}+data InterposeState s1 i1 a =+      InterposeFirst s1+    -- | InterposeFirstYield s1 i1+    | InterposeFirstInner s1 i1+    | InterposeFirstInject s1+    -- | InterposeFirstBuf s1 i1+    | InterposeSecondYield s1 i1+    -- -- | InterposeSecondYield s1 i1 a+    -- -- | InterposeFirstResume s1 i1 a++-- Note that this only interposes the pure values, we may run many effects to+-- generate those values as some effects may not generate anything (Skip).+{-# INLINE_NORMAL interposeM #-}+interposeM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c+interposeM+    action+    (Unfold istep1 inject1) (Stream step1 state1) =+    Stream step (InterposeFirst state1)++    where++    {-# 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++    {-+    step _ (InterposeFirstYield s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (InterposeFirstInner s1 i')+            Skip i'    -> Skip (InterposeFirstYield s1 i')+            Stop       -> Skip (InterposeFirst s1)+    -}++    step _ (InterposeFirstInner s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (InterposeFirstInner s1 i')+            Skip i'    -> Skip (InterposeFirstInner s1 i')+            Stop       -> Skip (InterposeFirstInject s1)++    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++    {-+    step _ (InterposeFirstBuf s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Skip (InterposeSecondYield s1 i' x)+            Skip i'    -> Skip (InterposeFirstBuf s1 i')+            Stop       -> Stop+    -}++    {-+    step _ (InterposeSecondYield s1 i1 v) = do+        r <- action+        return $ Yield r (InterposeFirstResume s1 i1 v)+    -}+    step _ (InterposeSecondYield s1 i1) = do+        r <- action+        return $ Yield r (InterposeFirstInner s1 i1)++    {-+    step _ (InterposeFirstResume s1 i1 v) = do+        return $ Yield v (InterposeFirstInner s1 i1)+    -}++-- > interpose x unf str = gintercalate unf str UF.identity (repeat x)++-- | Unfold the elements of a stream, intersperse the given element between the+-- unfolded streams and then concat them into a single stream.+--+-- >>> unwords = Stream.interpose ' '+--+-- /Pre-release/+{-# INLINE interpose #-}+interpose :: Monad m+    => c -> Unfold m b c -> Stream m b -> Stream m c+interpose x = interposeM (return x)++------------------------------------------------------------------------------+-- Combine N Streams - intercalate+------------------------------------------------------------------------------++data ICUState s1 s2 i1 i2 =+      ICUFirst s1 s2+    | ICUSecond s1 s2+    | ICUSecondOnly s2+    | ICUFirstOnly s1+    | ICUFirstInner s1 s2 i1+    | ICUSecondInner s1 s2 i2+    | ICUFirstOnlyInner s1 i1+    | ICUSecondOnlyInner s2 i2++-- | 'interleaveFstSuffix' followed by unfold and concat.+--+-- /Pre-release/+{-# INLINE_NORMAL gintercalateSuffix #-}+gintercalateSuffix+    :: Monad m+    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c+gintercalateSuffix+    (Unfold istep1 inject1) (Stream step1 state1)+    (Unfold istep2 inject2) (Stream step2 state2) =+    Stream step (ICUFirst state1 state2)++    where++    {-# 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++    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++    step _ (ICUFirstInner s1 s2 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (ICUFirstInner s1 s2 i')+            Skip i'    -> Skip (ICUFirstInner s1 s2 i')+            Stop       -> Skip (ICUSecond s1 s2)++    step _ (ICUFirstOnlyInner s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (ICUFirstOnlyInner s1 i')+            Skip i'    -> Skip (ICUFirstOnlyInner s1 i')+            Stop       -> Skip (ICUFirstOnly s1)++    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)++    step _ (ICUSecondInner s1 s2 i2) = do+        r <- istep2 i2+        return $ case r of+            Yield x i' -> Yield x (ICUSecondInner s1 s2 i')+            Skip i'    -> Skip (ICUSecondInner s1 s2 i')+            Stop       -> Skip (ICUFirst s1 s2)++    step _ (ICUSecondOnly _s2) = undefined+    step _ (ICUSecondOnlyInner _s2 _i2) = undefined++data ICALState s1 s2 i1 i2 a =+      ICALFirst s1 s2+    -- | ICALFirstYield s1 s2 i1+    | ICALFirstInner s1 s2 i1+    | ICALFirstOnly s1+    | ICALFirstOnlyInner s1 i1+    | ICALSecondInject s1 s2+    | ICALFirstInject s1 s2 i2+    -- | ICALFirstBuf s1 s2 i1 i2+    | ICALSecondInner s1 s2 i1 i2+    -- -- | ICALSecondInner s1 s2 i1 i2 a+    -- -- | ICALFirstResume s1 s2 i1 i2 a++-- XXX we can swap the order of arguments to gintercalate so that the+-- definition of unfoldMany becomes simpler? The first stream should be+-- infixed inside the second one. However, if we change the order in+-- "interleave" as well similarly, then that will make it a bit unintuitive.+--+-- > unfoldMany unf str =+-- >     gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())++-- | 'interleaveFst' followed by unfold and concat.+--+-- /Pre-release/+{-# INLINE_NORMAL gintercalate #-}+gintercalate+    :: Monad m+    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c+gintercalate+    (Unfold istep1 inject1) (Stream step1 state1)+    (Unfold istep2 inject2) (Stream step2 state2) =+    Stream step (ICALFirst state1 state2)++    where++    {-# 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++    {-+    step _ (ICALFirstYield s1 s2 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (ICALFirstInner s1 s2 i')+            Skip i'    -> Skip (ICALFirstYield s1 s2 i')+            Stop       -> Skip (ICALFirst s1 s2)+    -}++    step _ (ICALFirstInner s1 s2 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (ICALFirstInner s1 s2 i')+            Skip i'    -> Skip (ICALFirstInner s1 s2 i')+            Stop       -> Skip (ICALSecondInject s1 s2)++    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++    step _ (ICALFirstOnlyInner s1 i1) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Yield x (ICALFirstOnlyInner s1 i')+            Skip i'    -> Skip (ICALFirstOnlyInner s1 i')+            Stop       -> Skip (ICALFirstOnly s1)++    -- We inject the second stream even before checking if the first stream+    -- would yield any more elements. There is no clear choice whether we+    -- should do this before or after that. Doing it after may make the state+    -- 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)++    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++    {-+    step _ (ICALFirstBuf s1 s2 i1 i2) = do+        r <- istep1 i1+        return $ case r of+            Yield x i' -> Skip (ICALSecondInner s1 s2 i' i2 x)+            Skip i'    -> Skip (ICALFirstBuf s1 s2 i' i2)+            Stop       -> Stop++    step _ (ICALSecondInner s1 s2 i1 i2 v) = do+        r <- istep2 i2+        return $ case r of+            Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i' v)+            Skip i'    -> Skip (ICALSecondInner s1 s2 i1 i' v)+            Stop       -> Skip (ICALFirstResume s1 s2 i1 i2 v)+    -}++    step _ (ICALSecondInner s1 s2 i1 i2) = do+        r <- istep2 i2+        return $ case r of+            Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i')+            Skip i'    -> Skip (ICALSecondInner s1 s2 i1 i')+            Stop       -> Skip (ICALFirstInner s1 s2 i1)+            -- Stop       -> Skip (ICALFirstResume s1 s2 i1 i2)++    {-+    step _ (ICALFirstResume s1 s2 i1 i2 x) = do+        return $ Yield x (ICALFirstInner s1 s2 i1 i2)+    -}++-- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)++-- | 'intersperseMSuffix' followed by unfold and concat.+--+-- >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseMSuffix a+-- >>> intersperseMSuffix = Stream.intercalateSuffix Unfold.identity+-- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"+--+-- >>> input = Stream.fromList ["abc", "def", "ghi"]+-- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input+-- "abc\ndef\nghi\n"+--+{-# INLINE intercalateSuffix #-}+intercalateSuffix :: Monad m+    => Unfold m b c -> b -> Stream m b -> Stream m c+intercalateSuffix unf seed = unfoldMany unf . intersperseMSuffix (return seed)++-- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)++-- | 'intersperse' followed by unfold and concat.+--+-- >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a+-- >>> intersperse = Stream.intercalate Unfold.identity+-- >>> unwords = Stream.intercalate Unfold.fromList " "+--+-- >>> input = Stream.fromList ["abc", "def", "ghi"]+-- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input+-- "abc def ghi"+--+{-# INLINE intercalate #-}+intercalate :: Monad m+    => Unfold m b c -> b -> Stream m b -> Stream m c+intercalate unf seed str = unfoldMany unf $ intersperse seed str++------------------------------------------------------------------------------+-- Folding+------------------------------------------------------------------------------++-- | Apply a stream of folds to an input stream and emit the results in the+-- output stream.+--+-- /Unimplemented/+--+{-# INLINE foldSequence #-}+foldSequence+       :: -- Monad m =>+       Stream m (Fold m a b)+    -> Stream m a+    -> Stream m b+foldSequence _f _m = undefined++{-# ANN type FIterState Fuse #-}+data FIterState s f m a b+    = FIterInit s f+    | forall fs. FIterStream s (fs -> a -> m (FL.Step fs b)) fs (fs -> m b)+        (fs -> m b)+    | FIterYield b (FIterState s f m a b)+    | FIterStop++-- | Iterate a fold generator on a stream. The initial value @b@ is used to+-- generate the first fold, the fold is applied on the stream and the result of+-- the fold is used to generate the next fold and so on.+--+-- >>> import Data.Monoid (Sum(..))+-- >>> f x = return (Fold.take 2 (Fold.sconcat x))+-- >>> s = fmap Sum $ Stream.fromList [1..10]+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s+-- [3,10,21,36,55,55]+--+-- This is the streaming equivalent of monad like sequenced application of+-- folds where next fold is dependent on the previous fold.+--+-- /Pre-release/+--+{-# INLINE_NORMAL foldIterateM #-}+foldIterateM ::+       Monad m => (b -> m (FL.Fold m a b)) -> m b -> Stream m a -> Stream m b+foldIterateM func seed0 (Stream step state) =+    Stream stepOuter (FIterInit state seed0)++    where++    {-# INLINE iterStep #-}+    iterStep from st fstep extract final = do+        res <- from+        return+            $ Skip+            $ case res of+                  FL.Partial fs -> FIterStream st fstep fs extract final+                  FL.Done fb -> FIterYield fb $ FIterInit st (return fb)++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (FIterInit st seed) = do+        (FL.Fold fstep initial extract final) <- seed >>= func+        iterStep initial st fstep extract final+    stepOuter gst (FIterStream st fstep fs extract final) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                iterStep (fstep fs x) s fstep extract final+            Skip s -> return $ Skip $ FIterStream s fstep fs extract final+            Stop -> do+                b <- final fs+                return $ Skip $ FIterYield b FIterStop+    stepOuter _ (FIterYield a next) = return $ Yield a next+    stepOuter _ FIterStop = return Stop++{-# ANN type CIterState Fuse #-}+data CIterState s f fs b+    = CIterInit s f+    | CIterConsume s fs+    | CIterYield b (CIterState s f fs b)+    | CIterStop++-- | Like 'foldIterateM' but using the 'Refold' type instead. This could be+-- much more efficient due to stream fusion.+--+-- /Internal/+{-# INLINE_NORMAL refoldIterateM #-}+refoldIterateM ::+       Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b+refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =+    Stream stepOuter (CIterInit state initial)++    where++    {-# INLINE iterStep #-}+    iterStep st action = do+        res <- action+        return+            $ Skip+            $ case res of+                  FL.Partial fs -> CIterConsume st fs+                  FL.Done fb -> CIterYield fb $ CIterInit st (return fb)++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (CIterInit st action) = do+        iterStep st (action >>= finject)+    stepOuter gst (CIterConsume st fs) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> iterStep s (fstep fs x)+            Skip s -> return $ Skip $ CIterConsume s fs+            Stop -> do+                b <- fextract fs+                return $ Skip $ CIterYield b CIterStop+    stepOuter _ (CIterYield a next) = return $ Yield a next+    stepOuter _ CIterStop = return Stop++-- "n" elements at the end are dropped by the fold.+{-# INLINE sliceBy #-}+sliceBy :: Monad m => Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)+sliceBy (Fold step1 initial1 extract1 _final) n = Refold step inject extract++    where++    inject (i, len) = do+        r <- initial1+        return $ case r of+            Partial s -> Partial $ Tuple' (i + len + n) s+            Done l -> Done (i, l)++    step (Tuple' i s) x = do+        r <- step1 s x+        return $ case r of+            Partial s1 -> Partial $ Tuple' i s1+            Done len -> Done (i, len)++    extract (Tuple' i s) = (i,) <$> extract1 s++{-# INLINE sliceOnSuffix #-}+sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+sliceOnSuffix predicate =+    -- Scan the stream with the given refold+    refoldIterateM+        (sliceBy (FL.takeEndBy_ predicate FL.length) 1)+        (return (-1, 0))++------------------------------------------------------------------------------+-- Parsing+------------------------------------------------------------------------------++{-# ANN type ParseChunksState Fuse #-}+data ParseChunksState x inpBuf st pst =+      ParseChunksInit inpBuf st+    | ParseChunksInitBuf inpBuf+    | ParseChunksInitLeftOver inpBuf+    | ParseChunksStream st inpBuf !pst+    | ParseChunksStop inpBuf !pst+    | ParseChunksBuf inpBuf st inpBuf !pst+    | ParseChunksExtract inpBuf inpBuf !pst+    | ParseChunksYield x (ParseChunksState x inpBuf st pst)++-- XXX return the remaining stream as part of the error.+-- XXX This is in fact parseMany1 (a la foldMany1). Do we need a parseMany as+-- well?+{-# INLINE_NORMAL parseManyD #-}+parseManyD+    :: Monad m+    => PRD.Parser a m b+    -> Stream m a+    -> Stream m (Either ParseError b)+parseManyD (PRD.Parser pstep initial extract) (Stream step state) =+    Stream stepOuter (ParseChunksInit [] state)++    where++    {-# INLINE_LATE stepOuter #-}+    -- Buffer is empty, get the first element from the stream, initialize the+    -- fold and then go to stream processing loop.+    stepOuter gst (ParseChunksInit [] st) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                res <- initial+                case res of+                    PRD.IPartial ps ->+                        return $ Skip $ ParseChunksBuf [x] s [] ps+                    PRD.IDone pb ->+                        let next = ParseChunksInit [x] s+                         in return $ Skip $ ParseChunksYield (Right pb) next+                    PRD.IError err ->+                        return+                            $ Skip+                            $ ParseChunksYield+                                (Left (ParseError err))+                                (ParseChunksInitLeftOver [])+            Skip s -> return $ Skip $ ParseChunksInit [] s+            Stop   -> return Stop++    -- Buffer is not empty, go to buffered processing loop+    stepOuter _ (ParseChunksInit src st) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ Skip $ ParseChunksBuf src st [] ps+            PRD.IDone pb ->+                let next = ParseChunksInit src st+                 in return $ Skip $ ParseChunksYield (Right pb) next+            PRD.IError err ->+                return+                    $ Skip+                    $ ParseChunksYield+                        (Left (ParseError err))+                        (ParseChunksInitLeftOver [])++    -- This is simplified ParseChunksInit+    stepOuter _ (ParseChunksInitBuf src) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ Skip $ ParseChunksExtract src [] ps+            PRD.IDone pb ->+                let next = ParseChunksInitBuf src+                 in return $ Skip $ ParseChunksYield (Right pb) next+            PRD.IError err ->+                return+                    $ Skip+                    $ ParseChunksYield+                        (Left (ParseError err))+                        (ParseChunksInitLeftOver [])++    -- XXX we just discard any leftover input at the end+    stepOuter _ (ParseChunksInitLeftOver _) = return Stop++    -- Buffer is empty, process elements from the stream+    stepOuter gst (ParseChunksStream st buf pst) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                pRes <- pstep pst x+                case pRes of+                    PR.Partial 0 pst1 ->+                        return $ Skip $ ParseChunksStream s [] pst1+                    PR.Partial n pst1 -> do+                        assert (n <= length (x:buf)) (return ())+                        let src0 = Prelude.take n (x:buf)+                            src  = Prelude.reverse src0+                        return $ Skip $ ParseChunksBuf src s [] pst1+                    PR.Continue 0 pst1 ->+                        return $ Skip $ ParseChunksStream s (x:buf) pst1+                    PR.Continue n pst1 -> do+                        assert (n <= length (x:buf)) (return ())+                        let (src0, buf1) = splitAt n (x:buf)+                            src  = Prelude.reverse src0+                        return $ Skip $ ParseChunksBuf src s buf1 pst1+                    PR.Done 0 b -> do+                        return $ Skip $+                            ParseChunksYield (Right b) (ParseChunksInit [] s)+                    PR.Done n b -> do+                        assert (n <= length (x:buf)) (return ())+                        let src = Prelude.reverse (Prelude.take n (x:buf))+                        return $ Skip $+                            ParseChunksYield (Right b) (ParseChunksInit src s)+                    PR.Error err ->+                        return+                            $ Skip+                            $ ParseChunksYield+                                (Left (ParseError err))+                                (ParseChunksInitLeftOver [])+            Skip s -> return $ Skip $ ParseChunksStream s buf pst+            Stop -> return $ Skip $ ParseChunksStop buf pst++    -- go back to stream processing mode+    stepOuter _ (ParseChunksBuf [] s buf pst) =+        return $ Skip $ ParseChunksStream s buf pst++    -- buffered processing loop+    stepOuter _ (ParseChunksBuf (x:xs) s buf pst) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ Skip $ ParseChunksBuf xs s [] pst1+            PR.Partial n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ParseChunksBuf src s [] pst1+            PR.Continue 0 pst1 ->+                return $ Skip $ ParseChunksBuf xs s (x:buf) pst1+            PR.Continue n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let (src0, buf1) = splitAt n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ParseChunksBuf src s buf1 pst1+            PR.Done 0 b ->+                return+                    $ Skip+                    $ ParseChunksYield (Right b) (ParseChunksInit xs s)+            PR.Done n b -> do+                assert (n <= length (x:buf)) (return ())+                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs+                return $ Skip+                    $ ParseChunksYield (Right b) (ParseChunksInit src s)+            PR.Error err ->+                return+                    $ Skip+                    $ ParseChunksYield+                        (Left (ParseError err))+                        (ParseChunksInitLeftOver [])++    -- This is simplified ParseChunksBuf+    stepOuter _ (ParseChunksExtract [] buf pst) =+        return $ Skip $ ParseChunksStop buf pst++    stepOuter _ (ParseChunksExtract (x:xs) buf pst) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ Skip $ ParseChunksExtract xs [] pst1+            PR.Partial n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ParseChunksExtract src [] pst1+            PR.Continue 0 pst1 ->+                return $ Skip $ ParseChunksExtract xs (x:buf) pst1+            PR.Continue n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let (src0, buf1) = splitAt n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ParseChunksExtract src buf1 pst1+            PR.Done 0 b ->+                return+                    $ Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)+            PR.Done n b -> do+                assert (n <= length (x:buf)) (return ())+                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs+                return+                    $ Skip+                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)+            PR.Error err ->+                return+                    $ Skip+                    $ ParseChunksYield+                        (Left (ParseError err))+                        (ParseChunksInitLeftOver [])++    -- This is simplified ParseChunksExtract+    stepOuter _ (ParseChunksStop buf pst) = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "Bug: parseMany: Partial in extract"+            PR.Continue 0 pst1 ->+                return $ Skip $ ParseChunksStop buf pst1+            PR.Continue n pst1 -> do+                assert (n <= length buf) (return ())+                let (src0, buf1) = splitAt n buf+                    src  = Prelude.reverse src0+                return $ Skip $ ParseChunksExtract src buf1 pst1+            PR.Done 0 b -> do+                return $ Skip $+                    ParseChunksYield (Right b) (ParseChunksInitLeftOver [])+            PR.Done n b -> do+                assert (n <= length buf) (return ())+                let src = Prelude.reverse (Prelude.take n buf)+                return $ Skip $+                    ParseChunksYield (Right b) (ParseChunksInitBuf src)+            PR.Error err ->+                return+                    $ Skip+                    $ ParseChunksYield+                        (Left (ParseError err))+                        (ParseChunksInitLeftOver [])++    stepOuter _ (ParseChunksYield a next) = return $ Yield a next++-- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the+-- output stream.+--+-- Example:+--+-- >>> s = Stream.fromList [1..10]+-- >>> parser = Parser.takeBetween 0 2 Fold.sum+-- >>> Stream.fold Fold.toList $ Stream.parseMany parser s+-- [Right 3,Right 7,Right 11,Right 15,Right 19]+--+-- This is the streaming equivalent of the 'Streamly.Data.Parser.many' parse+-- combinator.+--+-- Known Issues: When the parser fails there is no way to get the remaining+-- stream.+--+{-# INLINE parseMany #-}+parseMany+    :: Monad m+    => PR.Parser a m b+    -> Stream m a+    -> Stream m (Either ParseError b)+parseMany = parseManyD++-- | Apply a stream of parsers to an input stream and emit the results in the+-- output stream.+--+-- /Unimplemented/+--+{-# INLINE parseSequence #-}+parseSequence+       :: -- Monad m =>+       Stream m (PR.Parser a m b)+    -> Stream m a+    -> Stream m b+parseSequence _f _m = undefined++-- XXX Change the parser arguments' order++-- | @parseManyTill collect test stream@ tries the parser @test@ on the input,+-- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds+-- @test@ is tried again and so on. The parser stops when @test@ succeeds.  The+-- output of @test@ is discarded and the output of @collect@ is emitted in the+-- output stream. The parser fails if @collect@ fails.+--+-- /Unimplemented/+--+{-# INLINE parseManyTill #-}+parseManyTill ::+    -- MonadThrow m =>+       PR.Parser a m b+    -> PR.Parser a m x+    -> Stream m a+    -> Stream m b+parseManyTill = undefined++{-# ANN type ConcatParseState Fuse #-}+data ConcatParseState c b inpBuf st p m a =+      ConcatParseInit inpBuf st p+    | ConcatParseInitBuf inpBuf p+    | ConcatParseInitLeftOver inpBuf+    | forall s. ConcatParseStop+        inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))+    | forall s. ConcatParseStream+        st inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))+    | forall s. ConcatParseBuf+        inpBuf st inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))+    | forall s. ConcatParseExtract+        inpBuf inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))+    | ConcatParseYield c (ConcatParseState c b inpBuf st p m a)++-- XXX Review the changes+{-# INLINE_NORMAL parseIterateD #-}+parseIterateD+    :: Monad m+    => (b -> PRD.Parser a m b)+    -> b+    -> Stream m a+    -> Stream m (Either ParseError b)+parseIterateD func seed (Stream step state) =+    Stream stepOuter (ConcatParseInit [] state (func seed))++    where++    {-# INLINE_LATE stepOuter #-}+    -- Buffer is empty, go to stream processing loop+    stepOuter _ (ConcatParseInit [] st (PRD.Parser pstep initial extract)) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ Skip $ ConcatParseStream st [] pstep ps extract+            PRD.IDone pb ->+                let next = ConcatParseInit [] st (func pb)+                 in return $ Skip $ ConcatParseYield (Right pb) next+            PRD.IError err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    -- Buffer is not empty, go to buffered processing loop+    stepOuter _ (ConcatParseInit src st+                    (PRD.Parser pstep initial extract)) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ Skip $ ConcatParseBuf src st [] pstep ps extract+            PRD.IDone pb ->+                let next = ConcatParseInit src st (func pb)+                 in return $ Skip $ ConcatParseYield (Right pb) next+            PRD.IError err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    -- This is simplified ConcatParseInit+    stepOuter _ (ConcatParseInitBuf src+                    (PRD.Parser pstep initial extract)) = do+        res <- initial+        case res of+            PRD.IPartial ps ->+                return $ Skip $ ConcatParseExtract src [] pstep ps extract+            PRD.IDone pb ->+                let next = ConcatParseInitBuf src (func pb)+                 in return $ Skip $ ConcatParseYield (Right pb) next+            PRD.IError err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    -- XXX we just discard any leftover input at the end+    stepOuter _ (ConcatParseInitLeftOver _) = return Stop++    -- Buffer is empty process elements from the stream+    stepOuter gst (ConcatParseStream st buf pstep pst extract) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                pRes <- pstep pst x+                case pRes of+                    PR.Partial 0 pst1 ->+                        return $ Skip $ ConcatParseStream s [] pstep pst1 extract+                    PR.Partial n pst1 -> do+                        assert (n <= length (x:buf)) (return ())+                        let src0 = Prelude.take n (x:buf)+                            src  = Prelude.reverse src0+                        return $ Skip $ ConcatParseBuf src s [] pstep pst1 extract+                    -- PR.Continue 0 pst1 ->+                    --     return $ Skip $ ConcatParseStream s (x:buf) pst1+                    PR.Continue n pst1 -> do+                        assert (n <= length (x:buf)) (return ())+                        let (src0, buf1) = splitAt n (x:buf)+                            src  = Prelude.reverse src0+                        return $ Skip $ ConcatParseBuf src s buf1 pstep pst1 extract+                    -- XXX Specialize for Stop 0 common case?+                    PR.Done n b -> do+                        assert (n <= length (x:buf)) (return ())+                        let src = Prelude.reverse (Prelude.take n (x:buf))+                        return $ Skip $+                            ConcatParseYield (Right b) (ConcatParseInit src s (func b))+                    PR.Error err ->+                        return+                            $ Skip+                            $ ConcatParseYield+                                (Left (ParseError err))+                                (ConcatParseInitLeftOver [])+            Skip s -> return $ Skip $ ConcatParseStream s buf pstep pst extract+            Stop -> return $ Skip $ ConcatParseStop buf pstep pst extract++    -- go back to stream processing mode+    stepOuter _ (ConcatParseBuf [] s buf pstep ps extract) =+        return $ Skip $ ConcatParseStream s buf pstep ps extract++    -- buffered processing loop+    stepOuter _ (ConcatParseBuf (x:xs) s buf pstep pst extract) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ Skip $ ConcatParseBuf xs s [] pstep pst1 extract+            PR.Partial n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ConcatParseBuf src s [] pstep pst1 extract+         -- PR.Continue 0 pst1 -> return $ Skip $ ConcatParseBuf xs s (x:buf) pst1+            PR.Continue n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let (src0, buf1) = splitAt n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ConcatParseBuf src s buf1 pstep pst1 extract+            -- XXX Specialize for Stop 0 common case?+            PR.Done n b -> do+                assert (n <= length (x:buf)) (return ())+                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs+                return $ Skip $ ConcatParseYield (Right b)+                                    (ConcatParseInit src s (func b))+            PR.Error err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    -- This is simplified ConcatParseBuf+    stepOuter _ (ConcatParseExtract [] buf pstep pst extract) =+        return $ Skip $ ConcatParseStop buf pstep pst extract++    stepOuter _ (ConcatParseExtract (x:xs) buf pstep pst extract) = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 pst1 ->+                return $ Skip $ ConcatParseExtract xs [] pstep pst1 extract+            PR.Partial n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ConcatParseExtract src [] pstep pst1 extract+            PR.Continue 0 pst1 ->+                return $ Skip $ ConcatParseExtract xs (x:buf) pstep pst1 extract+            PR.Continue n pst1 -> do+                assert (n <= length (x:buf)) (return ())+                let (src0, buf1) = splitAt n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                return $ Skip $ ConcatParseExtract src buf1 pstep pst1 extract+            PR.Done 0 b ->+                 return $ Skip $ ConcatParseYield (Right b) (ConcatParseInitBuf xs (func b))+            PR.Done n b -> do+                assert (n <= length (x:buf)) (return ())+                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs+                return $ Skip $ ConcatParseYield (Right b) (ConcatParseInitBuf src (func b))+            PR.Error err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    -- This is simplified ConcatParseExtract+    stepOuter _ (ConcatParseStop buf pstep pst extract) = do+        pRes <- extract pst+        case pRes of+            PR.Partial _ _ -> error "Bug: parseIterate: Partial in extract"+            PR.Continue 0 pst1 ->+                return $ Skip $ ConcatParseStop buf pstep pst1 extract+            PR.Continue n pst1 -> do+                assert (n <= length buf) (return ())+                let (src0, buf1) = splitAt n buf+                    src  = Prelude.reverse src0+                return $ Skip $ ConcatParseExtract src buf1 pstep pst1 extract+            PR.Done 0 b -> do+                return $ Skip $+                    ConcatParseYield (Right b) (ConcatParseInitLeftOver [])+            PR.Done n b -> do+                assert (n <= length buf) (return ())+                let src = Prelude.reverse (Prelude.take n buf)+                return $ Skip $+                    ConcatParseYield (Right b) (ConcatParseInitBuf src (func b))+            PR.Error err ->+                return+                    $ Skip+                    $ ConcatParseYield+                        (Left (ParseError err))+                        (ConcatParseInitLeftOver [])++    stepOuter _ (ConcatParseYield a next) = return $ Yield a next++-- | Iterate a parser generating function on a stream. The initial value @b@ is+-- used to generate the first parser, the parser is applied on the stream and+-- the result is used to generate the next parser and so on.+--+-- >>> import Data.Monoid (Sum(..))+-- >>> s = Stream.fromList [1..10]+-- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.catRights $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) (Sum 0) $ fmap Sum s+-- [3,10,21,36,55,55]+--+-- This is the streaming equivalent of monad like sequenced application of+-- parsers where next parser is dependent on the previous parser.+--+-- /Pre-release/+--+{-# INLINE parseIterate #-}+parseIterate+    :: Monad m+    => (b -> PR.Parser a m b)+    -> b+    -> Stream m a+    -> Stream m (Either ParseError b)+parseIterate = parseIterateD++------------------------------------------------------------------------------+-- Grouping+------------------------------------------------------------------------------++data GroupByState st fs a b+    = GroupingInit st+    | GroupingDo st !fs+    | GroupingInitWith st !a+    | GroupingDoWith st !fs !a+    | GroupingYield !b (GroupByState st fs a b)+    | GroupingDone++-- | The argument order of the comparison function in `groupsWhile` is+-- different than that of `groupsBy`.+--+-- In `groupsBy` the comparison function takes the next element as the first+-- argument and the previous element as the second argument. In `groupsWhile`+-- the first argument is the previous element and second argument is the next+-- element.+{-# INLINE_NORMAL groupsWhile #-}+groupsWhile :: Monad m+    => (a -> a -> Bool)+    -> Fold m a b+    -> Stream m a+    -> Stream m b+{-+groupsWhile eq fld = parseMany (PRD.groupBy eq fld)+-}+groupsWhile cmp (Fold fstep initial _ final) (Stream step state) =+    Stream stepOuter (GroupingInit state)++    where++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (GroupingInit st) = do+        -- XXX Note that if the stream stops without yielding a single element+        -- in the group we discard the "initial" effect.+        res <- initial+        return+            $ case res of+                  FL.Partial s -> Skip $ GroupingDo st s+                  FL.Done b -> Yield b $ GroupingInit st+    stepOuter gst (GroupingDo st fs) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                r <- fstep fs x+                case r of+                    FL.Partial fs1 -> go SPEC x s fs1+                    FL.Done b -> return $ Yield b (GroupingInit s)+            Skip s -> return $ Skip $ GroupingDo s fs+            Stop -> final fs >> return Stop++        where++        go !_ prev stt !acc = do+            res <- step (adaptState gst) stt+            case res of+                Yield x s -> do+                    if cmp prev x+                    then do+                        r <- fstep acc x+                        case r of+                            FL.Partial fs1 -> go SPEC prev s fs1+                            FL.Done b -> return $ Yield b (GroupingInit s)+                    else do+                        r <- final acc+                        return $ Yield r (GroupingInitWith s x)+                Skip s -> go SPEC prev s acc+                Stop -> do+                    r <- final acc+                    return $ Yield r GroupingDone+    stepOuter _ (GroupingInitWith st x) = do+        res <- initial+        return+            $ case res of+                  FL.Partial s -> Skip $ GroupingDoWith st s x+                  FL.Done b -> Yield b $ GroupingInitWith st x+    stepOuter gst (GroupingDoWith st fs prev) = do+        res <- fstep fs prev+        case res of+            FL.Partial fs1 -> go SPEC st fs1+            FL.Done b -> return $ Yield b (GroupingInit st)++        where++        -- XXX code duplicated from the previous equation+        go !_ stt !acc = do+            res <- step (adaptState gst) stt+            case res of+                Yield x s -> do+                    if cmp prev x+                    then do+                        r <- fstep acc x+                        case r of+                            FL.Partial fs1 -> go SPEC s fs1+                            FL.Done b -> return $ Yield b (GroupingInit s)+                    else do+                        r <- final acc+                        return $ Yield r (GroupingInitWith s x)+                Skip s -> go SPEC s acc+                Stop -> do+                    r <- final acc+                    return $ Yield r GroupingDone+    stepOuter _ (GroupingYield _ _) = error "groupsWhile: Unreachable"+    stepOuter _ GroupingDone = return Stop++{-# DEPRECATED groupsBy "Please use groupsWhile instead. Please note the change in the argument order of the comparison function." #-}+{-# INLINE_NORMAL groupsBy #-}+groupsBy :: Monad m+    => (a -> a -> Bool)+    -> Fold m a b+    -> Stream m a+    -> Stream m b+groupsBy cmp = groupsWhile (flip cmp)++{-# INLINE_NORMAL groupsRollingBy #-}+groupsRollingBy :: Monad m+    => (a -> a -> Bool)+    -> Fold m a b+    -> Stream m a+    -> Stream m b+{-+groupsRollingBy eq fld = parseMany (PRD.groupByRolling eq fld)+-}+groupsRollingBy cmp (Fold fstep initial _ final) (Stream step state) =+    Stream stepOuter (GroupingInit state)++    where++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (GroupingInit st) = do+        -- XXX Note that if the stream stops without yielding a single element+        -- in the group we discard the "initial" effect.+        res <- initial+        return+            $ case res of+                  FL.Partial fs -> Skip $ GroupingDo st fs+                  FL.Done fb -> Yield fb $ GroupingInit st+    stepOuter gst (GroupingDo st fs) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                r <- fstep fs x+                case r of+                    FL.Partial fs1 -> go SPEC x s fs1+                    FL.Done fb -> return $ Yield fb (GroupingInit s)+            Skip s -> return $ Skip $ GroupingDo s fs+            Stop -> final fs >> return Stop++        where++        go !_ prev stt !acc = do+            res <- step (adaptState gst) stt+            case res of+                Yield x s -> do+                    if cmp prev x+                    then do+                        r <- fstep acc x+                        case r of+                            FL.Partial fs1 -> go SPEC x s fs1+                            FL.Done b -> return $ Yield b (GroupingInit s)+                    else do+                        r <- final acc+                        return $ Yield r (GroupingInitWith s x)+                Skip s -> go SPEC prev s acc+                Stop -> do+                    r <- final acc+                    return $ Yield r GroupingDone+    stepOuter _ (GroupingInitWith st x) = do+        res <- initial+        return+            $ case res of+                  FL.Partial s -> Skip $ GroupingDoWith st s x+                  FL.Done b -> Yield b $ GroupingInitWith st x+    stepOuter gst (GroupingDoWith st fs previous) = do+        res <- fstep fs previous+        case res of+            FL.Partial s -> go SPEC previous st s+            FL.Done b -> return $ Yield b (GroupingInit st)++        where++        -- XXX GHC: groupsWhile has one less parameter in this go loop and it+        -- fuses. However, groupsRollingBy does not fuse, removing the prev+        -- parameter makes it fuse. Something needs to be fixed in GHC. The+        -- workaround for this is noted in the comments below.+        go !_ prev !stt !acc = do+            res <- step (adaptState gst) stt+            case res of+                Yield x s -> do+                    if cmp prev x+                    then do+                        r <- fstep acc x+                        case r of+                            FL.Partial fs1 -> go SPEC x s fs1+                            FL.Done b -> return $ Yield b (GroupingInit st)+                    else do+                        {-+                        r <- final acc+                        return $ Yield r (GroupingInitWith s x)+                        -}+                        -- The code above does not let groupBy fuse. We use the+                        -- alternative code below instead.  Instead of jumping+                        -- to GroupingInitWith state, we unroll the code of+                        -- GroupingInitWith state here to help GHC with stream+                        -- fusion.+                        result <- initial+                        r <- final acc+                        return+                            $ Yield r+                            $ case result of+                                  FL.Partial fsi -> GroupingDoWith s fsi x+                                  FL.Done b -> GroupingYield b (GroupingInit s)+                Skip s -> go SPEC prev s acc+                Stop -> do+                    r <- final acc+                    return $ Yield r GroupingDone+    stepOuter _ (GroupingYield r next) = return $ Yield r next+    stepOuter _ GroupingDone = return Stop++------------------------------------------------------------------------------+-- Splitting - by a predicate+------------------------------------------------------------------------------++data WordsByState st fs b+    = WordsByInit st+    | WordsByDo st !fs+    | WordsByDone+    | WordsByYield !b (WordsByState st fs b)++-- | Split the stream after stripping leading, trailing, and repeated separators+-- as per the fold supplied.+-- Therefore, @".a..b."@ with '.' as the separator would be parsed as+-- @["a","b"]@.  In other words, its like parsing words from whitespace+-- separated text.++{-# INLINE_NORMAL wordsBy #-}+wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+wordsBy predicate (Fold fstep initial _ final) (Stream step state) =+    Stream stepOuter (WordsByInit state)++    where++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ (WordsByInit st) = do+        res <- initial+        return+            $ case res of+                  FL.Partial s -> Skip $ WordsByDo st s+                  FL.Done b -> Yield b (WordsByInit st)++    stepOuter gst (WordsByDo st fs) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                if predicate x+                then do+                    resi <- initial+                    return+                        $ case resi of+                              FL.Partial fs1 -> Skip $ WordsByDo s fs1+                              FL.Done b -> Yield b (WordsByInit s)+                else do+                    r <- fstep fs x+                    case r of+                        FL.Partial fs1 -> go SPEC s fs1+                        FL.Done b -> return $ Yield b (WordsByInit s)+            Skip s    -> return $ Skip $ WordsByDo s fs+            Stop      -> final fs >> return Stop++        where++        go !_ stt !acc = do+            res <- step (adaptState gst) stt+            case res of+                Yield x s -> do+                    if predicate x+                    then do+                        {-+                        r <- final acc+                        return $ Yield r (WordsByInit s)+                        -}+                        -- The above code does not fuse well. Need to check why+                        -- GHC is not able to simplify it well.  Using the code+                        -- below, instead of jumping through the WordsByInit+                        -- state always, we directly go to WordsByDo state in+                        -- the common case of Partial.+                        resi <- initial+                        r <- final acc+                        return+                            $ Yield r+                            $ case resi of+                                  FL.Partial fs1 -> WordsByDo s fs1+                                  FL.Done b -> WordsByYield b (WordsByInit s)+                    else do+                        r <- fstep acc x+                        case r of+                            FL.Partial fs1 -> go SPEC s fs1+                            FL.Done b -> return $ Yield b (WordsByInit s)+                Skip s -> go SPEC s acc+                Stop -> do+                    r <- final acc+                    return $ Yield r WordsByDone++    stepOuter _ WordsByDone = return Stop++    stepOuter _ (WordsByYield b next) = return $ Yield b next++------------------------------------------------------------------------------+-- Splitting on a sequence+------------------------------------------------------------------------------++-- String search algorithms:+-- http://www-igm.univ-mlv.fr/~lecroq/string/index.html++{-+-- TODO can we unify the splitting operations using a splitting configuration+-- like in the split package.+--+data SplitStyle = Infix | Suffix | Prefix deriving (Eq, Show)+data SplitOptions = SplitOptions+    { style    :: SplitStyle+    , withSep  :: Bool  -- ^ keep the separators in output+    -- , compact  :: Bool  -- ^ treat multiple consecutive separators as one+    -- , trimHead :: Bool  -- ^ drop blank at head+    -- , trimTail :: Bool  -- ^ drop blank at tail+    }+-}++-- XXX using "fs" as the last arg in Constructors may simplify the code a bit,+-- because we can use the constructor directly without having to create "jump"+-- functions.+{-# ANN type SplitOnSeqState Fuse #-}+data SplitOnSeqState rb rh ck w fs s b x =+      SplitOnSeqInit+    | SplitOnSeqYield b (SplitOnSeqState rb rh ck w fs s b x)+    | SplitOnSeqDone++    | SplitOnSeqEmpty !fs s++    | SplitOnSeqSingle !fs s x++    | SplitOnSeqWordInit !fs s+    | SplitOnSeqWordLoop !w s !fs+    | SplitOnSeqWordDone Int !fs !w++    | SplitOnSeqKRInit Int !fs s rb !rh+    | SplitOnSeqKRLoop fs s rb !rh !ck+    | SplitOnSeqKRCheck fs s rb !rh+    | SplitOnSeqKRDone Int !fs rb !rh++    | SplitOnSeqReinit (fs -> SplitOnSeqState rb rh ck w fs s b x)++{-# INLINE_NORMAL splitOnSeq #-}+splitOnSeq+    :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a)+    => Array a+    -> Fold m a b+    -> Stream m a+    -> Stream m b+splitOnSeq patArr (Fold fstep initial _ final) (Stream step state) =+    Stream stepOuter SplitOnSeqInit++    where++    patLen = A.length patArr+    maxIndex = patLen - 1+    elemBits = SIZE_OF(a) * 8++    -- For word pattern case+    wordMask :: Word+    wordMask = (1 `shiftL` (elemBits * patLen)) - 1++    elemMask :: Word+    elemMask = (1 `shiftL` elemBits) - 1++    wordPat :: Word+    wordPat = wordMask .&. A.foldl' addToWord 0 patArr++    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)++    -- For Rabin-Karp search+    k = 2891336453 :: Word32+    coeff = k ^ patLen++    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)++    deltaCksum cksum old new =+        addCksum cksum new - coeff * fromIntegral (fromEnum old)++    -- XXX shall we use a random starting hash or 1 instead of 0?+    patHash = A.foldl' addCksum 0 patArr++    skip = return . Skip++    nextAfterInit nextGen stepRes =+        case stepRes of+            FL.Partial s -> nextGen s+            FL.Done b -> SplitOnSeqYield b (SplitOnSeqReinit nextGen)++    {-# INLINE yieldProceed #-}+    yieldProceed nextGen fs =+        initial >>= skip . SplitOnSeqYield fs . nextAfterInit nextGen++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ SplitOnSeqInit = do+        res <- initial+        case res of+            FL.Partial acc ->+                if patLen == 0+                then return $ Skip $ SplitOnSeqEmpty acc state+                else if patLen == 1+                     then do+                         pat <- liftIO $ A.unsafeIndexIO 0 patArr+                         return $ Skip $ SplitOnSeqSingle acc state pat+                     else if SIZE_OF(a) * patLen+                               <= sizeOf (Proxy :: Proxy Word)+                          then return $ Skip $ SplitOnSeqWordInit acc state+                          else do+                              (rb, rhead) <- liftIO $ RB.new patLen+                              skip $ SplitOnSeqKRInit 0 acc state rb rhead+            FL.Done b -> skip $ SplitOnSeqYield b SplitOnSeqInit++    stepOuter _ (SplitOnSeqYield x next) = return $ Yield x next++    ---------------------------+    -- Checkpoint+    ---------------------------++    stepOuter _ (SplitOnSeqReinit nextGen) =+        initial >>= skip . nextAfterInit nextGen++    ---------------------------+    -- Empty pattern+    ---------------------------++    stepOuter gst (SplitOnSeqEmpty acc st) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                r <- fstep acc x+                b1 <-+                    case r of+                        FL.Partial acc1 -> final acc1+                        FL.Done b -> return b+                let jump c = SplitOnSeqEmpty c s+                 in yieldProceed jump b1+            Skip s -> skip (SplitOnSeqEmpty acc s)+            Stop -> final acc >> return Stop++    -----------------+    -- Done+    -----------------++    stepOuter _ SplitOnSeqDone = return Stop++    -----------------+    -- Single Pattern+    -----------------++    stepOuter gst (SplitOnSeqSingle fs st pat) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                let jump c = SplitOnSeqSingle c s pat+                if pat == x+                then final fs >>= yieldProceed jump+                else do+                    r <- fstep fs x+                    case r of+                        FL.Partial fs1 -> skip $ jump fs1+                        FL.Done b -> yieldProceed jump b+            Skip s -> return $ Skip $ SplitOnSeqSingle fs s pat+            Stop -> do+                r <- final fs+                return $ Skip $ SplitOnSeqYield r SplitOnSeqDone++    ---------------------------+    -- Short Pattern - Shift Or+    ---------------------------++    stepOuter _ (SplitOnSeqWordDone 0 fs _) = do+        r <- final fs+        skip $ SplitOnSeqYield r SplitOnSeqDone+    stepOuter _ (SplitOnSeqWordDone n fs wrd) = do+        let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))+        r <- fstep fs (toEnum $ fromIntegral old)+        case r of+            FL.Partial fs1 -> skip $ SplitOnSeqWordDone (n - 1) fs1 wrd+            FL.Done b -> do+                 let jump c = SplitOnSeqWordDone (n - 1) c wrd+                 yieldProceed jump b++    stepOuter gst (SplitOnSeqWordInit fs st0) =+        go SPEC 0 0 st0++        where++        {-# INLINE go #-}+        go !_ !idx !wrd !st = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    let wrd1 = addToWord wrd x+                    if idx == maxIndex+                    then do+                        if wrd1 .&. wordMask == wordPat+                        then do+                            let jump c = SplitOnSeqWordInit c s+                            final fs >>= yieldProceed jump+                        else skip $ SplitOnSeqWordLoop wrd1 s fs+                    else go SPEC (idx + 1) wrd1 s+                Skip s -> go SPEC idx wrd s+                Stop -> do+                    if idx /= 0+                    then skip $ SplitOnSeqWordDone idx fs wrd+                    else do+                        r <- final fs+                        skip $ SplitOnSeqYield r SplitOnSeqDone++    stepOuter gst (SplitOnSeqWordLoop wrd0 st0 fs0) =+        go SPEC wrd0 st0 fs0++        where++        {-# INLINE go #-}+        go !_ !wrd !st !fs = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    let jump c = SplitOnSeqWordInit c s+                        wrd1 = addToWord wrd x+                        old = (wordMask .&. wrd)+                                `shiftR` (elemBits * (patLen - 1))+                    r <- fstep fs (toEnum $ fromIntegral old)+                    case r of+                        FL.Partial fs1 -> do+                            if wrd1 .&. wordMask == wordPat+                            then final fs1 >>= yieldProceed jump+                            else go SPEC wrd1 s fs1+                        FL.Done b -> yieldProceed jump b+                Skip s -> go SPEC wrd s fs+                Stop -> skip $ SplitOnSeqWordDone patLen fs wrd++    -------------------------------+    -- General Pattern - Karp Rabin+    -------------------------------++    stepOuter gst (SplitOnSeqKRInit idx fs st rb rh) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                rh1 <- liftIO $ RB.unsafeInsert rb rh x+                if idx == maxIndex+                then do+                    let fld = RB.unsafeFoldRing (RB.ringBound rb)+                    let !ringHash = fld addCksum 0 rb+                    if ringHash == patHash+                    then skip $ SplitOnSeqKRCheck fs s rb rh1+                    else skip $ SplitOnSeqKRLoop fs s rb rh1 ringHash+                else skip $ SplitOnSeqKRInit (idx + 1) fs s rb rh1+            Skip s -> skip $ SplitOnSeqKRInit idx fs s rb rh+            Stop -> do+                skip $ SplitOnSeqKRDone idx fs rb (RB.startOf rb)++    -- XXX The recursive "go" is more efficient than the state based recursion+    -- code commented out below. Perhaps its more efficient because of+    -- factoring out "rb" outside the loop.+    --+    stepOuter gst (SplitOnSeqKRLoop fs0 st0 rb rh0 cksum0) =+        go SPEC fs0 st0 rh0 cksum0++        where++        go !_ !fs !st !rh !cksum = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    old <- liftIO $ peek rh+                    let cksum1 = deltaCksum cksum old x+                    r <- fstep fs old+                    case r of+                        FL.Partial fs1 -> do+                            rh1 <- liftIO (RB.unsafeInsert rb rh x)+                            if cksum1 == patHash+                            then skip $ SplitOnSeqKRCheck fs1 s rb rh1+                            else go SPEC fs1 s rh1 cksum1+                        FL.Done b -> do+                            let rst = RB.startOf rb+                                jump c = SplitOnSeqKRInit 0 c s rb rst+                            yieldProceed jump b+                Skip s -> go SPEC fs s rh cksum+                Stop -> skip $ SplitOnSeqKRDone patLen fs rb rh++    -- XXX The following code is 5 times slower compared to the recursive loop+    -- based code above. Need to investigate why. One possibility is that the+    -- go loop above does not thread around the ring buffer (rb). This code may+    -- be causing the state to bloat and getting allocated on each iteration.+    -- We can check the cmm/asm code to confirm.  If so a good GHC solution to+    -- such problem is needed. One way to avoid this could be to use unboxed+    -- mutable state?+    {-+    stepOuter gst (SplitOnSeqKRLoop fs st rb rh cksum) = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    old <- liftIO $ peek rh+                    let cksum1 = deltaCksum cksum old x+                    fs1 <- fstep fs old+                    if (cksum1 == patHash)+                    then do+                        r <- done fs1+                        skip $ SplitOnSeqYield r $ SplitOnSeqKRInit 0 s rb rh+                    else do+                        rh1 <- liftIO (RB.unsafeInsert rb rh x)+                        skip $ SplitOnSeqKRLoop fs1 s rb rh1 cksum1+                Skip s -> skip $ SplitOnSeqKRLoop fs s rb rh cksum+                Stop -> skip $ SplitOnSeqKRDone patLen fs rb rh+    -}++    stepOuter _ (SplitOnSeqKRCheck fs st rb rh) = do+        if RB.unsafeEqArray rb rh patArr+        then do+            r <- final fs+            let rst = RB.startOf rb+                jump c = SplitOnSeqKRInit 0 c st rb rst+            yieldProceed jump r+        else skip $ SplitOnSeqKRLoop fs st rb rh patHash++    stepOuter _ (SplitOnSeqKRDone 0 fs _ _) = do+        r <- final fs+        skip $ SplitOnSeqYield r SplitOnSeqDone+    stepOuter _ (SplitOnSeqKRDone n fs rb rh) = do+        old <- liftIO $ peek rh+        let rh1 = RB.advance rb rh+        r <- fstep fs old+        case r of+            FL.Partial fs1 -> skip $ SplitOnSeqKRDone (n - 1) fs1 rb rh1+            FL.Done b -> do+                 let jump c = SplitOnSeqKRDone (n - 1) c rb rh1+                 yieldProceed jump b++{-# ANN type SplitOnSuffixSeqState Fuse #-}+data SplitOnSuffixSeqState rb rh ck w fs s b x =+      SplitOnSuffixSeqInit+    | SplitOnSuffixSeqYield b (SplitOnSuffixSeqState rb rh ck w fs s b x)+    | SplitOnSuffixSeqDone++    | SplitOnSuffixSeqEmpty !fs s++    | SplitOnSuffixSeqSingleInit !fs s x+    | SplitOnSuffixSeqSingle !fs s x++    | SplitOnSuffixSeqWordInit !fs s+    | SplitOnSuffixSeqWordLoop !w s !fs+    | SplitOnSuffixSeqWordDone Int !fs !w++    | SplitOnSuffixSeqKRInit Int !fs s rb !rh+    | SplitOnSuffixSeqKRInit1 !fs s rb !rh+    | SplitOnSuffixSeqKRLoop fs s rb !rh !ck+    | SplitOnSuffixSeqKRCheck fs s rb !rh+    | SplitOnSuffixSeqKRDone Int !fs rb !rh++    | SplitOnSuffixSeqReinit+          (fs -> SplitOnSuffixSeqState rb rh ck w fs s b x)++{-# INLINE_NORMAL splitOnSuffixSeq #-}+splitOnSuffixSeq+    :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a)+    => Bool+    -> Array a+    -> Fold m a b+    -> Stream m a+    -> Stream m b+splitOnSuffixSeq withSep patArr (Fold fstep initial _ final) (Stream step state) =+    Stream stepOuter SplitOnSuffixSeqInit++    where++    patLen = A.length patArr+    maxIndex = patLen - 1+    elemBits = SIZE_OF(a) * 8++    -- For word pattern case+    wordMask :: Word+    wordMask = (1 `shiftL` (elemBits * patLen)) - 1++    elemMask :: Word+    elemMask = (1 `shiftL` elemBits) - 1++    wordPat :: Word+    wordPat = wordMask .&. A.foldl' addToWord 0 patArr++    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)++    nextAfterInit nextGen stepRes =+        case stepRes of+            FL.Partial s -> nextGen s+            FL.Done b ->+                SplitOnSuffixSeqYield b (SplitOnSuffixSeqReinit nextGen)++    {-# INLINE yieldProceed #-}+    yieldProceed nextGen fs =+        initial >>= skip . SplitOnSuffixSeqYield fs . nextAfterInit nextGen++    -- For single element pattern case+    {-# INLINE processYieldSingle #-}+    processYieldSingle pat x s fs = do+        let jump c = SplitOnSuffixSeqSingleInit c s pat+        if pat == x+        then do+            r <- if withSep then fstep fs x else return $ FL.Partial fs+            b1 <-+                case r of+                    FL.Partial fs1 -> final fs1+                    FL.Done b -> return b+            yieldProceed jump b1+        else do+            r <- fstep fs x+            case r of+                FL.Partial fs1 -> skip $ SplitOnSuffixSeqSingle fs1 s pat+                FL.Done b -> yieldProceed jump b++    -- For Rabin-Karp search+    k = 2891336453 :: Word32+    coeff = k ^ patLen++    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)++    deltaCksum cksum old new =+        addCksum cksum new - coeff * fromIntegral (fromEnum old)++    -- XXX shall we use a random starting hash or 1 instead of 0?+    patHash = A.foldl' addCksum 0 patArr++    skip = return . Skip++    {-# INLINE_LATE stepOuter #-}+    stepOuter _ SplitOnSuffixSeqInit = do+        res <- initial+        case res of+            FL.Partial fs ->+                if patLen == 0+                then skip $ SplitOnSuffixSeqEmpty fs state+                else if patLen == 1+                     then do+                         pat <- liftIO $ A.unsafeIndexIO 0 patArr+                         skip $ SplitOnSuffixSeqSingleInit fs state pat+                     else if SIZE_OF(a) * patLen+                               <= sizeOf (Proxy :: Proxy Word)+                          then skip $ SplitOnSuffixSeqWordInit fs state+                          else do+                              (rb, rhead) <- liftIO $ RB.new patLen+                              skip $ SplitOnSuffixSeqKRInit 0 fs state rb rhead+            FL.Done fb -> skip $ SplitOnSuffixSeqYield fb SplitOnSuffixSeqInit++    stepOuter _ (SplitOnSuffixSeqYield x next) = return $ Yield x next++    ---------------------------+    -- Reinit+    ---------------------------++    stepOuter _ (SplitOnSuffixSeqReinit nextGen) =+        initial >>= skip . nextAfterInit nextGen++    ---------------------------+    -- Empty pattern+    ---------------------------++    stepOuter gst (SplitOnSuffixSeqEmpty acc st) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> do+                let jump c = SplitOnSuffixSeqEmpty c s+                r <- fstep acc x+                b1 <-+                    case r of+                        FL.Partial fs -> final fs+                        FL.Done b -> return b+                yieldProceed jump b1+            Skip s -> skip (SplitOnSuffixSeqEmpty acc s)+            Stop -> final acc >> return Stop++    -----------------+    -- Done+    -----------------++    stepOuter _ SplitOnSuffixSeqDone = return Stop++    -----------------+    -- Single Pattern+    -----------------++    stepOuter gst (SplitOnSuffixSeqSingleInit fs st pat) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> processYieldSingle pat x s fs+            Skip s -> skip $ SplitOnSuffixSeqSingleInit fs s pat+            Stop -> final fs >> return Stop++    stepOuter gst (SplitOnSuffixSeqSingle fs st pat) = do+        res <- step (adaptState gst) st+        case res of+            Yield x s -> processYieldSingle pat x s fs+            Skip s -> skip $ SplitOnSuffixSeqSingle fs s pat+            Stop -> do+                r <- final fs+                skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone++    ---------------------------+    -- Short Pattern - Shift Or+    ---------------------------++    stepOuter _ (SplitOnSuffixSeqWordDone 0 fs _) = do+        r <- final fs+        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone+    stepOuter _ (SplitOnSuffixSeqWordDone n fs wrd) = do+        let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))+        r <- fstep fs (toEnum $ fromIntegral old)+        case r of+            FL.Partial fs1 -> skip $ SplitOnSuffixSeqWordDone (n - 1) fs1 wrd+            FL.Done b -> do+                let jump c = SplitOnSuffixSeqWordDone (n - 1) c wrd+                yieldProceed jump b++    stepOuter gst (SplitOnSuffixSeqWordInit fs0 st0) = do+        res <- step (adaptState gst) st0+        case res of+            Yield x s -> do+                let wrd = addToWord 0 x+                r <- if withSep then fstep fs0 x else return $ FL.Partial fs0+                case r of+                    FL.Partial fs1 -> go SPEC 1 wrd s fs1+                    FL.Done b -> do+                        let jump c = SplitOnSuffixSeqWordInit c s+                        yieldProceed jump b+            Skip s -> skip (SplitOnSuffixSeqWordInit fs0 s)+            Stop -> final fs0 >> return Stop++        where++        {-# INLINE go #-}+        go !_ !idx !wrd !st !fs = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    let jump c = SplitOnSuffixSeqWordInit c s+                    let wrd1 = addToWord wrd x+                    r <- if withSep then fstep fs x else return $ FL.Partial fs+                    case r of+                        FL.Partial fs1 ->+                            if idx /= maxIndex+                            then go SPEC (idx + 1) wrd1 s fs1+                            else if wrd1 .&. wordMask /= wordPat+                            then skip $ SplitOnSuffixSeqWordLoop wrd1 s fs1+                            else do final fs >>= yieldProceed jump+                        FL.Done b -> yieldProceed jump b+                Skip s -> go SPEC idx wrd s fs+                Stop -> skip $ SplitOnSuffixSeqWordDone idx fs wrd++    stepOuter gst (SplitOnSuffixSeqWordLoop wrd0 st0 fs0) =+        go SPEC wrd0 st0 fs0++        where++        {-# INLINE go #-}+        go !_ !wrd !st !fs = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    let jump c = SplitOnSuffixSeqWordInit c s+                        wrd1 = addToWord wrd x+                        old = (wordMask .&. wrd)+                                `shiftR` (elemBits * (patLen - 1))+                    r <-+                        if withSep+                        then fstep fs x+                        else fstep fs (toEnum $ fromIntegral old)+                    case r of+                        FL.Partial fs1 ->+                            if wrd1 .&. wordMask == wordPat+                            then final fs1 >>= yieldProceed jump+                            else go SPEC wrd1 s fs1+                        FL.Done b -> yieldProceed jump b+                Skip s -> go SPEC wrd s fs+                Stop ->+                    if wrd .&. wordMask == wordPat+                    then final fs >> return Stop+                    else if withSep+                    then do+                        r <- final fs+                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone+                    else skip $ SplitOnSuffixSeqWordDone patLen fs wrd++    -------------------------------+    -- General Pattern - Karp Rabin+    -------------------------------++    stepOuter gst (SplitOnSuffixSeqKRInit idx0 fs st0 rb rh0) = do+        res <- step (adaptState gst) st0+        case res of+            Yield x s -> do+                rh1 <- liftIO $ RB.unsafeInsert rb rh0 x+                r <- if withSep then fstep fs x else return $ FL.Partial fs+                case r of+                    FL.Partial fs1 ->+                        skip $ SplitOnSuffixSeqKRInit1 fs1 s rb rh1+                    FL.Done b -> do+                        let rst = RB.startOf rb+                            jump c = SplitOnSuffixSeqKRInit 0 c s rb rst+                        yieldProceed jump b+            Skip s -> skip $ SplitOnSuffixSeqKRInit idx0 fs s rb rh0+            Stop -> final fs >> return Stop++    stepOuter gst (SplitOnSuffixSeqKRInit1 fs0 st0 rb rh0) = do+        go SPEC 1 rh0 st0 fs0++        where++        go !_ !idx !rh st !fs = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    rh1 <- liftIO (RB.unsafeInsert rb rh x)+                    r <- if withSep then fstep fs x else return $ FL.Partial fs+                    case r of+                        FL.Partial fs1 ->+                            if idx /= maxIndex+                            then go SPEC (idx + 1) rh1 s fs1+                            else skip $+                                let fld = RB.unsafeFoldRing (RB.ringBound rb)+                                    !ringHash = fld addCksum 0 rb+                                 in if ringHash == patHash+                                    then SplitOnSuffixSeqKRCheck fs1 s rb rh1+                                    else SplitOnSuffixSeqKRLoop+                                            fs1 s rb rh1 ringHash+                        FL.Done b -> do+                            let rst = RB.startOf rb+                                jump c = SplitOnSuffixSeqKRInit 0 c s rb rst+                            yieldProceed jump b+                Skip s -> go SPEC idx rh s fs+                Stop -> do+                    -- do not issue a blank segment when we end at pattern+                    if (idx == maxIndex) && RB.unsafeEqArray rb rh patArr+                    then final fs >> return Stop+                    else if withSep+                    then do+                        r <- final fs+                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone+                    else skip $ SplitOnSuffixSeqKRDone idx fs rb (RB.startOf rb)++    stepOuter gst (SplitOnSuffixSeqKRLoop fs0 st0 rb rh0 cksum0) =+        go SPEC fs0 st0 rh0 cksum0++        where++        go !_ !fs !st !rh !cksum = do+            res <- step (adaptState gst) st+            case res of+                Yield x s -> do+                    old <- liftIO $ peek rh+                    rh1 <- liftIO (RB.unsafeInsert rb rh x)+                    let cksum1 = deltaCksum cksum old x+                    r <- if withSep then fstep fs x else fstep fs old+                    case r of+                        FL.Partial fs1 ->+                            if cksum1 /= patHash+                            then go SPEC fs1 s rh1 cksum1+                            else skip $ SplitOnSuffixSeqKRCheck fs1 s rb rh1+                        FL.Done b -> do+                            let rst = RB.startOf rb+                                jump c = SplitOnSuffixSeqKRInit 0 c s rb rst+                            yieldProceed jump b+                Skip s -> go SPEC fs s rh cksum+                Stop ->+                    if RB.unsafeEqArray rb rh patArr+                    then final fs >> return Stop+                    else if withSep+                    then do+                        r <- final fs+                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone+                    else skip $ SplitOnSuffixSeqKRDone patLen fs rb rh++    stepOuter _ (SplitOnSuffixSeqKRCheck fs st rb rh) = do+        if RB.unsafeEqArray rb rh patArr+        then do+            r <- final fs+            let rst = RB.startOf rb+                jump c = SplitOnSuffixSeqKRInit 0 c st rb rst+            yieldProceed jump r+        else skip $ SplitOnSuffixSeqKRLoop fs st rb rh patHash++    stepOuter _ (SplitOnSuffixSeqKRDone 0 fs _ _) = do+        r <- final fs+        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone+    stepOuter _ (SplitOnSuffixSeqKRDone n fs rb rh) = do+        old <- liftIO $ peek rh+        let rh1 = RB.advance rb rh+        r <- fstep fs old+        case r of+            FL.Partial fs1 -> skip $ SplitOnSuffixSeqKRDone (n - 1) fs1 rb rh1+            FL.Done b -> do+                let jump c = SplitOnSuffixSeqKRDone (n - 1) c rb rh1+                yieldProceed jump b++-- Implement this as a fold or a parser instead.+-- This can be implemented easily using Rabin Karp+-- | Split post any one of the given patterns.+--+-- /Unimplemented/+{-# INLINE splitOnSuffixSeqAny #-}+splitOnSuffixSeqAny :: -- (Monad m, Unboxed a, Integral a) =>+    [Array a] -> Fold m a b -> Stream m a -> Stream m b+splitOnSuffixSeqAny _subseq _f _m = undefined+    -- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)++-- | Split on a prefixed separator element, dropping the separator.  The+-- supplied 'Fold' is applied on the split segments.+--+-- @+-- > splitOnPrefix' p xs = Stream.toList $ Stream.splitOnPrefix p (Fold.toList) (Stream.fromList xs)+-- > splitOnPrefix' (== '.') ".a.b"+-- ["a","b"]+-- @+--+-- An empty stream results in an empty output stream:+-- @+-- > splitOnPrefix' (== '.') ""+-- []+-- @+--+-- An empty segment consisting of only a prefix is folded to the default output+-- of the fold:+--+-- @+-- > splitOnPrefix' (== '.') "."+-- [""]+--+-- > splitOnPrefix' (== '.') ".a.b."+-- ["a","b",""]+--+-- > splitOnPrefix' (== '.') ".a..b"+-- ["a","","b"]+--+-- @+--+-- A prefix is optional at the beginning of the stream:+--+-- @+-- > splitOnPrefix' (== '.') "a"+-- ["a"]+--+-- > splitOnPrefix' (== '.') "a.b"+-- ["a","b"]+-- @+--+-- 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:+--+-- > Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id+--+-- Assuming the input stream does not contain the separator:+--+-- > Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList === id+--+-- /Unimplemented/+{-# INLINE splitOnPrefix #-}+splitOnPrefix :: -- (IsStream t, MonadCatch m) =>+    (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+splitOnPrefix _predicate _f = undefined+    -- parseMany (Parser.sliceBeginBy predicate f)++-- Int list examples for splitOn:+--+-- >>> splitList [] [1,2,3,3,4]+-- > [[1],[2],[3],[3],[4]]+--+-- >>> splitList [5] [1,2,3,3,4]+-- > [[1,2,3,3,4]]+--+-- >>> splitList [1] [1,2,3,3,4]+-- > [[],[2,3,3,4]]+--+-- >>> splitList [4] [1,2,3,3,4]+-- > [[1,2,3,3],[]]+--+-- >>> splitList [2] [1,2,3,3,4]+-- > [[1],[3,3,4]]+--+-- >>> splitList [3] [1,2,3,3,4]+-- > [[1,2],[],[4]]+--+-- >>> splitList [3,3] [1,2,3,3,4]+-- > [[1,2],[4]]+--+-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]+-- > [[],[]]++-- This can be implemented easily using Rabin Karp+-- | Split on any one of the given patterns.+--+-- /Unimplemented/+--+{-# INLINE splitOnAny #-}+splitOnAny :: -- (Monad m, Unboxed a, Integral a) =>+    [Array a] -> Fold m a b -> Stream m a -> Stream m b+splitOnAny _subseq _f _m =+    undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)++------------------------------------------------------------------------------+-- Nested Container Transformation+------------------------------------------------------------------------------++{-# ANN type SplitState Fuse #-}+data SplitState s arr+    = SplitInitial s+    | SplitBuffering s arr+    | SplitSplitting s arr+    | SplitYielding arr (SplitState s arr)+    | SplitFinishing++-- XXX An alternative approach would be to use a partial fold (Fold m a b) to+-- split using a splitBy like combinator. The Fold would consume upto the+-- separator and return any leftover which can then be fed to the next fold.+--+-- We can revisit this once we have partial folds/parsers.+--+-- | Performs infix separator style splitting.+{-# INLINE_NORMAL splitInnerBy #-}+splitInnerBy+    :: Monad m+    => (f a -> m (f a, Maybe (f a)))  -- splitter+    -> (f a -> f a -> m (f a))        -- joiner+    -> Stream m (f a)+    -> Stream m (f a)+splitInnerBy splitter joiner (Stream step1 state1) =+    Stream step (SplitInitial state1)++    where++    {-# INLINE_LATE step #-}+    step gst (SplitInitial st) = do+        r <- step1 gst st+        case r of+            Yield x s -> do+                (x1, mx2) <- splitter x+                return $ case mx2 of+                    Nothing -> Skip (SplitBuffering s x1)+                    Just x2 -> Skip (SplitYielding x1 (SplitSplitting s x2))+            Skip s -> return $ Skip (SplitInitial s)+            Stop -> return Stop++    step gst (SplitBuffering st buf) = do+        r <- step1 gst st+        case r of+            Yield x s -> do+                (x1, mx2) <- splitter x+                buf' <- joiner buf x1+                return $ case mx2 of+                    Nothing -> Skip (SplitBuffering s buf')+                    Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))+            Skip s -> return $ Skip (SplitBuffering s buf)+            Stop -> return $ Skip (SplitYielding buf SplitFinishing)++    step _ (SplitSplitting st buf) = do+        (x1, mx2) <- splitter buf+        return $ case mx2 of+                Nothing -> Skip $ SplitBuffering st x1+                Just x2 -> Skip $ SplitYielding x1 (SplitSplitting st x2)++    step _ (SplitYielding x next) = return $ Yield x next+    step _ SplitFinishing = return Stop++-- | Performs infix separator style splitting.+{-# INLINE_NORMAL splitInnerBySuffix #-}+splitInnerBySuffix+    :: (Monad m, Eq (f a), Monoid (f a))+    => (f a -> m (f a, Maybe (f a)))  -- splitter+    -> (f a -> f a -> m (f a))        -- joiner+    -> Stream m (f a)+    -> Stream m (f a)+splitInnerBySuffix splitter joiner (Stream step1 state1) =+    Stream step (SplitInitial state1)++    where++    {-# INLINE_LATE step #-}+    step gst (SplitInitial st) = do+        r <- step1 gst st+        case r of+            Yield x s -> do+                (x1, mx2) <- splitter x+                return $ case mx2 of+                    Nothing -> Skip (SplitBuffering s x1)+                    Just x2 -> Skip (SplitYielding x1 (SplitSplitting s x2))+            Skip s -> return $ Skip (SplitInitial s)+            Stop -> return Stop++    step gst (SplitBuffering st buf) = do+        r <- step1 gst st+        case r of+            Yield x s -> do+                (x1, mx2) <- splitter x+                buf' <- joiner buf x1+                return $ case mx2 of+                    Nothing -> Skip (SplitBuffering s buf')+                    Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))+            Skip s -> return $ Skip (SplitBuffering s buf)+            Stop -> return $+                if buf == mempty+                then Stop+                else Skip (SplitYielding buf SplitFinishing)++    step _ (SplitSplitting st buf) = do+        (x1, mx2) <- splitter buf+        return $ case mx2 of+                Nothing -> Skip $ SplitBuffering st x1+                Just x2 -> Skip $ SplitYielding x1 (SplitSplitting st x2)++    step _ (SplitYielding x next) = return $ Yield x next+    step _ SplitFinishing = return Stop++------------------------------------------------------------------------------+-- Trimming+------------------------------------------------------------------------------++-- | Drop prefix from the input stream if present.+--+-- Space: @O(1)@+--+-- /Unimplemented/+{-# INLINE dropPrefix #-}+dropPrefix ::+    -- (Monad m, Eq a) =>+    Stream m a -> Stream m a -> Stream m a+dropPrefix = error "Not implemented yet!"++-- | Drop all matching infix from the input stream if present. Infix stream+-- may be consumed multiple times.+--+-- Space: @O(n)@ where n is the length of the infix.+--+-- /Unimplemented/+{-# INLINE dropInfix #-}+dropInfix ::+    -- (Monad m, Eq a) =>+    Stream m a -> Stream m a -> Stream m a+dropInfix = error "Not implemented yet!"++-- | Drop suffix from the input stream if present. Suffix stream may be+-- consumed multiple times.+--+-- Space: @O(n)@ where n is the length of the suffix.+--+-- /Unimplemented/+{-# INLINE dropSuffix #-}+dropSuffix ::+    -- (Monad m, Eq a) =>+    Stream m a -> Stream m a -> Stream m a+dropSuffix = error "Not implemented yet!"
− src/Streamly/Internal/Data/Stream/Reduce.hs
@@ -1,444 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.Reduce--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Reduce streams by streams, folds or parsers.--module Streamly.Internal.Data.Stream.Reduce-    (-    -- * Reduce By Streams-      dropPrefix-    , dropInfix-    , dropSuffix--    -- * Reduce By Folds-    -- |-    -- Reduce a stream by folding or parsing chunks of the stream.  Functions-    -- generally ending in these shapes:-    ---    -- @-    -- f (Fold m a b) -> Stream m a -> Stream m b-    -- f (Parser a m b) -> Stream m a -> Stream m b-    -- @--    -- ** Generic Folding-    -- | Apply folds on a stream.-    , foldMany-    , foldManyPost-    , refoldMany-    , foldSequence-    , foldIterateM-    , refoldIterateM-    , reduceIterateBfs--    -- ** Chunking-    -- | Element unaware grouping.-    , chunksOf--    -- ** Splitting-    -- XXX Implement these as folds or parsers instead.-    , splitOnSuffixSeqAny-    , splitOnPrefix-    , splitOnAny--    -- * Reduce By Parsers-    -- ** Generic Parsing-    -- | Apply parsers on a stream.-    , parseMany-    , parseManyD-    , parseManyTill-    , parseSequence-    , parseIterate--    )-where--import Control.Monad.IO.Class (MonadIO(..))-import Streamly.Internal.Data.Array.Type (Array)-import Streamly.Internal.Data.Fold.Type (Fold (..))-import Streamly.Internal.Data.Parser (Parser (..))-import Streamly.Internal.Data.Parser.ParserD (ParseError)-import Streamly.Internal.Data.Refold.Type (Refold (..))-import Streamly.Internal.Data.Stream.Bottom (foldManyPost)-import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)-import Streamly.Internal.Data.Unboxed (Unbox)--import qualified Streamly.Internal.Data.Array.Type as Array-import qualified Streamly.Internal.Data.Parser.ParserD as ParserD-import qualified Streamly.Internal.Data.Stream.StreamD as D--import Prelude hiding (concatMap, map)---- $setup--- >>> :m--- >>> import Prelude hiding (zipWith, concatMap, concat)--- >>> import Streamly.Internal.Data.Stream as Stream--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Parser as Parser--- >>> import qualified Streamly.Data.Array as Array----------------------------------------------------------------------------------- Trimming----------------------------------------------------------------------------------- | Drop prefix from the input stream if present.------ Space: @O(1)@------ /Unimplemented/-{-# INLINE dropPrefix #-}-dropPrefix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropPrefix = error "Not implemented yet!"---- | Drop all matching infix from the input stream if present. Infix stream--- may be consumed multiple times.------ Space: @O(n)@ where n is the length of the infix.------ /Unimplemented/-{-# INLINE dropInfix #-}-dropInfix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropInfix = error "Not implemented yet!"---- | Drop suffix from the input stream if present. Suffix stream may be--- consumed multiple times.------ Space: @O(n)@ where n is the length of the suffix.------ /Unimplemented/-{-# INLINE dropSuffix #-}-dropSuffix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropSuffix = error "Not implemented yet!"----------------------------------------------------------------------------------- Folding----------------------------------------------------------------------------------- | Apply a 'Fold' repeatedly on a stream and emit the results in the--- output stream. Unlike 'foldManyPost' it evaluates the fold after the stream,--- therefore, an empty input stream results in an empty output stream.------ Definition:------ >>> foldMany f = Stream.parseMany (Parser.fromFold f)------ Example, empty stream:------ >>> f = Fold.take 2 Fold.sum--- >>> fmany = Stream.fold Fold.toList . Stream.foldMany f--- >>> fmany $ Stream.fromList []--- []------ Example, last fold empty:------ >>> fmany $ Stream.fromList [1..4]--- [3,7]------ Example, last fold non-empty:------ >>> fmany $ Stream.fromList [1..5]--- [3,7,5]------ Note that using a closed fold e.g. @Fold.take 0@, would result in an--- infinite stream on a non-empty input stream.----{-# INLINE foldMany #-}-foldMany-    :: Monad m-    => Fold m a b-    -> Stream m a-    -> Stream m b-foldMany f m = fromStreamD $ D.foldMany f (toStreamD m)---- | Like 'foldMany' but using the 'Refold' type instead of 'Fold'.------ /Pre-release/-{-# INLINE refoldMany #-}-refoldMany :: Monad m =>-    Refold m c a b -> m c -> Stream m a -> Stream m b-refoldMany f action = fromStreamD . D.refoldMany f action . toStreamD---- | Apply a stream of folds to an input stream and emit the results in the--- output stream.------ /Unimplemented/----{-# INLINE foldSequence #-}-foldSequence-       :: -- Monad m =>-       Stream m (Fold m a b)-    -> Stream m a-    -> Stream m b-foldSequence _f _m = undefined---- | Iterate a fold generator on a stream. The initial value @b@ is used to--- generate the first fold, the fold is applied on the stream and the result of--- the fold is used to generate the next fold and so on.------ >>> import Data.Monoid (Sum(..))--- >>> f x = return (Fold.take 2 (Fold.sconcat x))--- >>> s = fmap Sum $ Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s--- [3,10,21,36,55,55]------ This is the streaming equivalent of monad like sequenced application of--- folds where next fold is dependent on the previous fold.------ /Pre-release/----{-# INLINE foldIterateM #-}-foldIterateM ::-       Monad m => (b -> m (Fold m a b)) -> m b -> Stream m a -> Stream m b-foldIterateM f i m = fromStreamD $ D.foldIterateM f i (toStreamD m)---- | Like 'foldIterateM' but using the 'Refold' type instead. This could be--- much more efficient due to stream fusion.------ /Internal/-{-# INLINE refoldIterateM #-}-refoldIterateM :: Monad m =>-    Refold m b a b -> m b -> Stream m a -> Stream m b-refoldIterateM c i m = fromStreamD $ D.refoldIterateM c i (toStreamD m)---- | Binary BFS style reduce, folds a level entirely using the supplied fold--- function, collecting the outputs as next level of the tree, then repeats the--- same process on the next level. The last elements of a previously folded--- level are folded first.-{-# INLINE reduceIterateBfs #-}-reduceIterateBfs :: Monad m =>-    (a -> a -> m a) -> Stream m a -> m (Maybe a)-reduceIterateBfs f stream = D.reduceIterateBfs f (toStreamD stream)----------------------------------------------------------------------------------- Splitting----------------------------------------------------------------------------------- Implement this as a fold or a parser instead.--- This can be implemented easily using Rabin Karp--- | Split post any one of the given patterns.------ /Unimplemented/-{-# INLINE splitOnSuffixSeqAny #-}-splitOnSuffixSeqAny :: -- (Monad m, Unboxed a, Integral a) =>-    [Array a] -> Fold m a b -> Stream m a -> Stream m b-splitOnSuffixSeqAny _subseq _f _m = undefined-    -- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)---- | Split on a prefixed separator element, dropping the separator.  The--- supplied 'Fold' is applied on the split segments.------ @--- > splitOnPrefix' p xs = Stream.toList $ Stream.splitOnPrefix p (Fold.toList) (Stream.fromList xs)--- > splitOnPrefix' (== '.') ".a.b"--- ["a","b"]--- @------ An empty stream results in an empty output stream:--- @--- > splitOnPrefix' (== '.') ""--- []--- @------ An empty segment consisting of only a prefix is folded to the default output--- of the fold:------ @--- > splitOnPrefix' (== '.') "."--- [""]------ > splitOnPrefix' (== '.') ".a.b."--- ["a","b",""]------ > splitOnPrefix' (== '.') ".a..b"--- ["a","","b"]------ @------ A prefix is optional at the beginning of the stream:------ @--- > splitOnPrefix' (== '.') "a"--- ["a"]------ > splitOnPrefix' (== '.') "a.b"--- ["a","b"]--- @------ 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:------ > Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id------ Assuming the input stream does not contain the separator:------ > Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList === id------ /Unimplemented/-{-# INLINE splitOnPrefix #-}-splitOnPrefix :: -- (IsStream t, MonadCatch m) =>-    (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b-splitOnPrefix _predicate _f = undefined-    -- parseMany (Parser.sliceBeginBy predicate f)---- Int list examples for splitOn:------ >>> splitList [] [1,2,3,3,4]--- > [[1],[2],[3],[3],[4]]------ >>> splitList [5] [1,2,3,3,4]--- > [[1,2,3,3,4]]------ >>> splitList [1] [1,2,3,3,4]--- > [[],[2,3,3,4]]------ >>> splitList [4] [1,2,3,3,4]--- > [[1,2,3,3],[]]------ >>> splitList [2] [1,2,3,3,4]--- > [[1],[3,3,4]]------ >>> splitList [3] [1,2,3,3,4]--- > [[1,2],[],[4]]------ >>> splitList [3,3] [1,2,3,3,4]--- > [[1,2],[4]]------ >>> splitList [1,2,3,3,4] [1,2,3,3,4]--- > [[],[]]---- This can be implemented easily using Rabin Karp--- | Split on any one of the given patterns.------ /Unimplemented/----{-# INLINE splitOnAny #-}-splitOnAny :: -- (Monad m, Unboxed a, Integral a) =>-    [Array a] -> Fold m a b -> Stream m a -> Stream m b-splitOnAny _subseq _f _m =-    undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)----------------------------------------------------------------------------------- Parsing----------------------------------------------------------------------------------- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the--- output stream.------ Example:------ >>> s = Stream.fromList [1..10]--- >>> parser = Parser.takeBetween 0 2 Fold.sum--- >>> Stream.fold Fold.toList $ Stream.parseMany parser s--- [Right 3,Right 7,Right 11,Right 15,Right 19]------ This is the streaming equivalent of the 'Streamly.Data.Parser.many' parse--- combinator.------ Known Issues: When the parser fails there is no way to get the remaining--- stream.----{-# INLINE parseMany #-}-parseMany-    :: Monad m-    => Parser a m b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseMany p m =-    fromStreamD $ D.parseManyD p (toStreamD m)---- | Same as parseMany but for StreamD streams.------ /Internal/----{-# INLINE parseManyD #-}-parseManyD-    :: Monad m-    => ParserD.Parser a m b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseManyD p m =-    fromStreamD $ D.parseManyD p (toStreamD m)---- | Apply a stream of parsers to an input stream and emit the results in the--- output stream.------ /Unimplemented/----{-# INLINE parseSequence #-}-parseSequence-       :: -- Monad m =>-       Stream m (Parser a m b)-    -> Stream m a-    -> Stream m b-parseSequence _f _m = undefined---- XXX Change the parser arguments' order---- | @parseManyTill collect test stream@ tries the parser @test@ on the input,--- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds--- @test@ is tried again and so on. The parser stops when @test@ succeeds.  The--- output of @test@ is discarded and the output of @collect@ is emitted in the--- output stream. The parser fails if @collect@ fails.------ /Unimplemented/----{-# INLINE parseManyTill #-}-parseManyTill ::-    -- MonadThrow m =>-       Parser a m b-    -> Parser a m x-    -> t m a-    -> t m b-parseManyTill = undefined---- | Iterate a parser generating function on a stream. The initial value @b@ is--- used to generate the first parser, the parser is applied on the stream and--- the result is used to generate the next parser and so on.------ >>> import Data.Monoid (Sum(..))--- >>> s = Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.catRights $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) (Sum 0) $ fmap Sum s--- [3,10,21,36,55,55]------ This is the streaming equivalent of monad like sequenced application of--- parsers where next parser is dependent on the previous parser.------ /Pre-release/----{-# INLINE parseIterate #-}-parseIterate-    :: Monad m-    => (b -> Parser a m b)-    -> b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseIterate f i m = fromStreamD $-    D.parseIterateD f i (toStreamD m)----------------------------------------------------------------------------------- Chunking----------------------------------------------------------------------------------- | @chunksOf n stream@ groups the elements in the input stream into arrays of--- @n@ elements each.------ Same as the following but may be more efficient:------ >>> chunksOf n = Stream.foldMany (Array.writeN n)------ /Pre-release/-{-# INLINE chunksOf #-}-chunksOf :: (MonadIO m, Unbox a)-    => Int -> Stream m a -> Stream m (Array a)-chunksOf n = fromStreamD . Array.chunksOf n . toStreamD
+ src/Streamly/Internal/Data/Stream/Step.hs view
@@ -0,0 +1,39 @@+-- |+-- Module      : Streamly.Internal.Data.Stream.Step+-- Copyright   : (c) 2018 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC++module Streamly.Internal.Data.Stream.Step+    (+    -- * The stream type+      Step (..)+    )+where++import Fusion.Plugin.Types (Fuse(..))++-- | A stream is a succession of 'Step's. A 'Yield' produces a single value and+-- the next state of the stream. 'Stop' indicates there are no more values in+-- the stream.+{-# ANN type Step Fuse #-}+data Step s a = Yield a s | Skip s | Stop++instance Functor (Step s) where+    {-# INLINE fmap #-}+    fmap f (Yield x s) = Yield (f x) s+    fmap _ (Skip s) = Skip s+    fmap _ Stop = Stop++{-+fromPure :: Monad m => a -> s -> m (Step s a)+fromPure a = return . Yield a++skip :: Monad m => s -> m (Step s a)+skip = return . Skip++stop :: Monad m => m (Step s a)+stop = return Stop+-}
src/Streamly/Internal/Data/Stream/StreamD.hs view
@@ -5,38 +5,12 @@ -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC------ Direct style re-implementation of CPS stream in--- "Streamly.Internal.Data.Stream.StreamK".  The symbol or suffix 'D' in this--- module denotes the "Direct" style.  GHC is able to INLINE and fuse direct--- style better, providing better performance than CPS implementation.------ @--- import qualified Streamly.Internal.Data.Stream.StreamD as D--- @  module Streamly.Internal.Data.Stream.StreamD+{-# DEPRECATED "Please use \"Streamly.Internal.Data.Stream\" instead." #-}     (-      module Streamly.Internal.Data.Stream.StreamD.Type-    , module Streamly.Internal.Data.Stream.StreamD.Generate-    , module Streamly.Internal.Data.Stream.StreamD.Eliminate-    , module Streamly.Internal.Data.Stream.StreamD.Exception-    , module Streamly.Internal.Data.Stream.StreamD.Lift-    , module Streamly.Internal.Data.Stream.StreamD.Transformer-    , module Streamly.Internal.Data.Stream.StreamD.Nesting-    , module Streamly.Internal.Data.Stream.StreamD.Transform-    , module Streamly.Internal.Data.Stream.StreamD.Top-    , module Streamly.Internal.Data.Stream.StreamD.Container+      module Streamly.Internal.Data.Stream     ) where -import Streamly.Internal.Data.Stream.StreamD.Type-import Streamly.Internal.Data.Stream.StreamD.Generate-import Streamly.Internal.Data.Stream.StreamD.Eliminate-import Streamly.Internal.Data.Stream.StreamD.Exception-import Streamly.Internal.Data.Stream.StreamD.Lift-import Streamly.Internal.Data.Stream.StreamD.Transformer-import Streamly.Internal.Data.Stream.StreamD.Nesting-import Streamly.Internal.Data.Stream.StreamD.Transform-import Streamly.Internal.Data.Stream.StreamD.Top-import Streamly.Internal.Data.Stream.StreamD.Container+import Streamly.Internal.Data.Stream
− src/Streamly/Internal/Data/Stream/StreamD/Container.hs
@@ -1,302 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Container--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Stream operations that require transformers or containers like Set or Map.--module Streamly.Internal.Data.Stream.StreamD.Container-    (-      nub--    -- * Joins for unconstrained types-    , joinLeftGeneric-    , joinOuterGeneric--    -- * Joins with Ord constraint-    , joinInner-    , joinLeft-    , joinOuter-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO)-import Control.Monad.Trans.State.Strict (get, put)-import Data.Function ((&))-import Data.Maybe (isJust)-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))-import Streamly.Internal.Data.Stream.StreamD.Type-    (Stream(..), mkCross, unCross)--import qualified Data.Map.Strict as Map-import qualified Data.Set as Set-import qualified Streamly.Data.Fold as Fold-import qualified Streamly.Internal.Data.Array.Generic as Array-import qualified Streamly.Internal.Data.Array.Mut.Type as MA-import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Generate as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Transformer as Stream--#include "DocTestDataStream.hs"---- | The memory used is proportional to the number of unique elements in the--- stream. If we want to limit the memory we can just use "take" to limit the--- uniq elements in the stream.-{-# INLINE_NORMAL nub #-}-nub :: (Monad m, Ord a) => Stream m a -> Stream m a-nub (Stream step1 state1) = Stream step (Set.empty, state1)--    where--    step gst (set, st) = do-        r <- step1 gst st-        return-            $ case r of-                Yield x s ->-                    if Set.member x set-                    then Skip (set, s)-                    else Yield x (Set.insert x set, s)-                Skip s -> Skip (set, s)-                Stop -> Stop---- XXX Generate error if a duplicate insertion is attempted?-toMap ::  (Monad m, Ord k) => Stream m (k, v) -> m (Map.Map k v)-toMap =-    let f = Fold.foldl' (\kv (k, b) -> Map.insert k b kv) Map.empty-     in Stream.fold f---- If the second stream is too big it can be partitioned based on hashes and--- then we can process one parition at a time.------ XXX An IntMap may be faster when the keys are Int.--- XXX Use hashmap instead of map?------ | Like 'joinInner' but uses a 'Map' for efficiency.------ If the input streams have duplicate keys, the behavior is undefined.------ For space efficiency use the smaller stream as the second stream.------ Space: O(n)------ Time: O(m + n)------ /Pre-release/-{-# INLINE joinInner #-}-joinInner :: (Monad m, Ord k) =>-    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)-joinInner s1 s2 =-    Stream.concatEffect $ do-        km <- toMap s2-        pure $ Stream.mapMaybe (joinAB km) s1--    where--    joinAB kvm (k, a) =-        case k `Map.lookup` kvm of-            Just b -> Just (k, a, b)-            Nothing -> Nothing---- XXX We can do this concurrently.--- XXX If the second stream is sorted and passed as an Array or a seek capable--- stream then we could use binary search if we have an Ord instance or--- Ordering returning function. The time complexity would then become (m x log--- n).---- XXX Check performance of StreamD vs StreamK---- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s--- that are not equal to any @b@ emit @(a, Nothing)@.------ The second stream is evaluated multiple times. If the stream is a--- consume-once stream then the caller should cache it in an 'Data.Array.Array'--- before calling this function. Caching may also improve performance if the--- stream is expensive to evaluate.------ >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)------ Space: O(n) assuming the second stream is cached in memory.------ Time: O(m x n)------ /Unimplemented/-{-# INLINE joinLeftGeneric #-}-joinLeftGeneric :: Monad m =>-    (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)-joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do-    a <- mkCross (Stream.liftInner s1)-    -- XXX should we use StreamD monad here?-    -- XXX Is there a better way to perform some action at the end of a loop-    -- iteration?-    mkCross (Stream.fromEffect $ put False)-    let final = Stream.concatEffect $ do-            r <- get-            if r-            then pure Stream.nil-            else pure (Stream.fromPure Nothing)-    b <- mkCross (fmap Just (Stream.liftInner s2) `Stream.append` final)-    case b of-        Just b1 ->-            if a `eq` b1-            then do-                mkCross (Stream.fromEffect $ put True)-                return (a, Just b1)-            else mkCross Stream.nil-        Nothing -> return (a, Nothing)---- XXX rename to joinLeftOrd?---- | A more efficient 'joinLeft' using a hashmap for efficiency.------ Space: O(n)------ Time: O(m + n)------ /Pre-release/-{-# INLINE joinLeft #-}-joinLeft :: (Ord k, Monad m) =>-    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)-joinLeft s1 s2 =-    Stream.concatEffect $ do-        km <- toMap s2-        return $ fmap (joinAB km) s1--            where--            joinAB km (k, a) =-                case k `Map.lookup` km of-                    Just b -> (k, a, Just b)-                    Nothing -> (k, a, Nothing)---- XXX We can do this concurrently.---- XXX Check performance of StreamD vs StreamK---- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those--- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but--- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,--- Just b)@.------ For space efficiency use the smaller stream as the second stream.------ Space: O(n)------ Time: O(m x n)------ /Pre-release/-{-# INLINE joinOuterGeneric #-}-joinOuterGeneric :: MonadIO m =>-       (a -> b -> Bool)-    -> Stream m a-    -> Stream m b-    -> Stream m (Maybe a, Maybe b)-joinOuterGeneric eq s1 s =-    Stream.concatEffect $ do-        inputArr <- Array.fromStream s-        let len = Array.length inputArr-        foundArr <--            Stream.fold-            (MA.writeN len)-            (Stream.fromList (Prelude.replicate len False))-        return $ go inputArr foundArr `Stream.append` leftOver inputArr foundArr--    where--    leftOver inputArr foundArr =-            let stream1 = Array.read inputArr-                stream2 = Stream.unfold MA.reader foundArr-            in Stream.filter-                    isJust-                    ( Stream.zipWith (\x y ->-                        if y-                        then Nothing-                        else Just (Nothing, Just x)-                        ) stream1 stream2-                    ) & Stream.catMaybes--    evalState = Stream.evalStateT (return False) . unCross--    go inputArr foundArr = evalState $ do-        a <- mkCross (Stream.liftInner s1)-        -- XXX should we use StreamD monad here?-        -- XXX Is there a better way to perform some action at the end of a loop-        -- iteration?-        mkCross (Stream.fromEffect $ put False)-        let final = Stream.concatEffect $ do-                r <- get-                if r-                then pure Stream.nil-                else pure (Stream.fromPure Nothing)-        (i, b) <--            let stream = Array.read inputArr-             in mkCross-                (Stream.indexed $ fmap Just (Stream.liftInner stream) `Stream.append` final)--        case b of-            Just b1 ->-                if a `eq` b1-                then do-                    mkCross (Stream.fromEffect $ put True)-                    MA.putIndex i foundArr True-                    return (Just a, Just b1)-                else mkCross Stream.nil-            Nothing -> return (Just a, Nothing)---- Put the b's that have been paired, in another hash or mutate the hash to set--- a flag. At the end go through @Stream m b@ and find those that are not in that--- hash to return (Nothing, b).---- | Like 'joinOuter' but uses a 'Map' for efficiency.------ Space: O(m + n)------ Time: O(m + n)------ /Pre-release/-{-# INLINE joinOuter #-}-joinOuter ::-    (Ord k, MonadIO m) =>-    Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)-joinOuter s1 s2 =-    Stream.concatEffect $ do-        km1 <- kvFold s1-        km2 <- kvFold s2--        -- XXX Not sure if toList/fromList would fuse optimally. We may have to-        -- create a fused Map.toStream function.-        let res1 = fmap (joinAB km2)-                        $ Stream.fromList $ Map.toList km1-                    where-                    joinAB km (k, a) =-                        case k `Map.lookup` km of-                            Just b -> (k, Just a, Just b)-                            Nothing -> (k, Just a, Nothing)--        -- XXX We can take advantage of the lookups in the first pass above to-        -- reduce the number of lookups in this pass. If we keep mutable cells-        -- in the second Map, we can flag it in the first pass and not do any-        -- lookup in the second pass if it is flagged.-        let res2 = Stream.mapMaybe (joinAB km1)-                        $ Stream.fromList $ Map.toList km2-                    where-                    joinAB km (k, b) =-                        case k `Map.lookup` km of-                            Just _ -> Nothing-                            Nothing -> Just (k, Nothing, Just b)--        return $ Stream.append res1 res2--        where--        -- XXX Generate error if a duplicate insertion is attempted?-        kvFold =-            let f = Fold.foldl' (\kv (k, b) -> Map.insert k b kv) Map.empty-             in Stream.fold f
− src/Streamly/Internal/Data/Stream/StreamD/Eliminate.hs
@@ -1,833 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Eliminate--- Copyright   : (c) 2018 Composewell Technologies---               (c) Roman Leshchinskiy 2008-2010--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC---- A few functions in this module have been adapted from the vector package--- (c) Roman Leshchinskiy.----module Streamly.Internal.Data.Stream.StreamD.Eliminate-    (-    -- * Running a 'Fold'-      fold--    -- -- * Running a 'Parser'-    , parse-    , parseD-    , parseBreak-    , parseBreakD--    -- * Stream Deconstruction-    , uncons--    -- * Right Folds-    , foldrM-    , foldr-    , foldrMx-    , foldr1--    -- * Left Folds-    , foldlM'-    , foldl'-    , foldlMx'-    , foldlx'--    -- * Specific Fold Functions-    , drain-    , mapM_ -- Map and Fold-    , null-    , head-    , headElse-    , tail-    , last-    , elem-    , notElem-    , all-    , any-    , maximum-    , maximumBy-    , minimum-    , minimumBy-    , lookup-    , findM-    , find-    , (!!)-    , the--    -- * To containers-    , toList-    , toListRev--    -- * Multi-Stream Folds-    -- ** Comparisons-    -- | These should probably be expressed using zipping operations.-    , eqBy-    , cmpBy--    -- ** Substreams-    -- | These should probably be expressed using parsers.-    , isPrefixOf-    , isInfixOf-    , isSuffixOf-    , isSuffixOfUnbox-    , isSubsequenceOf-    , stripPrefix-    , stripSuffix-    , stripSuffixUnbox-    )-where--#include "inline.hs"--import Control.Exception (assert)-import Control.Monad.IO.Class (MonadIO(..))-import Foreign.Storable (Storable)-import GHC.Exts (SpecConstrAnnotation(..))-import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Parser (ParseError(..))-import Streamly.Internal.Data.SVar.Type (defState)-import Streamly.Internal.Data.Unboxed (Unbox)--import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))--import qualified Streamly.Internal.Data.Array.Type as Array-import qualified Streamly.Internal.Data.Fold as Fold-import qualified Streamly.Internal.Data.Parser as PR-import qualified Streamly.Internal.Data.Parser.ParserD as PRD-import qualified Streamly.Internal.Data.Stream.StreamD.Generate as StreamD-import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Nesting-import qualified Streamly.Internal.Data.Stream.StreamD.Transform as StreamD--import Prelude hiding-       ( all, any, elem, foldr, foldr1, head, last, lookup, mapM, mapM_-       , maximum, minimum, notElem, null, splitAt, tail, (!!))-import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- Elimination by Folds------------------------------------------------------------------------------------------------------------------------------------------------------------------ Right Folds---------------------------------------------------------------------------------{-# INLINE_NORMAL foldr1 #-}-foldr1 :: Monad m => (a -> a -> a) -> Stream m a -> m (Maybe a)-foldr1 f m = do-     r <- uncons m-     case r of-         Nothing   -> return Nothing-         Just (h, t) -> fmap Just (foldr f h t)----------------------------------------------------------------------------------- Parsers----------------------------------------------------------------------------------- Inlined definition. Without the inline "serially/parser/take" benchmark--- degrades and parseMany does not fuse. Even using "inline" at the callsite--- does not help.-{-# INLINE splitAt #-}-splitAt :: Int -> [a] -> ([a],[a])-splitAt n ls-  | n <= 0 = ([], ls)-  | otherwise          = splitAt' n ls-    where-        splitAt' :: Int -> [a] -> ([a], [a])-        splitAt' _  []     = ([], [])-        splitAt' 1  (x:xs) = ([x], xs)-        splitAt' m  (x:xs) = (x:xs', xs'')-          where-            (xs', xs'') = splitAt' (m - 1) xs---- GHC parser does not accept {-# ANN type [] NoSpecConstr #-}, so we need--- to make a newtype.-{-# ANN type List NoSpecConstr #-}-newtype List a = List {getList :: [a]}---- | Run a 'Parse' over a stream.-{-# INLINE_NORMAL parseD #-}-parseD-    :: Monad m-    => PRD.Parser a m b-    -> Stream m a-    -> m (Either ParseError b)-parseD parser strm = do-    (b, _) <- parseBreakD parser strm-    return b---- | Parse a stream using the supplied 'Parser'.------ Parsers (See "Streamly.Internal.Data.Parser") are more powerful folds that--- add backtracking and error functionality to terminating folds. Unlike folds,--- parsers may not always result in a valid output, they may result in an--- error.  For example:------ >>> Stream.parse (Parser.takeEQ 1 Fold.drain) Stream.nil--- Left (ParseError "takeEQ: Expecting exactly 1 elements, input terminated on 0")------ Note: @parse p@ is not the same as  @head . parseMany p@ on an empty stream.----{-# INLINE [3] parse #-}-parse :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b)-parse = parseD---- XXX It may be a good idea to use constant sized chunks for backtracking. We--- can take a byte stream but when we have to backtrack we create constant--- sized chunks. We maintain one forward list and one backward list of constant--- sized chunks, and a last backtracking offset. That way we just need lists of--- contents and no need to maintain start/end pointers for individual arrays,--- reducing bookkeeping work.---- | Run a 'Parse' over a stream and return rest of the Stream.-{-# INLINE_NORMAL parseBreakD #-}-parseBreakD-    :: Monad m-    => PRD.Parser a m b-    -> Stream m a-    -> m (Either ParseError b, Stream m a)-parseBreakD (PRD.Parser pstep initial extract) stream@(Stream step state) = do-    res <- initial-    case res of-        PRD.IPartial s -> go SPEC state (List []) s-        PRD.IDone b -> return (Right b, stream)-        PRD.IError err -> return (Left (ParseError err), stream)--    where--    -- "buf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    go !_ st buf !pst = do-        r <- step defState st-        case r of-            Yield x s -> do-                pRes <- pstep pst x-                case pRes of-                    PR.Partial 0 pst1 -> go SPEC s (List []) pst1-                    PR.Partial 1 pst1 -> go1 SPEC s x pst1-                    PR.Partial n pst1 -> do-                        assert (n <= length (x:getList buf)) (return ())-                        let src0 = Prelude.take n (x:getList buf)-                            src  = Prelude.reverse src0-                        gobuf SPEC s (List []) (List src) pst1-                    PR.Continue 0 pst1 -> go SPEC s (List (x:getList buf)) pst1-                    PR.Continue 1 pst1 -> gobuf SPEC s buf (List [x]) pst1-                    PR.Continue n pst1 -> do-                        assert (n <= length (x:getList buf)) (return ())-                        let (src0, buf1) = splitAt n (x:getList buf)-                            src  = Prelude.reverse src0-                        gobuf SPEC s (List buf1) (List src) pst1-                    PR.Done 0 b -> return (Right b, Stream step s)-                    PR.Done n b -> do-                        assert (n <= length (x:getList buf)) (return ())-                        let src0 = Prelude.take n (x:getList buf)-                            src  = Prelude.reverse src0-                        -- XXX This would make it quadratic. We should probably-                        -- use StreamK if we have to append many times.-                        return-                            ( Right b,-                              Nesting.append (fromList src) (Stream step s))-                    PR.Error err ->-                        return (Left (ParseError err), Stream step s)-            Skip s -> go SPEC s buf pst-            Stop -> goStop SPEC buf pst--    go1 _ s x !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                go SPEC s (List []) pst1-            PR.Partial 1 pst1 -> do-                go1 SPEC s x pst1-            PR.Partial n _ ->-                error $ "parseBreak: parser bug, go1: Partial n = " ++ show n-            PR.Continue 0 pst1 ->-                go SPEC s (List [x]) pst1-            PR.Continue 1 pst1 ->-                go1 SPEC s x pst1-            PR.Continue n _ -> do-                error $ "parseBreak: parser bug, go1: Continue n = " ++ show n-            PR.Done 0 b -> do-                return (Right b, Stream step s)-            PR.Done 1 b -> do-                return (Right b, StreamD.cons x (Stream step s))-            PR.Done n _ -> do-                error $ "parseBreak: parser bug, go1: Done n = " ++ show n-            PR.Error err ->-                return-                    ( Left (ParseError err)-                    , Nesting.append (fromPure x) (Stream step s)-                    )--    gobuf !_ s buf (List []) !pst = go SPEC s buf pst-    gobuf !_ s buf (List (x:xs)) !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                gobuf SPEC s (List []) (List xs) pst1-            PR.Partial n pst1 -> do-                assert (n <= length (x:getList buf)) (return ())-                let src0 = Prelude.take n (x:getList buf)-                    src  = Prelude.reverse src0 ++ xs-                gobuf SPEC s (List []) (List src) pst1-            PR.Continue 0 pst1 ->-                gobuf SPEC s (List (x:getList buf)) (List xs) pst1-            PR.Continue 1 pst1 ->-                gobuf SPEC s buf (List (x:xs)) pst1-            PR.Continue n pst1 -> do-                assert (n <= length (x:getList buf)) (return ())-                let (src0, buf1) = splitAt n (x:getList buf)-                    src  = Prelude.reverse src0 ++ xs-                gobuf SPEC s (List buf1) (List src) pst1-            PR.Done n b -> do-                assert (n <= length (x:getList buf)) (return ())-                let src0 = Prelude.take n (x:getList buf)-                    src  = Prelude.reverse src0-                return (Right b, Nesting.append (fromList src) (Stream step s))-            PR.Error err ->-                return-                    ( Left (ParseError err)-                    , Nesting.append (fromList (x:xs)) (Stream step s)-                    )--    -- This is simplified gobuf-    goExtract !_ buf (List []) !pst = goStop SPEC buf pst-    goExtract !_ buf (List (x:xs)) !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                goExtract SPEC (List []) (List xs) pst1-            PR.Partial n pst1 -> do-                assert (n <= length (x:getList buf)) (return ())-                let src0 = Prelude.take n (x:getList buf)-                    src  = Prelude.reverse src0 ++ xs-                goExtract SPEC (List []) (List src) pst1-            PR.Continue 0 pst1 ->-                goExtract SPEC (List (x:getList buf)) (List xs) pst1-            PR.Continue 1 pst1 ->-                goExtract SPEC buf (List (x:xs)) pst1-            PR.Continue n pst1 -> do-                assert (n <= length (x:getList buf)) (return ())-                let (src0, buf1) = splitAt n (x:getList buf)-                    src  = Prelude.reverse src0 ++ xs-                goExtract SPEC (List buf1) (List src) pst1-            PR.Done n b -> do-                assert (n <= length (x:getList buf)) (return ())-                let src0 = Prelude.take n (x:getList buf)-                    src  = Prelude.reverse src0-                return (Right b, fromList src)-            PR.Error err -> return (Left (ParseError err), fromList (x:xs))--    -- This is simplified goExtract-    -- XXX Use SPEC?-    {-# INLINE goStop #-}-    goStop _ buf pst = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"-            PR.Continue 0 pst1 -> goStop SPEC buf pst1-            PR.Continue n pst1 -> do-                assert (n <= length (getList buf)) (return ())-                let (src0, buf1) = splitAt n (getList buf)-                    src = Prelude.reverse src0-                goExtract SPEC (List buf1) (List src) pst1-            PR.Done 0 b -> return (Right b, StreamD.nil)-            PR.Done n b -> do-                assert (n <= length (getList buf)) (return ())-                let src0 = Prelude.take n (getList buf)-                    src  = Prelude.reverse src0-                return (Right b, fromList src)-            PR.Error err ->-                return (Left (ParseError err), StreamD.nil)---- | Parse a stream using the supplied 'Parser'.----{-# INLINE parseBreak #-}-parseBreak :: Monad m => PR.Parser a m b -> Stream m a -> m (Either ParseError b, Stream m a)-parseBreak = parseBreakD----------------------------------------------------------------------------------- Specialized Folds----------------------------------------------------------------------------------- benchmark after dropping 1 item from stream or using unfolds-{-# INLINE_NORMAL null #-}-null :: Monad m => Stream m a -> m Bool-#ifdef USE_FOLDS_EVERYWHERE-null = fold Fold.null-#else-null = foldrM (\_ _ -> return False) (return True)-#endif--{-# INLINE_NORMAL head #-}-head :: Monad m => Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-head = fold Fold.one-#else-head = foldrM (\x _ -> return (Just x)) (return Nothing)-#endif--{-# INLINE_NORMAL headElse #-}-headElse :: Monad m => a -> Stream m a -> m a-headElse a = foldrM (\x _ -> return x) (return a)---- Does not fuse, has the same performance as the StreamK version.-{-# INLINE_NORMAL tail #-}-tail :: Monad m => Stream m a -> m (Maybe (Stream m a))-tail (UnStream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield _ s -> return (Just $ Stream step s)-            Skip  s   -> go SPEC s-            Stop      -> return Nothing---- XXX will it fuse? need custom impl?-{-# INLINE_NORMAL last #-}-last :: Monad m => Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-last = fold Fold.last-#else-last = foldl' (\_ y -> Just y) Nothing-#endif---- XXX Use the foldrM based impl instead-{-# INLINE_NORMAL elem #-}-elem :: (Monad m, Eq a) => a -> Stream m a -> m Bool-#ifdef USE_FOLDS_EVERYWHERE-elem e = fold (Fold.elem e)-#else--- elem e m = foldrM (\x xs -> if x == e then return True else xs) (return False) m-elem e (Stream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield x s-              | x == e -> return True-              | otherwise -> go SPEC s-            Skip s -> go SPEC s-            Stop   -> return False-#endif--{-# INLINE_NORMAL notElem #-}-notElem :: (Monad m, Eq a) => a -> Stream m a -> m Bool-notElem e s = fmap not (elem e s)--{-# INLINE_NORMAL all #-}-all :: Monad m => (a -> Bool) -> Stream m a -> m Bool-#ifdef USE_FOLDS_EVERYWHERE-all p = fold (Fold.all p)-#else--- all p m = foldrM (\x xs -> if p x then xs else return False) (return True) m-all p (Stream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield x s-              | p x -> go SPEC s-              | otherwise -> return False-            Skip s -> go SPEC s-            Stop   -> return True-#endif--{-# INLINE_NORMAL any #-}-any :: Monad m => (a -> Bool) -> Stream m a -> m Bool-#ifdef USE_FOLDS_EVERYWHERE-any p = fold (Fold.any p)-#else--- any p m = foldrM (\x xs -> if p x then return True else xs) (return False) m-any p (Stream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield x s-              | p x -> return True-              | otherwise -> go SPEC s-            Skip s -> go SPEC s-            Stop   -> return False-#endif--{-# INLINE_NORMAL maximum #-}-maximum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-maximum = fold Fold.maximum-#else-maximum (Stream step state) = go SPEC Nothing' state-  where-    go !_ Nothing' st = do-        r <- step defState st-        case r of-            Yield x s -> go SPEC (Just' x) s-            Skip  s   -> go SPEC Nothing' s-            Stop      -> return Nothing-    go !_ (Just' acc) st = do-        r <- step defState st-        case r of-            Yield x s-              | acc <= x  -> go SPEC (Just' x) s-              | otherwise -> go SPEC (Just' acc) s-            Skip s -> go SPEC (Just' acc) s-            Stop   -> return (Just acc)-#endif--{-# INLINE_NORMAL maximumBy #-}-maximumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-maximumBy cmp = fold (Fold.maximumBy cmp)-#else-maximumBy cmp (Stream step state) = go SPEC Nothing' state-  where-    go !_ Nothing' st = do-        r <- step defState st-        case r of-            Yield x s -> go SPEC (Just' x) s-            Skip  s   -> go SPEC Nothing' s-            Stop      -> return Nothing-    go !_ (Just' acc) st = do-        r <- step defState st-        case r of-            Yield x s -> case cmp acc x of-                GT -> go SPEC (Just' acc) s-                _  -> go SPEC (Just' x) s-            Skip s -> go SPEC (Just' acc) s-            Stop   -> return (Just acc)-#endif--{-# INLINE_NORMAL minimum #-}-minimum :: (Monad m, Ord a) => Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-minimum = fold Fold.minimum-#else-minimum (Stream step state) = go SPEC Nothing' state--    where--    go !_ Nothing' st = do-        r <- step defState st-        case r of-            Yield x s -> go SPEC (Just' x) s-            Skip  s   -> go SPEC Nothing' s-            Stop      -> return Nothing-    go !_ (Just' acc) st = do-        r <- step defState st-        case r of-            Yield x s-              | acc <= x  -> go SPEC (Just' acc) s-              | otherwise -> go SPEC (Just' x) s-            Skip s -> go SPEC (Just' acc) s-            Stop   -> return (Just acc)-#endif--{-# INLINE_NORMAL minimumBy #-}-minimumBy :: Monad m => (a -> a -> Ordering) -> Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-minimumBy cmp = fold (Fold.minimumBy cmp)-#else-minimumBy cmp (Stream step state) = go SPEC Nothing' state--    where--    go !_ Nothing' st = do-        r <- step defState st-        case r of-            Yield x s -> go SPEC (Just' x) s-            Skip  s   -> go SPEC Nothing' s-            Stop      -> return Nothing-    go !_ (Just' acc) st = do-        r <- step defState st-        case r of-            Yield x s -> case cmp acc x of-                GT -> go SPEC (Just' x) s-                _  -> go SPEC (Just' acc) s-            Skip s -> go SPEC (Just' acc) s-            Stop   -> return (Just acc)-#endif--{-# INLINE_NORMAL (!!) #-}-(!!) :: (Monad m) => Stream m a -> Int -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-stream !! i = fold (Fold.index i) stream-#else-(Stream step state) !! i = go SPEC i state--    where--    go !_ !n st = do-        r <- step defState st-        case r of-            Yield x s | n < 0 -> return Nothing-                      | n == 0 -> return $ Just x-                      | otherwise -> go SPEC (n - 1) s-            Skip s -> go SPEC n s-            Stop   -> return Nothing-#endif--{-# INLINE_NORMAL lookup #-}-lookup :: (Monad m, Eq a) => a -> Stream m (a, b) -> m (Maybe b)-#ifdef USE_FOLDS_EVERYWHERE-lookup e = fold (Fold.lookup e)-#else-lookup e = foldrM (\(a, b) xs -> if e == a then return (Just b) else xs)-                   (return Nothing)-#endif--{-# INLINE_NORMAL findM #-}-findM :: Monad m => (a -> m Bool) -> Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-findM p = fold (Fold.findM p)-#else-findM p = foldrM (\x xs -> p x >>= \r -> if r then return (Just x) else xs)-                   (return Nothing)-#endif--{-# INLINE find #-}-find :: Monad m => (a -> Bool) -> Stream m a -> m (Maybe a)-find p = findM (return . p)--{-# INLINE toListRev #-}-toListRev :: Monad m => Stream m a -> m [a]-#ifdef USE_FOLDS_EVERYWHERE-toListRev = fold Fold.toListRev-#else-toListRev = foldl' (flip (:)) []-#endif----------------------------------------------------------------------------------- Transformation comprehensions---------------------------------------------------------------------------------{-# INLINE_NORMAL the #-}-the :: (Eq a, Monad m) => Stream m a -> m (Maybe a)-#ifdef USE_FOLDS_EVERYWHERE-the = fold Fold.the-#else-the (Stream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield x s -> go' SPEC x s-            Skip s    -> go SPEC s-            Stop      -> return Nothing-    go' !_ n st = do-        r <- step defState st-        case r of-            Yield x s | x == n -> go' SPEC n s-                      | otherwise -> return Nothing-            Skip s -> go' SPEC n s-            Stop   -> return (Just n)-#endif----------------------------------------------------------------------------------- Map and Fold----------------------------------------------------------------------------------- | Execute a monadic action for each element of the 'Stream'-{-# INLINE_NORMAL mapM_ #-}-mapM_ :: Monad m => (a -> m b) -> Stream m a -> m ()-#ifdef USE_FOLDS_EVERYWHERE-mapM_ f = fold (Fold.drainBy f)-#else-mapM_ m = drain . mapM m-#endif----------------------------------------------------------------------------------- Multi-stream folds----------------------------------------------------------------------------------- | Returns 'True' if the first stream is the same as or a prefix of the--- second. A stream is a prefix of itself.------ >>> Stream.isPrefixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)--- True----{-# INLINE_NORMAL isPrefixOf #-}-isPrefixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isPrefixOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb--    where--    go !_ Nothing' sa sb = do-        r <- stepa defState sa-        case r of-            Yield x sa' -> go SPEC (Just' x) sa' sb-            Skip sa'    -> go SPEC Nothing' sa' sb-            Stop        -> return True--    go !_ (Just' x) sa sb = do-        r <- stepb defState sb-        case r of-            Yield y sb' ->-                if x == y-                    then go SPEC Nothing' sa sb'-                    else return False-            Skip sb' -> go SPEC (Just' x) sa sb'-            Stop     -> return False---- | Returns 'True' if all the elements of the first stream occur, in order, in--- the second stream. The elements do not have to occur consecutively. A stream--- is a subsequence of itself.------ >>> Stream.isSubsequenceOf (Stream.fromList "hlo") (Stream.fromList "hello" :: Stream IO Char)--- True----{-# INLINE_NORMAL isSubsequenceOf #-}-isSubsequenceOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isSubsequenceOf (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb--    where--    go !_ Nothing' sa sb = do-        r <- stepa defState sa-        case r of-            Yield x sa' -> go SPEC (Just' x) sa' sb-            Skip sa' -> go SPEC Nothing' sa' sb-            Stop -> return True--    go !_ (Just' x) sa sb = do-        r <- stepb defState sb-        case r of-            Yield y sb' ->-                if x == y-                    then go SPEC Nothing' sa sb'-                    else go SPEC (Just' x) sa sb'-            Skip sb' -> go SPEC (Just' x) sa sb'-            Stop -> return False---- | @stripPrefix prefix input@ strips the @prefix@ stream from the @input@--- stream if it is a prefix of input. Returns 'Nothing' if the input does not--- start with the given prefix, stripped input otherwise. Returns @Just nil@--- when the prefix is the same as the input stream.------ Space: @O(1)@----{-# INLINE_NORMAL stripPrefix #-}-stripPrefix-    :: (Monad m, Eq a)-    => Stream m a -> Stream m a -> m (Maybe (Stream m a))-stripPrefix (Stream stepa ta) (Stream stepb tb) = go SPEC Nothing' ta tb--    where--    go !_ Nothing' sa sb = do-        r <- stepa defState sa-        case r of-            Yield x sa' -> go SPEC (Just' x) sa' sb-            Skip sa'    -> go SPEC Nothing' sa' sb-            Stop        -> return $ Just (Stream stepb sb)--    go !_ (Just' x) sa sb = do-        r <- stepb defState sb-        case r of-            Yield y sb' ->-                if x == y-                    then go SPEC Nothing' sa sb'-                    else return Nothing-            Skip sb' -> go SPEC (Just' x) sa sb'-            Stop     -> return Nothing---- | Returns 'True' if the first stream is an infix of the second. A stream is--- considered an infix of itself.------ >>> s = Stream.fromList "hello" :: Stream IO Char--- >>> Stream.isInfixOf s s--- True------ Space: @O(n)@ worst case where @n@ is the length of the infix.------ /Pre-release/------ /Requires 'Storable' constraint/----{-# INLINE isInfixOf #-}-isInfixOf :: (MonadIO m, Eq a, Enum a, Storable a, Unbox a)-    => Stream m a -> Stream m a -> m Bool-isInfixOf infx stream = do-    arr <- fold Array.write infx-    -- XXX can use breakOnSeq instead (when available)-    r <- null $ StreamD.drop 1 $ Nesting.splitOnSeq arr Fold.drain stream-    return (not r)---- Note: isPrefixOf uses the prefix stream only once. In contrast, isSuffixOf--- may use the suffix stream many times. To run in optimal memory we do not--- want to buffer the suffix stream in memory therefore  we need an ability to--- clone (or consume it multiple times) the suffix stream without any side--- effects so that multiple potential suffix matches can proceed in parallel--- without buffering the suffix stream. For example, we may create the suffix--- stream from a file handle, however, if we evaluate the stream multiple--- times, once for each match, we will need a different file handle each time--- which may exhaust the file descriptors. Instead, we want to share the same--- underlying file descriptor, use pread on it to generate the stream and clone--- the stream for each match. Therefore the suffix stream should be built in--- such a way that it can be consumed multiple times without any problems.---- XXX Can be implemented with better space/time complexity.--- Space: @O(n)@ worst case where @n@ is the length of the suffix.---- | Returns 'True' if the first stream is a suffix of the second. A stream is--- considered a suffix of itself.------ >>> Stream.isSuffixOf (Stream.fromList "hello") (Stream.fromList "hello" :: Stream IO Char)--- True------ Space: @O(n)@, buffers entire input stream and the suffix.------ /Pre-release/------ /Suboptimal/ - Help wanted.----{-# INLINE isSuffixOf #-}-isSuffixOf :: (Monad m, Eq a) => Stream m a -> Stream m a -> m Bool-isSuffixOf suffix stream =-    StreamD.reverse suffix `isPrefixOf` StreamD.reverse stream---- | Much faster than 'isSuffixOf'.-{-# INLINE isSuffixOfUnbox #-}-isSuffixOfUnbox :: (MonadIO m, Eq a, Unbox a) =>-    Stream m a -> Stream m a -> m Bool-isSuffixOfUnbox suffix stream =-    StreamD.reverseUnbox suffix `isPrefixOf` StreamD.reverseUnbox stream---- | Drops the given suffix from a stream. Returns 'Nothing' if the stream does--- not end with the given suffix. Returns @Just nil@ when the suffix is the--- same as the stream.------ It may be more efficient to convert the stream to an Array and use--- stripSuffix on that especially if the elements have a Storable or Prim--- instance.------ See also "Streamly.Internal.Data.Stream.Reduce.dropSuffix".------ Space: @O(n)@, buffers the entire input stream as well as the suffix------ /Pre-release/-{-# INLINE stripSuffix #-}-stripSuffix-    :: (Monad m, Eq a)-    => Stream m a -> Stream m a -> m (Maybe (Stream m a))-stripSuffix m1 m2 =-    fmap StreamD.reverse-        <$> stripPrefix (StreamD.reverse m1) (StreamD.reverse m2)---- | Much faster than 'stripSuffix'.-{-# INLINE stripSuffixUnbox #-}-stripSuffixUnbox-    :: (MonadIO m, Eq a, Unbox a)-    => Stream m a -> Stream m a -> m (Maybe (Stream m a))-stripSuffixUnbox m1 m2 =-    fmap StreamD.reverseUnbox-        <$> stripPrefix (StreamD.reverseUnbox m1) (StreamD.reverseUnbox m2)
− src/Streamly/Internal/Data/Stream/StreamD/Exception.hs
@@ -1,479 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Exception--- Copyright   : (c) 2020 Composewell Technologies and Contributors--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--module Streamly.Internal.Data.Stream.StreamD.Exception-    (-      gbracket_-    , gbracket-    , before-    , afterUnsafe-    , afterIO-    , bracketUnsafe-    , bracketIO3-    , bracketIO-    , onException-    , finallyUnsafe-    , finallyIO-    , ghandle-    , handle-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO(..))-import Control.Exception (Exception, SomeException, mask_)-import Control.Monad.Catch (MonadCatch)-import GHC.Exts (inline)-import Streamly.Internal.Data.IOFinalizer-    (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)--import qualified Control.Monad.Catch as MC--import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"--data GbracketState s1 s2 v-    = GBracketInit-    | GBracketNormal s1 v-    | GBracketException s2---- | Like 'gbracket' but with following differences:------ * alloc action @m c@ runs with async exceptions enabled--- * cleanup action @c -> m d@ won't run if the stream is garbage collected---   after partial evaluation.------ /Inhibits stream fusion/------ /Pre-release/----{-# INLINE_NORMAL gbracket_ #-}-gbracket_-    :: Monad m-    => m c                                  -- ^ before-    -> (c -> m d)                           -- ^ after, on normal stop-    -> (c -> e -> Stream m b -> Stream m b) -- ^ on exception-    -> (forall s. m s -> m (Either e s))    -- ^ try (exception handling)-    -> (c -> Stream m b)                    -- ^ stream generator-    -> Stream m b-gbracket_ bef aft onExc ftry action =-    Stream step GBracketInit--    where--    {-# INLINE_LATE step #-}-    step _ GBracketInit = do-        r <- bef-        return $ Skip $ GBracketNormal (action r) r--    step gst (GBracketNormal (UnStream step1 st) v) = do-        res <- ftry $ step1 gst st-        case res of-            Right r -> case r of-                Yield x s ->-                    return $ Yield x (GBracketNormal (Stream step1 s) v)-                Skip s -> return $ Skip (GBracketNormal (Stream step1 s) v)-                Stop -> aft v >> return Stop-            -- XXX Do not handle async exceptions, just rethrow them.-            Left e ->-                return-                    $ Skip (GBracketException (onExc v e (UnStream step1 st)))-    step gst (GBracketException (UnStream step1 st)) = do-        res <- step1 gst st-        case res of-            Yield x s -> return $ Yield x (GBracketException (Stream step1 s))-            Skip s    -> return $ Skip (GBracketException (Stream step1 s))-            Stop      -> return Stop--data GbracketIOState s1 s2 v wref-    = GBracketIOInit-    | GBracketIONormal s1 v wref-    | GBracketIOException s2---- | Run the alloc action @m c@ with async exceptions disabled but keeping--- blocking operations interruptible (see 'Control.Exception.mask').  Use the--- output @c@ as input to @c -> Stream m b@ to generate an output stream. When--- generating the stream use the supplied @try@ operation @forall s. m s -> m--- (Either e s)@ to catch synchronous exceptions. If an exception occurs run--- the exception handler @c -> e -> Stream m b -> m (Stream m b)@. Note that--- 'gbracket' does not rethrow the exception, it has to be done by the--- exception handler if desired.------ The cleanup action @c -> m d@, runs whenever the stream ends normally, due--- to a sync or async exception or if it gets garbage collected after a partial--- lazy evaluation.  See 'bracket' for the semantics of the cleanup action.------ 'gbracket' can express all other exception handling combinators.------ /Inhibits stream fusion/------ /Pre-release/-{-# INLINE_NORMAL gbracket #-}-gbracket-    :: MonadIO m-    => IO c -- ^ before-    -> (c -> IO d1) -- ^ on normal stop-    -> (c -> e -> Stream m b -> IO (Stream m b)) -- ^ on exception-    -> (c -> IO d2) -- ^ on GC without normal stop or exception-    -> (forall s. m s -> m (Either e s)) -- ^ try (exception handling)-    -> (c -> Stream m b) -- ^ stream generator-    -> Stream m b-gbracket bef aft onExc onGC ftry action =-    Stream step GBracketIOInit--    where--    -- If the stream is never evaluated the "aft" action will never be-    -- called. For that to occur we will need the user of this API to pass a-    -- weak pointer to us.-    {-# INLINE_LATE step #-}-    step _ GBracketIOInit = do-        -- We mask asynchronous exceptions to make the execution-        -- of 'bef' and the registration of 'aft' atomic.-        -- A similar thing is done in the resourcet package: https://git.io/JvKV3-        -- Tutorial: https://markkarpov.com/tutorial/exceptions.html-        (r, ref) <- liftIO $ mask_ $ do-            r <- bef-            ref <- newIOFinalizer (onGC r)-            return (r, ref)-        return $ Skip $ GBracketIONormal (action r) r ref--    step gst (GBracketIONormal (UnStream step1 st) v ref) = do-        res <- ftry $ step1 gst st-        case res of-            Right r -> case r of-                Yield x s ->-                    return $ Yield x (GBracketIONormal (Stream step1 s) v ref)-                Skip s ->-                    return $ Skip (GBracketIONormal (Stream step1 s) v ref)-                Stop ->-                    liftIO (clearingIOFinalizer ref (aft v)) >> return Stop-            -- XXX Do not handle async exceptions, just rethrow them.-            Left e -> do-                -- Clearing of finalizer and running of exception handler must-                -- 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.-                stream <--                    liftIO (clearingIOFinalizer ref (onExc v e (UnStream step1 st)))-                return $ Skip (GBracketIOException stream)-    step gst (GBracketIOException (UnStream step1 st)) = do-        res <- step1 gst st-        case res of-            Yield x s ->-                return $ Yield x (GBracketIOException (Stream step1 s))-            Skip s    -> return $ Skip (GBracketIOException (Stream step1 s))-            Stop      -> return Stop---- | Run the action @m b@ before the stream yields its first element.------ Same as the following but more efficient due to fusion:------ >>> before action xs = Stream.nilM action <> xs--- >>> before action xs = Stream.concatMap (const xs) (Stream.fromEffect action)----{-# INLINE_NORMAL before #-}-before :: Monad m => m b -> Stream m a -> Stream m a-before action (Stream step state) = Stream step' Nothing--    where--    {-# INLINE_LATE step' #-}-    step' _ Nothing = action >> return (Skip (Just state))--    step' gst (Just st) = do-        res <- step gst st-        case res of-            Yield x s -> return $ Yield x (Just s)-            Skip s    -> return $ Skip (Just s)-            Stop      -> return Stop---- | Like 'after', with following differences:------ * action @m b@ won't run if the stream is garbage collected---   after partial evaluation.--- * Monad @m@ does not require any other constraints.--- * has slightly better performance than 'after'.------ Same as the following, but with stream fusion:------ >>> afterUnsafe action xs = xs <> Stream.nilM action------ /Pre-release/----{-# INLINE_NORMAL afterUnsafe #-}-afterUnsafe :: Monad m => m b -> Stream m a -> Stream m a-afterUnsafe action (Stream step state) = Stream step' state--    where--    {-# INLINE_LATE step' #-}-    step' gst st = do-        res <- step gst st-        case res of-            Yield x s -> return $ Yield x s-            Skip s    -> return $ Skip s-            Stop      -> action >> return Stop---- | Run the action @IO b@ whenever the stream is evaluated to completion, or--- if it is garbage collected after a partial lazy evaluation.------ The semantics of the action @IO b@ are similar to the semantics of cleanup--- action in 'bracketIO'.------ /See also 'afterUnsafe'/----{-# INLINE_NORMAL afterIO #-}-afterIO :: MonadIO m-    => IO b -> Stream m a -> Stream m a-afterIO action (Stream step state) = Stream step' Nothing--    where--    {-# INLINE_LATE step' #-}-    step' _ Nothing = do-        ref <- liftIO $ newIOFinalizer action-        return $ Skip $ Just (state, ref)-    step' gst (Just (st, ref)) = do-        res <- step gst st-        case res of-            Yield x s -> return $ Yield x (Just (s, ref))-            Skip s    -> return $ Skip (Just (s, ref))-            Stop      -> do-                runIOFinalizer ref-                return Stop---- XXX For high performance error checks in busy streams we may need another--- Error constructor in step.---- | Run the action @m b@ if the stream evaluation is aborted due to an--- exception. The exception is not caught, simply rethrown.------ /Inhibits stream fusion/----{-# INLINE_NORMAL onException #-}-onException :: MonadCatch m => m b -> Stream m a -> Stream m a-onException action stream =-    gbracket_-        (return ()) -- before-        return      -- after-        (\_ (e :: MC.SomeException) _ -> nilM (action >> MC.throwM e))-        (inline MC.try)-        (const stream)--{-# INLINE_NORMAL _onException #-}-_onException :: MonadCatch m => m b -> Stream m a -> Stream m a-_onException action (Stream step state) = Stream step' state--    where--    {-# INLINE_LATE step' #-}-    step' gst st = do-        res <- step gst st `MC.onException` action-        case res of-            Yield x s -> return $ Yield x s-            Skip s    -> return $ Skip s-            Stop      -> return Stop---- | Like 'bracket' but with following differences:------ * alloc action @m b@ runs with async exceptions enabled--- * cleanup action @b -> m c@ won't run if the stream is garbage collected---   after partial evaluation.--- * has slightly better performance than 'bracketIO'.------ /Inhibits stream fusion/------ /Pre-release/----{-# INLINE_NORMAL bracketUnsafe #-}-bracketUnsafe :: MonadCatch m-    => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a-bracketUnsafe bef aft =-    gbracket_-        bef-        aft-        (\a (e :: SomeException) _ -> nilM (aft a >> MC.throwM e))-        (inline MC.try)---- For a use case of this see the "streamly-process" package. It needs to kill--- the process in case of exception or garbage collection, but waits for the--- process to terminate in normal cases.---- | Like 'bracketIO' but can use 3 separate cleanup actions depending on the--- mode of termination:------ 1. When the stream stops normally--- 2. When the stream is garbage collected--- 3. When the stream encounters an exception------ @bracketIO3 before onStop onGC onException action@ runs @action@ using the--- result of @before@. If the stream stops, @onStop@ action is executed, if the--- stream is abandoned @onGC@ is executed, if the stream encounters an--- exception @onException@ is executed.------ /Inhibits stream fusion/------ /Pre-release/-{-# INLINE_NORMAL bracketIO3 #-}-bracketIO3 :: (MonadIO m, MonadCatch m) =>-       IO b-    -> (b -> IO c)-    -> (b -> IO d)-    -> (b -> IO e)-    -> (b -> Stream m a)-    -> Stream m a-bracketIO3 bef aft onExc onGC =-    gbracket-        bef-        aft-        (\a (e :: SomeException) _ -> onExc a >> return (nilM (MC.throwM e)))-        onGC-        (inline MC.try)---- | Run the alloc action @IO b@ with async exceptions disabled but keeping--- blocking operations interruptible (see 'Control.Exception.mask').  Use the--- output @b@ as input to @b -> Stream m a@ to generate an output stream.------ @b@ is usually a resource under the IO monad, e.g. a file handle, that--- requires a cleanup after use. The cleanup action @b -> IO c@, runs whenever--- the stream ends normally, due to a sync or async exception or if it gets--- garbage collected after a partial lazy evaluation.------ 'bracketIO' only guarantees that the cleanup action runs, and it runs with--- async exceptions enabled. The action must ensure that it can successfully--- cleanup the resource in the face of sync or async exceptions.------ When the stream ends normally or on a sync exception, cleanup action runs--- immediately in the current thread context, whereas in other cases it runs in--- the GC context, therefore, cleanup may be delayed until the GC gets to run.------ /See also: 'bracketUnsafe'/------ /Inhibits stream fusion/----{-# INLINE bracketIO #-}-bracketIO :: (MonadIO m, MonadCatch m)-    => IO b -> (b -> IO c) -> (b -> Stream m a) -> Stream m a-bracketIO bef aft = bracketIO3 bef aft aft aft--data BracketState s v = BracketInit | BracketRun s v---- | Alternate (custom) implementation of 'bracket'.----{-# INLINE_NORMAL _bracket #-}-_bracket :: MonadCatch m-    => m b -> (b -> m c) -> (b -> Stream m a) -> Stream m a-_bracket bef aft bet = Stream step' BracketInit--    where--    {-# INLINE_LATE step' #-}-    step' _ BracketInit = bef >>= \x -> return (Skip (BracketRun (bet x) x))--    -- NOTE: It is important to use UnStream instead of the Stream pattern-    -- here, otherwise we get huge perf degradation, see note in concatMap.-    step' gst (BracketRun (UnStream step state) v) = do-        -- res <- step gst state `MC.onException` aft v-        res <- inline MC.try $ step gst state-        case res of-            Left (e :: SomeException) -> aft v >> MC.throwM e >> return Stop-            Right r -> case r of-                Yield x s -> return $ Yield x (BracketRun (Stream step s) v)-                Skip s    -> return $ Skip (BracketRun (Stream step s) v)-                Stop      -> aft v >> return Stop---- | Like 'finally' with following differences:------ * action @m b@ won't run if the stream is garbage collected---   after partial evaluation.--- * has slightly better performance than 'finallyIO'.------ /Inhibits stream fusion/------ /Pre-release/----{-# INLINE finallyUnsafe #-}-finallyUnsafe :: MonadCatch m => m b -> Stream m a -> Stream m a-finallyUnsafe action xs = bracketUnsafe (return ()) (const action) (const xs)---- | Run the action @IO b@ whenever the stream stream stops normally, aborts--- due to an exception or if it is garbage collected after a partial lazy--- evaluation.------ The semantics of running the action @IO b@ are similar to the cleanup action--- semantics described in 'bracketIO'.------ >>> finallyIO release = Stream.bracketIO (return ()) (const release)------ /See also 'finallyUnsafe'/------ /Inhibits stream fusion/----{-# INLINE finallyIO #-}-finallyIO :: (MonadIO m, MonadCatch m) => IO b -> Stream m a -> Stream m a-finallyIO action xs = bracketIO3 (return ()) act act act (const xs)-    where act _ = action---- | Like 'handle' but the exception handler is also provided with the stream--- that generated the exception as input. The exception handler can thus--- re-evaluate the stream to retry the action that failed. The exception--- handler can again call 'ghandle' on it to retry the action multiple times.------ This is highly experimental. In a stream of actions we can map the stream--- with a retry combinator to retry each action on failure.------ /Inhibits stream fusion/------ /Pre-release/----{-# INLINE_NORMAL ghandle #-}-ghandle :: (MonadCatch m, Exception e)-    => (e -> Stream m a -> Stream m a) -> Stream m a -> Stream m a-ghandle f stream =-    gbracket_ (return ()) return (const f) (inline MC.try) (const stream)---- | When evaluating a stream if an exception occurs, stream evaluation aborts--- and the specified exception handler is run with the exception as argument.------ /Inhibits stream fusion/----{-# INLINE_NORMAL handle #-}-handle :: (MonadCatch m, Exception e)-    => (e -> Stream m a) -> Stream m a -> Stream m a-handle f stream =-    gbracket_ (return ()) return (\_ e _ -> f e) (inline MC.try) (const stream)---- | Alternate (custom) implementation of 'handle'.----{-# INLINE_NORMAL _handle #-}-_handle :: (MonadCatch m, Exception e)-    => (e -> Stream m a) -> Stream m a -> Stream m a-_handle f (Stream step state) = Stream step' (Left state)--    where--    {-# INLINE_LATE step' #-}-    step' gst (Left st) = do-        res <- inline MC.try $ step gst st-        case res of-            Left e -> return $ Skip $ Right (f e)-            Right r -> case r of-                Yield x s -> return $ Yield x (Left s)-                Skip s    -> return $ Skip (Left s)-                Stop      -> return Stop--    step' gst (Right (UnStream step1 st)) = do-        res <- step1 gst st-        case res of-            Yield x s -> return $ Yield x (Right (Stream step1 s))-            Skip s    -> return $ Skip (Right (Stream step1 s))-            Stop      -> return Stop
− src/Streamly/Internal/Data/Stream/StreamD/Generate.hs
@@ -1,1205 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Generate--- Copyright   : (c) 2020 Composewell Technologies and Contributors---               (c) Roman Leshchinskiy 2008-2010--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------- A few combinators in this module have been adapted from the vector package--- (c) Roman Leshchinskiy. See the notes in specific combinators.----module Streamly.Internal.Data.Stream.StreamD.Generate-  (-    -- * Primitives-      nil-    , nilM-    , cons-    , consM--    -- * From 'Unfold'-    , unfold--    -- * Unfolding-    , unfoldr-    , unfoldrM--    -- * From Values-    , fromPure-    , fromEffect-    , repeat-    , repeatM-    , replicate-    , replicateM--    -- * Enumeration-    -- ** Enumerating 'Num' Types-    , enumerateFromStepNum-    , enumerateFromNum-    , enumerateFromThenNum--    -- ** Enumerating 'Bounded' 'Enum' Types-    , enumerate-    , enumerateTo-    , enumerateFromBounded--    -- ** Enumerating 'Enum' Types not larger than 'Int'-    , enumerateFromToSmall-    , enumerateFromThenToSmall-    , enumerateFromThenSmallBounded--    -- ** Enumerating 'Bounded' 'Integral' Types-    , enumerateFromIntegral-    , enumerateFromThenIntegral--    -- ** Enumerating 'Integral' Types-    , enumerateFromToIntegral-    , enumerateFromThenToIntegral--    -- ** Enumerating unbounded 'Integral' Types-    , enumerateFromStepIntegral--    -- ** Enumerating 'Fractional' Types-    , enumerateFromFractional-    , enumerateFromToFractional-    , enumerateFromThenFractional-    , enumerateFromThenToFractional--    -- ** Enumerable Type Class-    , Enumerable(..)--    -- * Time Enumeration-    , times-    , timesWith-    , absTimes-    , absTimesWith-    , relTimes-    , relTimesWith-    , durations-    , timeout--    -- * From Generators-    -- | Generate a monadic stream from a seed.-    , fromIndices-    , fromIndicesM-    , generate-    , generateM--    -- * Iteration-    , iterate-    , iterateM--    -- * From Containers-    -- | Transform an input structure into a stream.--    , fromList-    , fromListM-    , fromFoldable-    , fromFoldableM--    -- * From Pointers-    , fromPtr-    , fromPtrN-    , fromByteStr#--    -- * Conversions-    , fromStreamK-    , toStreamK-    )-where--#include "inline.hs"-#include "ArrayMacros.h"--import Control.Monad.IO.Class (MonadIO(..))-import Data.Functor.Identity (Identity(..))-import Foreign.Ptr (Ptr, plusPtr)-import Foreign.Storable (Storable (peek), sizeOf)-import GHC.Exts (Addr#, Ptr (Ptr))-import Streamly.Internal.Data.Time.Clock-    (Clock(Monotonic), asyncClock, readClock)-import Streamly.Internal.Data.Time.Units-    (toAbsTime, AbsTime, toRelTime64, RelTime64, addToAbsTime64)--#ifdef USE_UNFOLDS_EVERYWHERE-import qualified Streamly.Internal.Data.Unfold as Unfold-import qualified Streamly.Internal.Data.Unfold.Enumeration as Unfold-#endif--import Data.Fixed-import Data.Int-import Data.Ratio-import Data.Word-import Numeric.Natural-import Prelude hiding (iterate, repeat, replicate, take, takeWhile)-import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- Primitives----------------------------------------------------------------------------------- XXX implement in terms of nilM?---- | A stream that terminates without producing any output or side effect.------ >>> Stream.fold Fold.toList Stream.nil--- []----{-# INLINE_NORMAL nil #-}-nil :: Applicative m => Stream m a-nil = Stream (\_ _ -> pure Stop) ()---- XXX implement in terms of consM?--- cons x = consM (return x)---- | Fuse a pure value at the head of an existing stream::------ >>> s = 1 `Stream.cons` Stream.fromList [2,3]--- >>> Stream.fold Fold.toList s--- [1,2,3]------ This function should not be used to dynamically construct a stream. If a--- stream is constructed by successive use of this function it would take--- O(n^2) time to consume the stream.------ This function should only be used to statically fuse an element with a--- stream. Do not use this recursively or where it cannot be inlined.------ See "Streamly.Data.StreamK" for a 'cons' that can be used to--- construct a stream recursively.------ Definition:------ >>> cons x xs = return x `Stream.consM` xs----{-# INLINE_NORMAL cons #-}-cons :: Applicative m => a -> Stream m a -> Stream m a-cons x (Stream step state) = Stream step1 Nothing-    where-    {-# INLINE_LATE step1 #-}-    step1 _ Nothing = pure $ Yield x (Just state)-    step1 gst (Just st) = do-          (\case-            Yield a s -> Yield a (Just s)-            Skip  s   -> Skip (Just s)-            Stop      -> Stop) <$> step gst st----------------------------------------------------------------------------------- Unfolding----------------------------------------------------------------------------------- Adapted from vector package---- | Build a stream by unfolding a /monadic/ step function starting from a--- seed.  The step function returns the next element in the stream and the next--- seed value. When it is done it returns 'Nothing' and the stream ends. For--- example,------ >>> :{--- let f b =---         if b > 2---         then return Nothing---         else return (Just (b, b + 1))--- in Stream.fold Fold.toList $ Stream.unfoldrM f 0--- :}--- [0,1,2]----{-# INLINE_NORMAL unfoldrM #-}-unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-unfoldrM next = unfold (Unfold.unfoldrM next)-#else-unfoldrM next = Stream step-  where-    {-# INLINE_LATE step #-}-    step _ st = do-        r <- next st-        return $ case r of-            Just (x, s) -> Yield x s-            Nothing     -> Stop-#endif---- |--- >>> :{--- unfoldr step s =---     case step s of---         Nothing -> Stream.nil---         Just (a, b) -> a `Stream.cons` unfoldr step b--- :}------ Build a stream by unfolding a /pure/ step function @step@ starting from a--- seed @s@.  The step function returns the next element in the stream and the--- next seed value. When it is done it returns 'Nothing' and the stream ends.--- For example,------ >>> :{--- let f b =---         if b > 2---         then Nothing---         else Just (b, b + 1)--- in Stream.fold Fold.toList $ Stream.unfoldr f 0--- :}--- [0,1,2]----{-# INLINE_LATE unfoldr #-}-unfoldr :: Monad m => (s -> Maybe (a, s)) -> s -> Stream m a-unfoldr f = unfoldrM (return . f)----------------------------------------------------------------------------------- From values----------------------------------------------------------------------------------- |--- >>> repeatM = Stream.sequence . Stream.repeat--- >>> repeatM = fix . Stream.consM--- >>> repeatM = cycle1 . Stream.fromEffect------ Generate a stream by repeatedly executing a monadic action forever.------ >>> :{--- repeatAction =---        Stream.repeatM (threadDelay 1000000 >> print 1)---      & Stream.take 10---      & Stream.fold Fold.drain--- :}----{-# INLINE_NORMAL repeatM #-}-repeatM :: Monad m => m a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-repeatM = unfold Unfold.repeatM-#else-repeatM x = Stream (\_ _ -> x >>= \r -> return $ Yield r ()) ()-#endif---- |--- Generate an infinite stream by repeating a pure value.------ >>> repeat x = Stream.repeatM (pure x)----{-# INLINE_NORMAL repeat #-}-repeat :: Monad m => a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-repeat x = repeatM (pure x)-#else-repeat x = Stream (\_ _ -> return $ Yield x ()) ()-#endif---- Adapted from the vector package---- |--- >>> replicateM n = Stream.sequence . Stream.replicate n------ Generate a stream by performing a monadic action @n@ times.-{-# INLINE_NORMAL replicateM #-}-replicateM :: Monad m => Int -> m a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-replicateM n p = unfold Unfold.replicateM (n, p)-#else-replicateM n p = Stream step n-  where-    {-# INLINE_LATE step #-}-    step _ (i :: Int)-      | i <= 0    = return Stop-      | otherwise = do-          x <- p-          return $ Yield x (i - 1)-#endif---- |--- >>> replicate n = Stream.take n . Stream.repeat--- >>> replicate n x = Stream.replicateM n (pure x)------ Generate a stream of length @n@ by repeating a value @n@ times.----{-# INLINE_NORMAL replicate #-}-replicate :: Monad m => Int -> a -> Stream m a-replicate n x = replicateM n (return x)----------------------------------------------------------------------------------- Enumeration of Num----------------------------------------------------------------------------------- | For floating point numbers if the increment is less than the precision then--- it just gets lost. Therefore we cannot always increment it correctly by just--- repeated addition.--- 9007199254740992 + 1 + 1 :: Double => 9.007199254740992e15--- 9007199254740992 + 2     :: Double => 9.007199254740994e15------ Instead we accumulate the increment counter and compute the increment--- every time before adding it to the starting number.------ This works for Integrals as well as floating point numbers, but--- enumerateFromStepIntegral is faster for integrals.-{-# INLINE_NORMAL enumerateFromStepNum #-}-enumerateFromStepNum :: (Monad m, Num a) => a -> a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-enumerateFromStepNum from stride =-    unfold Unfold.enumerateFromStepNum (from, stride)-#else-enumerateFromStepNum from stride = Stream step 0-    where-    {-# INLINE_LATE step #-}-    step _ !i = return $ (Yield $! (from + i * stride)) $! (i + 1)-#endif--{-# INLINE_NORMAL enumerateFromNum #-}-enumerateFromNum :: (Monad m, Num a) => a -> Stream m a-enumerateFromNum from = enumerateFromStepNum from 1--{-# INLINE_NORMAL enumerateFromThenNum #-}-enumerateFromThenNum :: (Monad m, Num a) => a -> a -> Stream m a-enumerateFromThenNum from next = enumerateFromStepNum from (next - from)----------------------------------------------------------------------------------- Enumeration of Integrals---------------------------------------------------------------------------------#ifndef USE_UNFOLDS_EVERYWHERE-data EnumState a = EnumInit | EnumYield a a a | EnumStop--{-# INLINE_NORMAL enumerateFromThenToIntegralUp #-}-enumerateFromThenToIntegralUp-    :: (Monad m, Integral a)-    => a -> a -> a -> Stream m a-enumerateFromThenToIntegralUp from next to = Stream step EnumInit-    where-    {-# INLINE_LATE step #-}-    step _ EnumInit =-        return $-            if to < next-            then if to < from-                 then Stop-                 else Yield from EnumStop-            else -- from <= next <= to-                let stride = next - from-                in Skip $ EnumYield from stride (to - stride)--    step _ (EnumYield x stride toMinus) =-        return $-            if x > toMinus-            then Yield x EnumStop-            else Yield x $ EnumYield (x + stride) stride toMinus--    step _ EnumStop = return Stop--{-# INLINE_NORMAL enumerateFromThenToIntegralDn #-}-enumerateFromThenToIntegralDn-    :: (Monad m, Integral a)-    => a -> a -> a -> Stream m a-enumerateFromThenToIntegralDn from next to = Stream step EnumInit-    where-    {-# INLINE_LATE step #-}-    step _ EnumInit =-        return $ if to > next-            then if to > from-                 then Stop-                 else Yield from EnumStop-            else -- from >= next >= to-                let stride = next - from-                in Skip $ EnumYield from stride (to - stride)--    step _ (EnumYield x stride toMinus) =-        return $-            if x < toMinus-            then Yield x EnumStop-            else Yield x $ EnumYield (x + stride) stride toMinus--    step _ EnumStop = return Stop-#endif---- XXX This can perhaps be simplified and written in terms of--- enumeratFromStepIntegral as we have done in unfolds.---- | Enumerate an 'Integral' type in steps up to a given limit.--- @enumerateFromThenToIntegral from then to@ generates a finite stream whose--- first element is @from@, the second element is @then@ and the successive--- elements are in increments of @then - from@ up to @to@.------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 2 6--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToIntegral 0 (-2) (-6)--- [0,-2,-4,-6]----{-# INLINE_NORMAL enumerateFromThenToIntegral #-}-enumerateFromThenToIntegral-    :: (Monad m, Integral a)-    => a -> a -> a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-enumerateFromThenToIntegral from next to =-    unfold Unfold.enumerateFromThenToIntegral (from, next, to)-#else-enumerateFromThenToIntegral from next to-    | next >= from = enumerateFromThenToIntegralUp from next to-    | otherwise    = enumerateFromThenToIntegralDn from next to-#endif---- | Enumerate an 'Integral' type in steps. @enumerateFromThenIntegral from--- then@ generates a stream whose first element is @from@, the second element--- is @then@ and the successive elements are in increments of @then - from@.--- The stream is bounded by the size of the 'Integral' type.------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) 2--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenIntegral (0 :: Int) (-2)--- [0,-2,-4,-6]----{-# INLINE_NORMAL enumerateFromThenIntegral #-}-enumerateFromThenIntegral-    :: (Monad m, Integral a, Bounded a)-    => a -> a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-enumerateFromThenIntegral from next =-    unfold Unfold.enumerateFromThenIntegralBounded (from, next)-#else-enumerateFromThenIntegral from next =-    if next > from-    then enumerateFromThenToIntegralUp from next maxBound-    else enumerateFromThenToIntegralDn from next minBound-#endif---- | @enumerateFromStepIntegral from step@ generates an infinite stream whose--- first element is @from@ and the successive elements are in increments of--- @step@.------ CAUTION: This function is not safe for finite integral types. It does not--- check for overflow, underflow or bounds.------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromStepIntegral 0 2--- [0,2,4,6]------ >>> Stream.fold Fold.toList $ Stream.take 3 $ Stream.enumerateFromStepIntegral 0 (-2)--- [0,-2,-4]----{-# INLINE_NORMAL enumerateFromStepIntegral #-}-enumerateFromStepIntegral :: (Integral a, Monad m) => a -> a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-enumerateFromStepIntegral from stride =-    unfold Unfold.enumerateFromStepIntegral (from, stride)-#else-enumerateFromStepIntegral from stride =-    from `seq` stride `seq` Stream step from-    where-        {-# INLINE_LATE step #-}-        step _ !x = return $ Yield x $! (x + stride)-#endif---- | Enumerate an 'Integral' type up to a given limit.--- @enumerateFromToIntegral from to@ generates a finite stream whose first--- element is @from@ and successive elements are in increments of @1@ up to--- @to@.------ >>> Stream.fold Fold.toList $ Stream.enumerateFromToIntegral 0 4--- [0,1,2,3,4]----{-# INLINE enumerateFromToIntegral #-}-enumerateFromToIntegral :: (Monad m, Integral a) => a -> a -> Stream m a-enumerateFromToIntegral from to =-    takeWhile (<= to) $ enumerateFromStepIntegral from 1---- | Enumerate an 'Integral' type. @enumerateFromIntegral from@ generates a--- stream whose first element is @from@ and the successive elements are in--- increments of @1@. The stream is bounded by the size of the 'Integral' type.------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromIntegral (0 :: Int)--- [0,1,2,3]----{-# INLINE enumerateFromIntegral #-}-enumerateFromIntegral :: (Monad m, Integral a, Bounded a) => a -> Stream m a-enumerateFromIntegral from = enumerateFromToIntegral from maxBound----------------------------------------------------------------------------------- Enumeration of Fractionals----------------------------------------------------------------------------------- We cannot write a general function for Num.  The only way to write code--- portable between the two is to use a 'Real' constraint and convert between--- Fractional and Integral using fromRational which is horribly slow.---- Even though the underlying implementation of enumerateFromFractional and--- enumerateFromThenFractional works for any 'Num' we have restricted these to--- 'Fractional' because these do not perform any bounds check, in contrast to--- integral versions and are therefore not equivalent substitutes for those.---- | Numerically stable enumeration from a 'Fractional' number in steps of size--- @1@. @enumerateFromFractional from@ generates a stream whose first element--- is @from@ and the successive elements are in increments of @1@.  No overflow--- or underflow checks are performed.------ This is the equivalent to 'enumFrom' for 'Fractional' types. For example:------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromFractional 1.1--- [1.1,2.1,3.1,4.1]----{-# INLINE enumerateFromFractional #-}-enumerateFromFractional :: (Monad m, Fractional a) => a -> Stream m a-enumerateFromFractional = enumerateFromNum---- | Numerically stable enumeration from a 'Fractional' number in steps.--- @enumerateFromThenFractional from then@ generates a stream whose first--- element is @from@, the second element is @then@ and the successive elements--- are in increments of @then - from@.  No overflow or underflow checks are--- performed.------ This is the equivalent of 'enumFromThen' for 'Fractional' types. For--- example:------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 2.1--- [1.1,2.1,3.1,4.1]------ >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThenFractional 1.1 (-2.1)--- [1.1,-2.1,-5.300000000000001,-8.500000000000002]----{-# INLINE enumerateFromThenFractional #-}-enumerateFromThenFractional-    :: (Monad m, Fractional a)-    => a -> a -> Stream m a-enumerateFromThenFractional = enumerateFromThenNum---- | Numerically stable enumeration from a 'Fractional' number to a given--- limit.  @enumerateFromToFractional from to@ generates a finite stream whose--- first element is @from@ and successive elements are in increments of @1@ up--- to @to@.------ This is the equivalent of 'enumFromTo' for 'Fractional' types. For--- example:------ >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4--- [1.1,2.1,3.1,4.1]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromToFractional 1.1 4.6--- [1.1,2.1,3.1,4.1,5.1]------ Notice that the last element is equal to the specified @to@ value after--- rounding to the nearest integer.----{-# INLINE_NORMAL enumerateFromToFractional #-}-enumerateFromToFractional-    :: (Monad m, Fractional a, Ord a)-    => a -> a -> Stream m a-enumerateFromToFractional from to =-    takeWhile (<= to + 1 / 2) $ enumerateFromStepNum from 1---- | Numerically stable enumeration from a 'Fractional' number in steps up to a--- given limit.  @enumerateFromThenToFractional from then to@ generates a--- finite stream whose first element is @from@, the second element is @then@--- and the successive elements are in increments of @then - from@ up to @to@.------ This is the equivalent of 'enumFromThenTo' for 'Fractional' types. For--- example:------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 2 6--- [0.1,2.0,3.9,5.799999999999999]------ >>> Stream.fold Fold.toList $ Stream.enumerateFromThenToFractional 0.1 (-2) (-6)--- [0.1,-2.0,-4.1000000000000005,-6.200000000000001]----{-# INLINE_NORMAL enumerateFromThenToFractional #-}-enumerateFromThenToFractional-    :: (Monad m, Fractional a, Ord a)-    => a -> a -> a -> Stream m a-enumerateFromThenToFractional from next to =-    takeWhile predicate $ enumerateFromThenFractional from next-    where-    mid = (next - from) / 2-    predicate | next >= from  = (<= to + mid)-              | otherwise     = (>= to + mid)------------------------------------------------------------------------------------ Enumeration of Enum types not larger than Int-------------------------------------------------------------------------------------- | 'enumerateFromTo' for 'Enum' types not larger than 'Int'.----{-# INLINE enumerateFromToSmall #-}-enumerateFromToSmall :: (Monad m, Enum a) => a -> a -> Stream m a-enumerateFromToSmall from to =-      fmap toEnum-    $ enumerateFromToIntegral (fromEnum from) (fromEnum to)---- | 'enumerateFromThenTo' for 'Enum' types not larger than 'Int'.----{-# INLINE enumerateFromThenToSmall #-}-enumerateFromThenToSmall :: (Monad m, Enum a)-    => a -> a -> a -> Stream m a-enumerateFromThenToSmall from next to =-          fmap toEnum-        $ enumerateFromThenToIntegral-            (fromEnum from) (fromEnum next) (fromEnum to)---- | 'enumerateFromThen' for 'Enum' types not larger than 'Int'.------ Note: We convert the 'Enum' to 'Int' and enumerate the 'Int'. If a--- type is bounded but does not have a 'Bounded' instance then we can go on--- enumerating it beyond the legal values of the type, resulting in the failure--- of 'toEnum' when converting back to 'Enum'. Therefore we require a 'Bounded'--- instance for this function to be safely used.----{-# INLINE enumerateFromThenSmallBounded #-}-enumerateFromThenSmallBounded :: (Monad m, Enumerable a, Bounded a)-    => a -> a -> Stream m a-enumerateFromThenSmallBounded from next =-    if fromEnum next >= fromEnum from-    then enumerateFromThenTo from next maxBound-    else enumerateFromThenTo from next minBound------------------------------------------------------------------------------------ Enumerable type class-------------------------------------------------------------------------------------- NOTE: We would like to rewrite calls to fromList [1..] etc. to stream--- enumerations like this:------ {-# RULES "fromList enumFrom" [1]---     forall (a :: Int). D.fromList (enumFrom a) = D.enumerateFromIntegral a #-}------ But this does not work because enumFrom is a class method and GHC rewrites--- it quickly, so we do not get a chance to have our rule fired.---- | Types that can be enumerated as a stream. The operations in this type--- class are equivalent to those in the 'Enum' type class, except that these--- generate a stream instead of a list. Use the functions in--- "Streamly.Internal.Data.Stream.Enumeration" module to define new instances.----class Enum a => Enumerable a where-    -- | @enumerateFrom from@ generates a stream starting with the element-    -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or-    -- generating an infinite stream when the type is not 'Bounded'.-    ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom (0 :: Int)-    -- [0,1,2,3]-    ---    -- For 'Fractional' types, enumeration is numerically stable. However, no-    -- overflow or underflow checks are performed.-    ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFrom 1.1-    -- [1.1,2.1,3.1,4.1]-    ---    enumerateFrom :: (Monad m) => a -> Stream m a--    -- | Generate a finite stream starting with the element @from@, enumerating-    -- the type up to the value @to@. If @to@ is smaller than @from@ then an-    -- empty stream is returned.-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 0 4-    -- [0,1,2,3,4]-    ---    -- For 'Fractional' types, the last element is equal to the specified @to@-    -- value after rounding to the nearest integral value.-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4-    -- [1.1,2.1,3.1,4.1]-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromTo 1.1 4.6-    -- [1.1,2.1,3.1,4.1,5.1]-    ---    enumerateFromTo :: (Monad m) => a -> a -> Stream m a--    -- | @enumerateFromThen from then@ generates a stream whose first element-    -- is @from@, the second element is @then@ and the successive elements are-    -- in increments of @then - from@.  Enumeration can occur downwards or-    -- upwards depending on whether @then@ comes before or after @from@. For-    -- 'Bounded' types the stream ends when 'maxBound' is reached, for-    -- unbounded types it keeps enumerating infinitely.-    ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 2-    -- [0,2,4,6]-    ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.enumerateFromThen 0 (-2)-    -- [0,-2,-4,-6]-    ---    enumerateFromThen :: (Monad m) => a -> a -> Stream m a--    -- | @enumerateFromThenTo from then to@ generates a finite stream whose-    -- first element is @from@, the second element is @then@ and the successive-    -- elements are in increments of @then - from@ up to @to@. Enumeration can-    -- occur downwards or upwards depending on whether @then@ comes before or-    -- after @from@.-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 2 6-    -- [0,2,4,6]-    ---    -- >>> Stream.fold Fold.toList $ Stream.enumerateFromThenTo 0 (-2) (-6)-    -- [0,-2,-4,-6]-    ---    enumerateFromThenTo :: (Monad m) => a -> a -> a -> Stream m a---- MAYBE: Sometimes it is more convenient to know the count rather then the--- ending or starting element. For those cases we can define the folllowing--- APIs. All of these will work only for bounded types if we represent the--- count by Int.------ enumerateN--- enumerateFromN--- enumerateToN--- enumerateFromStep--- enumerateFromStepN------------------------------------------------------------------------------------ Convenient functions for bounded types-------------------------------------------------------------------------------------- |--- > enumerate = enumerateFrom minBound------ Enumerate a 'Bounded' type from its 'minBound' to 'maxBound'----{-# INLINE enumerate #-}-enumerate :: (Monad m, Bounded a, Enumerable a) => Stream m a-enumerate = enumerateFrom minBound---- |--- >>> enumerateTo = Stream.enumerateFromTo minBound------ Enumerate a 'Bounded' type from its 'minBound' to specified value.----{-# INLINE enumerateTo #-}-enumerateTo :: (Monad m, Bounded a, Enumerable a) => a -> Stream m a-enumerateTo = enumerateFromTo minBound---- |--- >>> enumerateFromBounded from = Stream.enumerateFromTo from maxBound------ 'enumerateFrom' for 'Bounded' 'Enum' types.----{-# INLINE enumerateFromBounded #-}-enumerateFromBounded :: (Monad m, Enumerable a, Bounded a)-    => a -> Stream m a-enumerateFromBounded from = enumerateFromTo from maxBound------------------------------------------------------------------------------------ Enumerable Instances-------------------------------------------------------------------------------------- For Enum types smaller than or equal to Int size.-#define ENUMERABLE_BOUNDED_SMALL(SMALL_TYPE)           \-instance Enumerable SMALL_TYPE where {                 \-    {-# INLINE enumerateFrom #-};                      \-    enumerateFrom = enumerateFromBounded;              \-    {-# INLINE enumerateFromThen #-};                  \-    enumerateFromThen = enumerateFromThenSmallBounded; \-    {-# INLINE enumerateFromTo #-};                    \-    enumerateFromTo = enumerateFromToSmall;            \-    {-# INLINE enumerateFromThenTo #-};                \-    enumerateFromThenTo = enumerateFromThenToSmall }--ENUMERABLE_BOUNDED_SMALL(())-ENUMERABLE_BOUNDED_SMALL(Bool)-ENUMERABLE_BOUNDED_SMALL(Ordering)-ENUMERABLE_BOUNDED_SMALL(Char)---- For bounded Integral Enum types, may be larger than Int.-#define ENUMERABLE_BOUNDED_INTEGRAL(INTEGRAL_TYPE)  \-instance Enumerable INTEGRAL_TYPE where {           \-    {-# INLINE enumerateFrom #-};                   \-    enumerateFrom = enumerateFromIntegral;          \-    {-# INLINE enumerateFromThen #-};               \-    enumerateFromThen = enumerateFromThenIntegral;  \-    {-# INLINE enumerateFromTo #-};                 \-    enumerateFromTo = enumerateFromToIntegral;      \-    {-# INLINE enumerateFromThenTo #-};             \-    enumerateFromThenTo = enumerateFromThenToIntegral }--ENUMERABLE_BOUNDED_INTEGRAL(Int)-ENUMERABLE_BOUNDED_INTEGRAL(Int8)-ENUMERABLE_BOUNDED_INTEGRAL(Int16)-ENUMERABLE_BOUNDED_INTEGRAL(Int32)-ENUMERABLE_BOUNDED_INTEGRAL(Int64)-ENUMERABLE_BOUNDED_INTEGRAL(Word)-ENUMERABLE_BOUNDED_INTEGRAL(Word8)-ENUMERABLE_BOUNDED_INTEGRAL(Word16)-ENUMERABLE_BOUNDED_INTEGRAL(Word32)-ENUMERABLE_BOUNDED_INTEGRAL(Word64)---- For unbounded Integral Enum types.-#define ENUMERABLE_UNBOUNDED_INTEGRAL(INTEGRAL_TYPE)              \-instance Enumerable INTEGRAL_TYPE where {                         \-    {-# INLINE enumerateFrom #-};                                 \-    enumerateFrom from = enumerateFromStepIntegral from 1;        \-    {-# INLINE enumerateFromThen #-};                             \-    enumerateFromThen from next =                                 \-        enumerateFromStepIntegral from (next - from);             \-    {-# INLINE enumerateFromTo #-};                               \-    enumerateFromTo = enumerateFromToIntegral;                    \-    {-# INLINE enumerateFromThenTo #-};                           \-    enumerateFromThenTo = enumerateFromThenToIntegral }--ENUMERABLE_UNBOUNDED_INTEGRAL(Integer)-ENUMERABLE_UNBOUNDED_INTEGRAL(Natural)--#define ENUMERABLE_FRACTIONAL(FRACTIONAL_TYPE,CONSTRAINT)         \-instance (CONSTRAINT) => Enumerable FRACTIONAL_TYPE where {     \-    {-# INLINE enumerateFrom #-};                                 \-    enumerateFrom = enumerateFromFractional;                      \-    {-# INLINE enumerateFromThen #-};                             \-    enumerateFromThen = enumerateFromThenFractional;              \-    {-# INLINE enumerateFromTo #-};                               \-    enumerateFromTo = enumerateFromToFractional;                  \-    {-# INLINE enumerateFromThenTo #-};                           \-    enumerateFromThenTo = enumerateFromThenToFractional }--ENUMERABLE_FRACTIONAL(Float,)-ENUMERABLE_FRACTIONAL(Double,)-ENUMERABLE_FRACTIONAL((Fixed a),HasResolution a)-ENUMERABLE_FRACTIONAL((Ratio a),Integral a)--instance Enumerable a => Enumerable (Identity a) where-    {-# INLINE enumerateFrom #-}-    enumerateFrom (Identity from) =-        fmap Identity $ enumerateFrom from-    {-# INLINE enumerateFromThen #-}-    enumerateFromThen (Identity from) (Identity next) =-        fmap Identity $ enumerateFromThen from next-    {-# INLINE enumerateFromTo #-}-    enumerateFromTo (Identity from) (Identity to) =-        fmap Identity $ enumerateFromTo from to-    {-# INLINE enumerateFromThenTo #-}-    enumerateFromThenTo (Identity from) (Identity next) (Identity to) =-          fmap Identity-        $ enumerateFromThenTo from next to---- TODO-{--instance Enumerable a => Enumerable (Last a)-instance Enumerable a => Enumerable (First a)-instance Enumerable a => Enumerable (Max a)-instance Enumerable a => Enumerable (Min a)-instance Enumerable a => Enumerable (Const a b)-instance Enumerable (f a) => Enumerable (Alt f a)-instance Enumerable (f a) => Enumerable (Ap f a)--}---------------------------------------------------------------------------------- Time Enumeration----------------------------------------------------------------------------------- | @timesWith g@ returns a stream of time value tuples. The first component--- of the tuple is an absolute time reference (epoch) denoting the start of the--- stream and the second component is a time relative to the reference.------ The argument @g@ specifies the granularity of the relative time in seconds.--- A lower granularity clock gives higher precision but is more expensive in--- terms of CPU usage. Any granularity lower than 1 ms is treated as 1 ms.------ >>> import Control.Concurrent (threadDelay)--- >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)--- >>> Stream.fold f $ Stream.take 3 $ Stream.timesWith 0.01--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE_NORMAL timesWith #-}-timesWith :: MonadIO m => Double -> Stream m (AbsTime, RelTime64)-timesWith g = Stream step Nothing--    where--    {-# INLINE_LATE step #-}-    step _ Nothing = do-        clock <- liftIO $ asyncClock Monotonic g-        a <- liftIO $ readClock clock-        return $ Skip $ Just (clock, a)--    step _ s@(Just (clock, t0)) = do-        a <- liftIO $ readClock clock-        -- XXX we can perhaps use an AbsTime64 using a 64 bit Int for-        -- efficiency.  or maybe we can use a representation using Double for-        -- floating precision time-        return $ Yield (toAbsTime t0, toRelTime64 (a - t0)) s---- | @absTimesWith g@ returns a stream of absolute timestamps using a clock of--- granularity @g@ specified in seconds. A low granularity clock is more--- expensive in terms of CPU usage.  Any granularity lower than 1 ms is treated--- as 1 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimesWith 0.01--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE absTimesWith #-}-absTimesWith :: MonadIO m => Double -> Stream m AbsTime-absTimesWith = fmap (uncurry addToAbsTime64) . timesWith---- | @relTimesWith g@ returns a stream of relative time values starting from 0,--- using a clock of granularity @g@ specified in seconds. A low granularity--- clock is more expensive in terms of CPU usage.  Any granularity lower than 1--- ms is treated as 1 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimesWith 0.01--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE relTimesWith #-}-relTimesWith :: MonadIO m => Double -> Stream m RelTime64-relTimesWith = fmap snd . timesWith---- | @times@ returns a stream of time value tuples with clock of 10 ms--- granularity. The first component of the tuple is an absolute time reference--- (epoch) denoting the start of the stream and the second component is a time--- relative to the reference.------ >>> f = Fold.drainMapM (\x -> print x >> threadDelay 1000000)--- >>> Stream.fold f $ Stream.take 3 $ Stream.times--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))--- (AbsTime (TimeSpec {sec = ..., nsec = ...}),RelTime64 (NanoSecond64 ...))------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE times #-}-times :: MonadIO m => Stream m (AbsTime, RelTime64)-times = timesWith 0.01---- | @absTimes@ returns a stream of absolute timestamps using a clock of 10 ms--- granularity.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.absTimes--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})--- AbsTime (TimeSpec {sec = ..., nsec = ...})------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE absTimes #-}-absTimes :: MonadIO m => Stream m AbsTime-absTimes = fmap (uncurry addToAbsTime64) times---- | @relTimes@ returns a stream of relative time values starting from 0,--- using a clock of granularity 10 ms.------ >>> f = Fold.drainMapM print--- >>> Stream.fold f $ Stream.delayPre 1 $ Stream.take 3 $ Stream.relTimes--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)--- RelTime64 (NanoSecond64 ...)------ Note: This API is not safe on 32-bit machines.------ /Pre-release/----{-# INLINE relTimes #-}-relTimes ::  MonadIO m => Stream m RelTime64-relTimes = fmap snd times---- | @durations g@ returns a stream of relative time values measuring the time--- elapsed since the immediate predecessor element of the stream was generated.--- The first element of the stream is always 0. @durations@ uses a clock of--- granularity @g@ specified in seconds. A low granularity clock is more--- expensive in terms of CPU usage. The minimum granularity is 1 millisecond.--- Durations lower than 1 ms will be 0.------ Note: This API is not safe on 32-bit machines.------ /Unimplemented/----{-# INLINE durations #-}-durations :: -- Monad m =>-    Double -> t m RelTime64-durations = undefined---- | Generate a singleton event at or after the specified absolute time. Note--- that this is different from a threadDelay, a threadDelay starts from the--- time when the action is evaluated, whereas if we use AbsTime based timeout--- it will immediately expire if the action is evaluated too late.------ /Unimplemented/----{-# INLINE timeout #-}-timeout :: -- Monad m =>-    AbsTime -> t m ()-timeout = undefined------------------------------------------------------------------------------------ From Generators----------------------------------------------------------------------------------{-# INLINE_NORMAL fromIndicesM #-}-fromIndicesM :: Monad m => (Int -> m a) -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-fromIndicesM gen = unfold (Unfold.fromIndicesM gen) 0-#else-fromIndicesM gen = Stream step 0-  where-    {-# INLINE_LATE step #-}-    step _ i = do-       x <- gen i-       return $ Yield x (i + 1)-#endif--{-# INLINE fromIndices #-}-fromIndices :: Monad m => (Int -> a) -> Stream m a-fromIndices gen = fromIndicesM (return . gen)---- Adapted from the vector package-{-# INLINE_NORMAL generateM #-}-generateM :: Monad m => Int -> (Int -> m a) -> Stream m a-generateM n gen = n `seq` Stream step 0-  where-    {-# INLINE_LATE step #-}-    step _ i | i < n     = do-                           x <- gen i-                           return $ Yield x (i + 1)-             | otherwise = return Stop--{-# INLINE generate #-}-generate :: Monad m => Int -> (Int -> a) -> Stream m a-generate n gen = generateM n (return . gen)------------------------------------------------------------------------------------ Iteration------------------------------------------------------------------------------------ |--- >>> iterateM f m = m >>= \a -> return a `Stream.consM` iterateM f (f a)------ Generate an infinite stream with the first element generated by the action--- @m@ and each successive element derived by applying the monadic function--- @f@ on the previous element.------ >>> :{--- Stream.iterateM (\x -> print x >> return (x + 1)) (return 0)---     & Stream.take 3---     & Stream.fold Fold.toList--- :}--- 0--- 1--- [0,1,2]----{-# INLINE_NORMAL iterateM #-}-iterateM :: Monad m => (a -> m a) -> m a -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-iterateM step = unfold (Unfold.iterateM step)-#else-iterateM step = Stream (\_ st -> st >>= \(!x) -> return $ Yield x (step x))-#endif---- |--- >>> iterate f x = x `Stream.cons` iterate f x------ Generate an infinite stream with @x@ as the first element and each--- successive element derived by applying the function @f@ on the previous--- element.------ >>> Stream.fold Fold.toList $ Stream.take 5 $ Stream.iterate (+1) 1--- [1,2,3,4,5]----{-# INLINE_NORMAL iterate #-}-iterate :: Monad m => (a -> a) -> a -> Stream m a-iterate step st = iterateM (return . step) (return st)------------------------------------------------------------------------------------ From containers------------------------------------------------------------------------------------ | Convert a list of monadic actions to a 'Stream'-{-# INLINE_LATE fromListM #-}-fromListM :: Monad m => [m a] -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-fromListM = unfold Unfold.fromListM-#else-fromListM = Stream step-  where-    {-# INLINE_LATE step #-}-    step _ (m:ms) = m >>= \x -> return $ Yield x ms-    step _ []     = return Stop-#endif---- |--- >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil------ Construct a stream from a 'Foldable' containing pure values:------ /WARNING: O(n^2), suitable only for a small number of--- elements in the stream/----{-# INLINE fromFoldable #-}-fromFoldable :: (Monad m, Foldable f) => f a -> Stream m a-fromFoldable = Prelude.foldr cons nil---- |--- >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil------ Construct a stream from a 'Foldable' containing pure values:------ /WARNING: O(n^2), suitable only for a small number of--- elements in the stream/----{-# INLINE fromFoldableM #-}-fromFoldableM :: (Monad m, Foldable f) => f (m a) -> Stream m a-fromFoldableM = Prelude.foldr consM nil------------------------------------------------------------------------------------ From pointers------------------------------------------------------------------------------------ | Keep reading 'Storable' elements from 'Ptr' onwards.------ /Unsafe:/ The caller is responsible for safe addressing.------ /Pre-release/-{-# INLINE fromPtr #-}-fromPtr :: forall m a. (MonadIO m, Storable a) => Ptr a -> Stream m a-fromPtr = Stream step--    where--    {-# INLINE_LATE step #-}-    step _ p = do-        x <- liftIO $ peek p-        return $ Yield x (PTR_NEXT(p, a))---- | Take @n@ 'Storable' elements starting from 'Ptr' onwards.------ >>> fromPtrN n = Stream.take n . Stream.fromPtr------ /Unsafe:/ The caller is responsible for safe addressing.------ /Pre-release/-{-# INLINE fromPtrN #-}-fromPtrN :: (MonadIO m, Storable a) => Int -> Ptr a -> Stream m a-fromPtrN n = take n . fromPtr---- | Read bytes from an 'Addr#' until a 0 byte is encountered, the 0 byte is--- not included in the stream.------ >>> :set -XMagicHash--- >>> fromByteStr# addr = Stream.takeWhile (/= 0) $ Stream.fromPtr $ Ptr addr------ /Unsafe:/ The caller is responsible for safe addressing.------ Note that this is completely safe when reading from Haskell string--- literals because they are guaranteed to be NULL terminated:------ >>> Stream.fold Fold.toList $ Stream.fromByteStr# "\1\2\3\0"#--- [1,2,3]----{-# INLINE fromByteStr# #-}-fromByteStr# :: MonadIO m => Addr# -> Stream m Word8-fromByteStr# addr =-    takeWhile (/= 0) $ fromPtr $ Ptr addr
− src/Streamly/Internal/Data/Stream/StreamD/Lift.hs
@@ -1,129 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Lift--- Copyright   : (c) 2018 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Transform the underlying monad of a stream.--module Streamly.Internal.Data.Stream.StreamD.Lift-    (-    -- * Generalize Inner Monad-      morphInner-    , generalizeInner--    -- * Transform Inner Monad-    , liftInnerWith-    , runInnerWith-    , runInnerWithState-    )-where--#include "inline.hs"--import Data.Functor.Identity (Identity(..))-import Streamly.Internal.Data.SVar.Type (adaptState)--import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"------------------------------------------------------------------------------------ Generalize Inner Monad------------------------------------------------------------------------------------ | Transform the inner monad of a stream using a natural transformation.------ Example, generalize the inner monad from Identity to any other:------ >>> generalizeInner = Stream.morphInner (return . runIdentity)------ Also known as hoist.----{-# INLINE_NORMAL morphInner #-}-morphInner :: Monad n => (forall x. m x -> n x) -> Stream m a -> Stream n a-morphInner f (Stream step state) = Stream step' state-    where-    {-# INLINE_LATE step' #-}-    step' gst st = do-        r <- f $ step (adaptState gst) st-        return $ case r of-            Yield x s -> Yield x s-            Skip  s   -> Skip s-            Stop      -> Stop---- | Generalize the inner monad of the stream from 'Identity' to any monad.------ Definition:------ >>> generalizeInner = Stream.morphInner (return . runIdentity)----{-# INLINE generalizeInner #-}-generalizeInner :: Monad m => Stream Identity a -> Stream m a-generalizeInner = morphInner (return . runIdentity)------------------------------------------------------------------------------------ Transform Inner Monad------------------------------------------------------------------------------------ | Lift the inner monad @m@ of a stream @Stream m a@ to @t m@ using the--- supplied lift function.----{-# INLINE_NORMAL liftInnerWith #-}-liftInnerWith :: (Monad (t m)) =>-    (forall b. m b -> t m b) -> Stream m a -> Stream (t m) a-liftInnerWith lift (Stream step state) = Stream step1 state--    where--    {-# INLINE_LATE step1 #-}-    step1 gst st = do-        r <- lift $ step (adaptState gst) st-        return $ case r of-            Yield x s -> Yield x s-            Skip s    -> Skip s-            Stop      -> Stop---- | Evaluate the inner monad of a stream using the supplied runner function.----{-# INLINE_NORMAL runInnerWith #-}-runInnerWith :: Monad m =>-    (forall b. t m b -> m b) -> Stream (t m) a -> Stream m a-runInnerWith run (Stream step state) = Stream step1 state--    where--    {-# INLINE_LATE step1 #-}-    step1 gst st = do-        r <- run $ step (adaptState gst) st-        return $ case r of-            Yield x s -> Yield x s-            Skip s -> Skip s-            Stop -> Stop---- | Evaluate the inner monad of a stream using the supplied stateful runner--- function and the initial state. The state returned by an invocation of the--- runner is supplied as input state to the next invocation.----{-# INLINE_NORMAL runInnerWithState #-}-runInnerWithState :: Monad m =>-    (forall b. s -> t m b -> m (b, s))-    -> m s-    -> Stream (t m) a-    -> Stream m (s, a)-runInnerWithState run initial (Stream step state) =-    Stream step1 (state, initial)--    where--    {-# INLINE_LATE step1 #-}-    step1 gst (st, action) = do-        sv <- action-        (r, !sv1) <- run sv (step (adaptState gst) st)-        return $ case r of-            Yield x s -> Yield (sv1, x) (s, return sv1)-            Skip s -> Skip (s, return sv1)-            Stop -> Stop
− src/Streamly/Internal/Data/Stream/StreamD/Nesting.hs
@@ -1,3111 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Nesting--- Copyright   : (c) 2018 Composewell Technologies---               (c) Roman Leshchinskiy 2008-2010--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ This module contains transformations involving multiple streams, unfolds or--- folds. There are two types of transformations generational or eliminational.--- Generational transformations are like the "Generate" module but they--- generate a stream by combining streams instead of elements. Eliminational--- transformations are like the "Eliminate" module but they transform a stream--- by eliminating parts of the stream instead of eliminating the whole stream.------ These combinators involve transformation, generation, elimination so can be--- classified under any of those.------ Ultimately these operations should be supported by Unfolds, Pipes and Folds,--- and this module may become redundant.---- The zipWithM combinator in this module has been adapted from the vector--- package (c) Roman Leshchinskiy.----module Streamly.Internal.Data.Stream.StreamD.Nesting-    (-    -- * Generate-    -- | Combining streams to generate streams.--    -- ** Combine Two Streams-    -- | Functions ending in the shape:-    ---    -- @t m a -> t m a -> t m a@.--    -- *** Appending-    -- | Append a stream after another. A special case of concatMap or-    -- unfoldMany.-      AppendState(..)-    , append--    -- *** Interleaving-    -- | Interleave elements from two streams alternately. A special case of-    -- unfoldInterleave.-    , InterleaveState(..)-    , interleave-    , interleaveMin-    , interleaveFst-    , interleaveFstSuffix--    -- *** Scheduling-    -- | Execute streams alternately irrespective of whether they generate-    -- elements or not. Note 'interleave' would execute a stream until it-    -- yields an element. A special case of unfoldRoundRobin.-    , roundRobin -- interleaveFair?/ParallelFair--    -- *** Zipping-    -- | Zip corresponding elements of two streams.-    , zipWith-    , zipWithM--    -- *** Merging-    -- | Interleave elements from two streams based on a condition.-    , mergeBy-    , mergeByM-    , mergeMinBy-    , mergeFstBy--    -- ** Combine N Streams-    -- | Functions generally ending in these shapes:-    ---    -- @-    -- concat: f (t m a) -> t m a-    -- concatMap: (a -> t m b) -> t m a -> t m b-    -- unfoldMany: Unfold m a b -> t m a -> t m b-    -- @--    -- *** ConcatMap-    -- | Generate streams by mapping a stream generator on each element of an-    -- input stream, append the resulting streams and flatten.-    , concatMap-    , concatMapM--    -- *** ConcatUnfold-    -- | Generate streams by using an unfold on each element of an input-    -- stream, append the resulting streams and flatten. A special case of-    -- gintercalate.-    , unfoldMany-    , ConcatUnfoldInterleaveState (..)-    , unfoldInterleave-    , unfoldRoundRobin--    -- *** Interpose-    -- | Like unfoldMany but intersperses an effect between the streams. A-    -- special case of gintercalate.-    , interpose-    , interposeM-    , interposeSuffix-    , interposeSuffixM--    -- *** Intercalate-    -- | Like unfoldMany but intersperses streams from another source between-    -- the streams from the first source.-    , gintercalate-    , gintercalateSuffix-    , intercalate-    , intercalateSuffix--    -- * Eliminate-    -- | Folding and Parsing chunks of streams to eliminate nested streams.-    -- Functions generally ending in these shapes:-    ---    -- @-    -- f (Fold m a b) -> t m a -> t m b-    -- f (Parser a m b) -> t m a -> t m b-    -- @--    -- ** Folding-    -- | Apply folds on a stream.-    , foldMany-    , refoldMany-    , foldSequence-    , foldIterateM-    , refoldIterateM--    -- ** Parsing-    -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or-    -- unfoldMany. concatMap generates a stream from single values in a-    -- stream and flattens, parseMany does the opposite of flattening by-    -- splitting the stream and then folds each such split to single value in-    -- the output stream.-    , parseMany-    , parseManyD-    , parseSequence-    , parseManyTill-    , parseIterate-    , parseIterateD--    -- ** Grouping-    -- | Group segments of a stream and fold. Special case of parsing.-    , groupsOf-    , groupsBy-    , groupsRollingBy--    -- ** Splitting-    -- | A special case of parsing.-    , wordsBy-    , splitOnSeq-    , splitOnSuffixSeq-    , sliceOnSuffix--    -- XXX Implement these as folds or parsers instead.-    , splitOnSuffixSeqAny-    , splitOnPrefix-    , splitOnAny--    -- * Transform (Nested Containers)-    -- | Opposite to compact in ArrayStream-    , splitInnerBy-    , splitInnerBySuffix-    , intersectBySorted--    -- * Reduce By Streams-    , dropPrefix-    , dropInfix-    , dropSuffix-    )-where--#include "inline.hs"-#include "ArrayMacros.h"--import Control.Exception (assert)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Bits (shiftR, shiftL, (.|.), (.&.))-import Data.Proxy (Proxy(..))-import Data.Word (Word32)-import Foreign.Storable (Storable, peek)-import Fusion.Plugin.Types (Fuse(..))-import GHC.Types (SPEC(..))--import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Fold.Step (Step(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Parser (ParseError(..))-import Streamly.Internal.Data.Refold.Type (Refold(..))-import Streamly.Internal.Data.SVar.Type (adaptState)-import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))-import Streamly.Internal.Data.Unboxed (Unbox, sizeOf)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))--import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Fold as FL-import qualified Streamly.Internal.Data.Parser as PR-import qualified Streamly.Internal.Data.Parser.ParserD as PRD-import qualified Streamly.Internal.Data.Ring.Unboxed as RB--import Streamly.Internal.Data.Stream.StreamD.Transform-    (intersperse, intersperseMSuffix)-import Streamly.Internal.Data.Stream.StreamD.Type--import Prelude hiding (concatMap, mapM, zipWith)--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- Appending---------------------------------------------------------------------------------data AppendState s1 s2 = AppendFirst s1 | AppendSecond s2---- | Fuses two streams sequentially, yielding all elements from the first--- stream, and then all elements from the second stream.------ >>> s1 = Stream.fromList [1,2]--- >>> s2 = Stream.fromList [3,4]--- >>> Stream.fold Fold.toList $ s1 `Stream.append` s2--- [1,2,3,4]------ This function should not be used to dynamically construct a stream. If a--- stream is constructed by successive use of this function it would take--- quadratic time complexity to consume the stream.------ This function should only be used to statically fuse a stream with another--- stream. Do not use this recursively or where it cannot be inlined.------ See "Streamly.Data.StreamK" for an 'append' that can be used to--- construct a stream recursively.----{-# INLINE_NORMAL append #-}-append :: Monad m => Stream m a -> Stream m a -> Stream m a-append (Stream step1 state1) (Stream step2 state2) =-    Stream step (AppendFirst state1)--    where--    {-# INLINE_LATE step #-}-    step gst (AppendFirst st) = do-        r <- step1 gst st-        return $ case r of-            Yield a s -> Yield a (AppendFirst s)-            Skip s -> Skip (AppendFirst s)-            Stop -> Skip (AppendSecond state2)--    step gst (AppendSecond st) = do-        r <- step2 gst st-        return $ case r of-            Yield a s -> Yield a (AppendSecond s)-            Skip s -> Skip (AppendSecond s)-            Stop -> Stop----------------------------------------------------------------------------------- Interleaving---------------------------------------------------------------------------------data InterleaveState s1 s2 = InterleaveFirst s1 s2 | InterleaveSecond s1 s2-    | InterleaveSecondOnly s2 | InterleaveFirstOnly s1---- | Interleaves two streams, yielding one element from each stream--- alternately.  When one stream stops the rest of the other stream is used in--- the output stream.------ When joining many streams in a left associative manner earlier streams will--- get exponential priority than the ones joining later. Because of exponential--- weighting it can be used with 'concatMapWith' even on a large number of--- streams.----{-# INLINE_NORMAL interleave #-}-interleave :: Monad m => Stream m a -> Stream m a -> Stream m a-interleave (Stream step1 state1) (Stream step2 state2) =-    Stream step (InterleaveFirst state1 state2)--    where--    {-# INLINE_LATE step #-}-    step gst (InterleaveFirst st1 st2) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveSecond s st2)-            Skip s -> Skip (InterleaveFirst s st2)-            Stop -> Skip (InterleaveSecondOnly st2)--    step gst (InterleaveSecond st1 st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveFirst st1 s)-            Skip s -> Skip (InterleaveSecond st1 s)-            Stop -> Skip (InterleaveFirstOnly st1)--    step gst (InterleaveFirstOnly st1) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveFirstOnly s)-            Skip s -> Skip (InterleaveFirstOnly s)-            Stop -> Stop--    step gst (InterleaveSecondOnly st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveSecondOnly s)-            Skip s -> Skip (InterleaveSecondOnly s)-            Stop -> Stop---- | Like `interleave` but stops interleaving as soon as any of the two streams--- stops.----{-# INLINE_NORMAL interleaveMin #-}-interleaveMin :: Monad m => Stream m a -> Stream m a -> Stream m a-interleaveMin (Stream step1 state1) (Stream step2 state2) =-    Stream step (InterleaveFirst state1 state2)--    where--    {-# INLINE_LATE step #-}-    step gst (InterleaveFirst st1 st2) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveSecond s st2)-            Skip s -> Skip (InterleaveFirst s st2)-            Stop -> Stop--    step gst (InterleaveSecond st1 st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveFirst st1 s)-            Skip s -> Skip (InterleaveSecond st1 s)-            Stop -> Stop--    step _ (InterleaveFirstOnly _) =  undefined-    step _ (InterleaveSecondOnly _) =  undefined---- | Interleaves the outputs of two streams, yielding elements from each stream--- alternately, starting from the first stream. As soon as the first stream--- finishes, the output stops, discarding the remaining part of the second--- stream. In this case, the last element in the resulting stream would be from--- the second stream. If the second stream finishes early then the first stream--- still continues to yield elements until it finishes.------ >>> :set -XOverloadedStrings--- >>> import Data.Functor.Identity (Identity)--- >>> Stream.interleaveFstSuffix "abc" ",,,," :: Stream Identity Char--- fromList "a,b,c,"--- >>> Stream.interleaveFstSuffix "abc" "," :: Stream Identity Char--- fromList "a,bc"------ 'interleaveFstSuffix' is a dual of 'interleaveFst'.------ Do not use dynamically.------ /Pre-release/-{-# INLINE_NORMAL interleaveFstSuffix #-}-interleaveFstSuffix :: Monad m => Stream m a -> Stream m a -> Stream m a-interleaveFstSuffix (Stream step1 state1) (Stream step2 state2) =-    Stream step (InterleaveFirst state1 state2)--    where--    {-# INLINE_LATE step #-}-    step gst (InterleaveFirst st1 st2) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveSecond s st2)-            Skip s -> Skip (InterleaveFirst s st2)-            Stop -> Stop--    step gst (InterleaveSecond st1 st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveFirst st1 s)-            Skip s -> Skip (InterleaveSecond st1 s)-            Stop -> Skip (InterleaveFirstOnly st1)--    step gst (InterleaveFirstOnly st1) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveFirstOnly s)-            Skip s -> Skip (InterleaveFirstOnly s)-            Stop -> Stop--    step _ (InterleaveSecondOnly _) =  undefined--data InterleaveInfixState s1 s2 a-    = InterleaveInfixFirst s1 s2-    | InterleaveInfixSecondBuf s1 s2-    | InterleaveInfixSecondYield s1 s2 a-    | InterleaveInfixFirstYield s1 s2 a-    | InterleaveInfixFirstOnly s1---- | Interleaves the outputs of two streams, yielding elements from each stream--- alternately, starting from the first stream and ending at the first stream.--- If the second stream is longer than the first, elements from the second--- stream are infixed with elements from the first stream. If the first stream--- is longer then it continues yielding elements even after the second stream--- has finished.------ >>> :set -XOverloadedStrings--- >>> import Data.Functor.Identity (Identity)--- >>> Stream.interleaveFst "abc" ",,,," :: Stream Identity Char--- fromList "a,b,c"--- >>> Stream.interleaveFst "abc" "," :: Stream Identity Char--- fromList "a,bc"------ 'interleaveFst' is a dual of 'interleaveFstSuffix'.------ Do not use dynamically.------ /Pre-release/-{-# INLINE_NORMAL interleaveFst #-}-interleaveFst :: Monad m => Stream m a -> Stream m a -> Stream m a-interleaveFst (Stream step1 state1) (Stream step2 state2) =-    Stream step (InterleaveInfixFirst state1 state2)--    where--    {-# INLINE_LATE step #-}-    step gst (InterleaveInfixFirst st1 st2) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveInfixSecondBuf s st2)-            Skip s -> Skip (InterleaveInfixFirst s st2)-            Stop -> Stop--    step gst (InterleaveInfixSecondBuf st1 st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Skip (InterleaveInfixSecondYield st1 s a)-            Skip s -> Skip (InterleaveInfixSecondBuf st1 s)-            Stop -> Skip (InterleaveInfixFirstOnly st1)--    step gst (InterleaveInfixSecondYield st1 st2 x) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield x (InterleaveInfixFirstYield s st2 a)-            Skip s -> Skip (InterleaveInfixSecondYield s st2 x)-            Stop -> Stop--    step _ (InterleaveInfixFirstYield st1 st2 x) = do-        return $ Yield x (InterleaveInfixSecondBuf st1 st2)--    step gst (InterleaveInfixFirstOnly st1) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveInfixFirstOnly s)-            Skip s -> Skip (InterleaveInfixFirstOnly s)-            Stop -> Stop----------------------------------------------------------------------------------- Scheduling----------------------------------------------------------------------------------- | Schedule the execution of two streams in a fair round-robin manner,--- executing each stream once, alternately. Execution of a stream may not--- necessarily result in an output, a stream may choose to @Skip@ producing an--- element until later giving the other stream a chance to run. Therefore, this--- combinator fairly interleaves the execution of two streams rather than--- fairly interleaving the output of the two streams. This can be useful in--- co-operative multitasking without using explicit threads. This can be used--- as an alternative to `async`.------ Do not use dynamically.------ /Pre-release/-{-# INLINE_NORMAL roundRobin #-}-roundRobin :: Monad m => Stream m a -> Stream m a -> Stream m a-roundRobin (Stream step1 state1) (Stream step2 state2) =-    Stream step (InterleaveFirst state1 state2)--    where--    {-# INLINE_LATE step #-}-    step gst (InterleaveFirst st1 st2) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveSecond s st2)-            Skip s -> Skip (InterleaveSecond s st2)-            Stop -> Skip (InterleaveSecondOnly st2)--    step gst (InterleaveSecond st1 st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveFirst st1 s)-            Skip s -> Skip (InterleaveFirst st1 s)-            Stop -> Skip (InterleaveFirstOnly st1)--    step gst (InterleaveSecondOnly st2) = do-        r <- step2 gst st2-        return $ case r of-            Yield a s -> Yield a (InterleaveSecondOnly s)-            Skip s -> Skip (InterleaveSecondOnly s)-            Stop -> Stop--    step gst (InterleaveFirstOnly st1) = do-        r <- step1 gst st1-        return $ case r of-            Yield a s -> Yield a (InterleaveFirstOnly s)-            Skip s -> Skip (InterleaveFirstOnly s)-            Stop -> Stop----------------------------------------------------------------------------------- Merging----------------------------------------------------------------------------------- | Like 'mergeBy' but with a monadic comparison function.------ Merge two streams randomly:------ @--- > randomly _ _ = randomIO >>= \x -> return $ if x then LT else GT--- > Stream.toList $ Stream.mergeByM randomly (Stream.fromList [1,1,1,1]) (Stream.fromList [2,2,2,2])--- [2,1,2,2,2,1,1,1]--- @------ Merge two streams in a proportion of 2:1:------ >>> :{--- do---  let s1 = Stream.fromList [1,1,1,1,1,1]---      s2 = Stream.fromList [2,2,2]---  let proportionately m n = do---       ref <- newIORef $ cycle $ Prelude.concat [Prelude.replicate m LT, Prelude.replicate n GT]---       return $ \_ _ -> do---          r <- readIORef ref---          writeIORef ref $ Prelude.tail r---          return $ Prelude.head r---  f <- proportionately 2 1---  xs <- Stream.fold Fold.toList $ Stream.mergeByM f s1 s2---  print xs--- :}--- [1,1,2,1,1,2,1,1,2]----{-# INLINE_NORMAL mergeByM #-}-mergeByM-    :: (Monad m)-    => (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeByM cmp (Stream stepa ta) (Stream stepb tb) =-    Stream step (Just ta, Just tb, Nothing, Nothing)-  where-    {-# INLINE_LATE step #-}--    -- one of the values is missing, and the corresponding stream is running-    step gst (Just sa, sb, Nothing, b) = do-        r <- stepa gst sa-        return $ case r of-            Yield a sa' -> Skip (Just sa', sb, Just a, b)-            Skip sa'    -> Skip (Just sa', sb, Nothing, b)-            Stop        -> Skip (Nothing, sb, Nothing, b)--    step gst (sa, Just sb, a, Nothing) = do-        r <- stepb gst sb-        return $ case r of-            Yield b sb' -> Skip (sa, Just sb', a, Just b)-            Skip sb'    -> Skip (sa, Just sb', a, Nothing)-            Stop        -> Skip (sa, Nothing, a, Nothing)--    -- both the values are available-    step _ (sa, sb, Just a, Just b) = do-        res <- cmp a b-        return $ case res of-            GT -> Yield b (sa, sb, Just a, Nothing)-            _  -> Yield a (sa, sb, Nothing, Just b)--    -- one of the values is missing, corresponding stream is done-    step _ (Nothing, sb, Nothing, Just b) =-            return $ Yield b (Nothing, sb, Nothing, Nothing)--    step _ (sa, Nothing, Just a, Nothing) =-            return $ Yield a (sa, Nothing, Nothing, Nothing)--    step _ (Nothing, Nothing, Nothing, Nothing) = return Stop---- | Merge two streams using a comparison function. The head elements of both--- the streams are compared and the smaller of the two elements is emitted, if--- both elements are equal then the element from the first stream is used--- first.------ If the streams are sorted in ascending order, the resulting stream would--- also remain sorted in ascending order.------ >>> s1 = Stream.fromList [1,3,5]--- >>> s2 = Stream.fromList [2,4,6,8]--- >>> Stream.fold Fold.toList $ Stream.mergeBy compare s1 s2--- [1,2,3,4,5,6,8]----{-# INLINE mergeBy #-}-mergeBy-    :: (Monad m)-    => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeBy cmp = mergeByM (\a b -> return $ cmp a b)---- | Like 'mergeByM' but stops merging as soon as any of the two streams stops.------ /Unimplemented/-{-# INLINABLE mergeMinBy #-}-mergeMinBy :: -- Monad m =>-    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeMinBy _f _m1 _m2 = undefined-    -- fromStreamD $ D.mergeMinBy f (toStreamD m1) (toStreamD m2)---- | Like 'mergeByM' but stops merging as soon as the first stream stops.------ /Unimplemented/-{-# INLINABLE mergeFstBy #-}-mergeFstBy :: -- Monad m =>-    (a -> a -> m Ordering) -> Stream m a -> Stream m a -> Stream m a-mergeFstBy _f _m1 _m2 = undefined-    -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)------------------------------------------------------------------------------------ Intersection of sorted streams------------------------------------------------------------------------------------ Assuming the streams are sorted in ascending order-{-# INLINE_NORMAL intersectBySorted #-}-intersectBySorted :: Monad m-    => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =-    Stream step-        ( ta -- left stream state-        , tb -- right stream state-        , Nothing -- left value-        , Nothing -- right value-        )--    where--    {-# INLINE_LATE step #-}-    -- step 1, fetch the first value-    step gst (sa, sb, Nothing, b) = do-        r <- stepa gst sa-        return $ case r of-            Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3-            Skip sa'    -> Skip (sa', sb, Nothing, b)-            Stop        -> Stop--    -- step 2, fetch the second value-    step gst (sa, sb, a@(Just _), Nothing) = do-        r <- stepb gst sb-        return $ case r of-            Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3-            Skip sb'    -> Skip (sa, sb', a, Nothing)-            Stop        -> Stop--    -- step 3, compare the two values-    step _ (sa, sb, Just a, Just b) = do-        let res = cmp a b-        return $ case res of-            GT -> Skip (sa, sb, Just a, Nothing) -- step 2-            LT -> Skip (sa, sb, Nothing, Just b) -- step 1-            EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1----------------------------------------------------------------------------------- Combine N Streams - unfoldMany---------------------------------------------------------------------------------data ConcatUnfoldInterleaveState o i =-      ConcatUnfoldInterleaveOuter o [i]-    | ConcatUnfoldInterleaveInner o [i]-    | ConcatUnfoldInterleaveInnerL [i] [i]-    | ConcatUnfoldInterleaveInnerR [i] [i]---- XXX use arrays to store state instead of lists?------ XXX In general we can use different scheduling strategies e.g. how to--- schedule the outer vs inner loop or assigning weights to different streams--- or outer and inner loops.---- After a yield, switch to the next stream. Do not switch streams on Skip.--- Yield from outer stream switches to the inner stream.------ There are two choices here, (1) exhaust the outer stream first and then--- start yielding from the inner streams, this is much simpler to implement,--- (2) yield at least one element from an inner stream before going back to--- outer stream and opening the next stream from it.------ Ideally, we need some scheduling bias to inner streams vs outer stream.--- Maybe we can configure the behavior.------ XXX Instead of using "concatPairsWith wSerial" we can implement an N-way--- interleaving CPS combinator which behaves like unfoldInterleave. Instead--- of pairing up the streams we just need to go yielding one element from each--- stream and storing the remaining streams and then keep doing rounds through--- those in a round robin fashion. This would be much like wAsync.---- | This does not pair streams like mergeMapWith, instead, it goes through--- each stream one by one and yields one element from each stream. After it--- goes to the last stream it reverses the traversal to come back to the first--- stream yielding elements from each stream on its way back to the first--- stream and so on.------ >>> lists = Stream.fromList [[1,1],[2,2],[3,3],[4,4],[5,5]]--- >>> interleaved = Stream.unfoldInterleave Unfold.fromList lists--- >>> Stream.fold Fold.toList interleaved--- [1,2,3,4,5,5,4,3,2,1]------ Note that this is order of magnitude more efficient than "mergeMapWith--- interleave" because of fusion.----{-# INLINE_NORMAL unfoldInterleave #-}-unfoldInterleave :: Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldInterleave (Unfold istep inject) (Stream ostep ost) =-    Stream step (ConcatUnfoldInterleaveOuter ost [])--    where--    {-# 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 [])--    step _ (ConcatUnfoldInterleaveInner _ []) = undefined-    step _ (ConcatUnfoldInterleaveInner o (st:ls)) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveOuter o (s:ls))-            Skip s    -> Skip (ConcatUnfoldInterleaveInner o (s:ls))-            Stop      -> Skip (ConcatUnfoldInterleaveOuter o ls)--    step _ (ConcatUnfoldInterleaveInnerL [] []) = return Stop-    step _ (ConcatUnfoldInterleaveInnerL [] rs) =-        return $ Skip (ConcatUnfoldInterleaveInnerR [] rs)--    step _ (ConcatUnfoldInterleaveInnerL (st:ls) rs) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerL ls (s:rs))-            Skip s    -> Skip (ConcatUnfoldInterleaveInnerL (s:ls) rs)-            Stop      -> Skip (ConcatUnfoldInterleaveInnerL ls rs)--    step _ (ConcatUnfoldInterleaveInnerR [] []) = return Stop-    step _ (ConcatUnfoldInterleaveInnerR ls []) =-        return $ Skip (ConcatUnfoldInterleaveInnerL ls [])--    step _ (ConcatUnfoldInterleaveInnerR ls (st:rs)) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerR (s:ls) rs)-            Skip s    -> Skip (ConcatUnfoldInterleaveInnerR ls (s:rs))-            Stop      -> Skip (ConcatUnfoldInterleaveInnerR ls rs)---- XXX In general we can use different scheduling strategies e.g. how to--- schedule the outer vs inner loop or assigning weights to different streams--- or outer and inner loops.------ This could be inefficient if the tasks are too small.------ Compared to unfoldInterleave this one switches streams on Skips.---- | 'unfoldInterleave' switches to the next stream whenever a value from a--- stream is yielded, it does not switch on a 'Skip'. So if a stream keeps--- skipping for long time other streams won't get a chance to run.--- 'unfoldRoundRobin' switches on Skip as well. So it basically schedules each--- stream fairly irrespective of whether it produces a value or not.----{-# INLINE_NORMAL unfoldRoundRobin #-}-unfoldRoundRobin :: Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldRoundRobin (Unfold istep inject) (Stream ostep ost) =-    Stream step (ConcatUnfoldInterleaveOuter ost [])-  where-    {-# 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 (ConcatUnfoldInterleaveInner o' ls)-            Stop -> return $ Skip (ConcatUnfoldInterleaveInnerL ls [])--    step _ (ConcatUnfoldInterleaveInner o []) =-            return $ Skip (ConcatUnfoldInterleaveOuter o [])--    step _ (ConcatUnfoldInterleaveInner o (st:ls)) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveOuter o (s:ls))-            Skip s    -> Skip (ConcatUnfoldInterleaveOuter o (s:ls))-            Stop      -> Skip (ConcatUnfoldInterleaveOuter o ls)--    step _ (ConcatUnfoldInterleaveInnerL [] []) = return Stop-    step _ (ConcatUnfoldInterleaveInnerL [] rs) =-        return $ Skip (ConcatUnfoldInterleaveInnerR [] rs)--    step _ (ConcatUnfoldInterleaveInnerL (st:ls) rs) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerL ls (s:rs))-            Skip s    -> Skip (ConcatUnfoldInterleaveInnerL ls (s:rs))-            Stop      -> Skip (ConcatUnfoldInterleaveInnerL ls rs)--    step _ (ConcatUnfoldInterleaveInnerR [] []) = return Stop-    step _ (ConcatUnfoldInterleaveInnerR ls []) =-        return $ Skip (ConcatUnfoldInterleaveInnerL ls [])--    step _ (ConcatUnfoldInterleaveInnerR ls (st:rs)) = do-        r <- istep st-        return $ case r of-            Yield x s -> Yield x (ConcatUnfoldInterleaveInnerR (s:ls) rs)-            Skip s    -> Skip (ConcatUnfoldInterleaveInnerR (s:ls) rs)-            Stop      -> Skip (ConcatUnfoldInterleaveInnerR ls rs)----------------------------------------------------------------------------------- Combine N Streams - interpose---------------------------------------------------------------------------------{-# ANN type InterposeSuffixState Fuse #-}-data InterposeSuffixState s1 i1 =-      InterposeSuffixFirst s1-    -- | InterposeSuffixFirstYield s1 i1-    | InterposeSuffixFirstInner s1 i1-    | InterposeSuffixSecond s1---- Note that if an unfolded layer turns out to be nil we still emit the--- separator effect. An alternate behavior could be to emit the separator--- effect only if at least one element has been yielded by the unfolding.--- However, that becomes a bit complicated, so we have chosen the former--- behvaior for now.-{-# INLINE_NORMAL interposeSuffixM #-}-interposeSuffixM-    :: Monad m-    => m c -> Unfold m b c -> Stream m b -> Stream m c-interposeSuffixM-    action-    (Unfold istep1 inject1) (Stream step1 state1) =-    Stream step (InterposeSuffixFirst state1)--    where--    {-# 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--    {--    step _ (InterposeSuffixFirstYield s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')-            Skip i'    -> Skip (InterposeSuffixFirstYield s1 i')-            Stop       -> Skip (InterposeSuffixFirst s1)-    -}--    step _ (InterposeSuffixFirstInner s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (InterposeSuffixFirstInner s1 i')-            Skip i'    -> Skip (InterposeSuffixFirstInner s1 i')-            Stop       -> Skip (InterposeSuffixSecond s1)--    step _ (InterposeSuffixSecond s1) = do-        r <- action-        return $ Yield r (InterposeSuffixFirst s1)---- interposeSuffix x unf str = gintercalateSuffix unf str UF.identity (repeat x)---- | Unfold the elements of a stream, append the given element after each--- unfolded stream and then concat them into a single stream.------ >>> unlines = Stream.interposeSuffix '\n'------ /Pre-release/-{-# INLINE interposeSuffix #-}-interposeSuffix :: Monad m-    => c -> Unfold m b c -> Stream m b -> Stream m c-interposeSuffix x = interposeSuffixM (return x)--{-# ANN type InterposeState Fuse #-}-data InterposeState s1 i1 a =-      InterposeFirst s1-    -- | InterposeFirstYield s1 i1-    | InterposeFirstInner s1 i1-    | InterposeFirstInject s1-    -- | InterposeFirstBuf s1 i1-    | InterposeSecondYield s1 i1-    -- -- | InterposeSecondYield s1 i1 a-    -- -- | InterposeFirstResume s1 i1 a---- Note that this only interposes the pure values, we may run many effects to--- generate those values as some effects may not generate anything (Skip).-{-# INLINE_NORMAL interposeM #-}-interposeM :: Monad m => m c -> Unfold m b c -> Stream m b -> Stream m c-interposeM-    action-    (Unfold istep1 inject1) (Stream step1 state1) =-    Stream step (InterposeFirst state1)--    where--    {-# 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--    {--    step _ (InterposeFirstYield s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (InterposeFirstInner s1 i')-            Skip i'    -> Skip (InterposeFirstYield s1 i')-            Stop       -> Skip (InterposeFirst s1)-    -}--    step _ (InterposeFirstInner s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (InterposeFirstInner s1 i')-            Skip i'    -> Skip (InterposeFirstInner s1 i')-            Stop       -> Skip (InterposeFirstInject s1)--    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--    {--    step _ (InterposeFirstBuf s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Skip (InterposeSecondYield s1 i' x)-            Skip i'    -> Skip (InterposeFirstBuf s1 i')-            Stop       -> Stop-    -}--    {--    step _ (InterposeSecondYield s1 i1 v) = do-        r <- action-        return $ Yield r (InterposeFirstResume s1 i1 v)-    -}-    step _ (InterposeSecondYield s1 i1) = do-        r <- action-        return $ Yield r (InterposeFirstInner s1 i1)--    {--    step _ (InterposeFirstResume s1 i1 v) = do-        return $ Yield v (InterposeFirstInner s1 i1)-    -}---- > interpose x unf str = gintercalate unf str UF.identity (repeat x)---- | Unfold the elements of a stream, intersperse the given element between the--- unfolded streams and then concat them into a single stream.------ >>> unwords = Stream.interpose ' '------ /Pre-release/-{-# INLINE interpose #-}-interpose :: Monad m-    => c -> Unfold m b c -> Stream m b -> Stream m c-interpose x = interposeM (return x)----------------------------------------------------------------------------------- Combine N Streams - intercalate---------------------------------------------------------------------------------data ICUState s1 s2 i1 i2 =-      ICUFirst s1 s2-    | ICUSecond s1 s2-    | ICUSecondOnly s2-    | ICUFirstOnly s1-    | ICUFirstInner s1 s2 i1-    | ICUSecondInner s1 s2 i2-    | ICUFirstOnlyInner s1 i1-    | ICUSecondOnlyInner s2 i2---- | 'interleaveFstSuffix' followed by unfold and concat.------ /Pre-release/-{-# INLINE_NORMAL gintercalateSuffix #-}-gintercalateSuffix-    :: Monad m-    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c-gintercalateSuffix-    (Unfold istep1 inject1) (Stream step1 state1)-    (Unfold istep2 inject2) (Stream step2 state2) =-    Stream step (ICUFirst state1 state2)--    where--    {-# 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--    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--    step _ (ICUFirstInner s1 s2 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (ICUFirstInner s1 s2 i')-            Skip i'    -> Skip (ICUFirstInner s1 s2 i')-            Stop       -> Skip (ICUSecond s1 s2)--    step _ (ICUFirstOnlyInner s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (ICUFirstOnlyInner s1 i')-            Skip i'    -> Skip (ICUFirstOnlyInner s1 i')-            Stop       -> Skip (ICUFirstOnly s1)--    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)--    step _ (ICUSecondInner s1 s2 i2) = do-        r <- istep2 i2-        return $ case r of-            Yield x i' -> Yield x (ICUSecondInner s1 s2 i')-            Skip i'    -> Skip (ICUSecondInner s1 s2 i')-            Stop       -> Skip (ICUFirst s1 s2)--    step _ (ICUSecondOnly _s2) = undefined-    step _ (ICUSecondOnlyInner _s2 _i2) = undefined--data ICALState s1 s2 i1 i2 a =-      ICALFirst s1 s2-    -- | ICALFirstYield s1 s2 i1-    | ICALFirstInner s1 s2 i1-    | ICALFirstOnly s1-    | ICALFirstOnlyInner s1 i1-    | ICALSecondInject s1 s2-    | ICALFirstInject s1 s2 i2-    -- | ICALFirstBuf s1 s2 i1 i2-    | ICALSecondInner s1 s2 i1 i2-    -- -- | ICALSecondInner s1 s2 i1 i2 a-    -- -- | ICALFirstResume s1 s2 i1 i2 a---- XXX we can swap the order of arguments to gintercalate so that the--- definition of unfoldMany becomes simpler? The first stream should be--- infixed inside the second one. However, if we change the order in--- "interleave" as well similarly, then that will make it a bit unintuitive.------ > unfoldMany unf str =--- >     gintercalate unf str (UF.nilM (\_ -> return ())) (repeat ())---- | 'interleaveFst' followed by unfold and concat.------ /Pre-release/-{-# INLINE_NORMAL gintercalate #-}-gintercalate-    :: Monad m-    => Unfold m a c -> Stream m a -> Unfold m b c -> Stream m b -> Stream m c-gintercalate-    (Unfold istep1 inject1) (Stream step1 state1)-    (Unfold istep2 inject2) (Stream step2 state2) =-    Stream step (ICALFirst state1 state2)--    where--    {-# 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--    {--    step _ (ICALFirstYield s1 s2 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (ICALFirstInner s1 s2 i')-            Skip i'    -> Skip (ICALFirstYield s1 s2 i')-            Stop       -> Skip (ICALFirst s1 s2)-    -}--    step _ (ICALFirstInner s1 s2 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (ICALFirstInner s1 s2 i')-            Skip i'    -> Skip (ICALFirstInner s1 s2 i')-            Stop       -> Skip (ICALSecondInject s1 s2)--    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--    step _ (ICALFirstOnlyInner s1 i1) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Yield x (ICALFirstOnlyInner s1 i')-            Skip i'    -> Skip (ICALFirstOnlyInner s1 i')-            Stop       -> Skip (ICALFirstOnly s1)--    -- We inject the second stream even before checking if the first stream-    -- would yield any more elements. There is no clear choice whether we-    -- should do this before or after that. Doing it after may make the state-    -- 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)--    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--    {--    step _ (ICALFirstBuf s1 s2 i1 i2) = do-        r <- istep1 i1-        return $ case r of-            Yield x i' -> Skip (ICALSecondInner s1 s2 i' i2 x)-            Skip i'    -> Skip (ICALFirstBuf s1 s2 i' i2)-            Stop       -> Stop--    step _ (ICALSecondInner s1 s2 i1 i2 v) = do-        r <- istep2 i2-        return $ case r of-            Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i' v)-            Skip i'    -> Skip (ICALSecondInner s1 s2 i1 i' v)-            Stop       -> Skip (ICALFirstResume s1 s2 i1 i2 v)-    -}--    step _ (ICALSecondInner s1 s2 i1 i2) = do-        r <- istep2 i2-        return $ case r of-            Yield x i' -> Yield x (ICALSecondInner s1 s2 i1 i')-            Skip i'    -> Skip (ICALSecondInner s1 s2 i1 i')-            Stop       -> Skip (ICALFirstInner s1 s2 i1)-            -- Stop       -> Skip (ICALFirstResume s1 s2 i1 i2)--    {--    step _ (ICALFirstResume s1 s2 i1 i2 x) = do-        return $ Yield x (ICALFirstInner s1 s2 i1 i2)-    -}---- > intercalateSuffix unf seed str = gintercalateSuffix unf str unf (repeatM seed)---- | 'intersperseMSuffix' followed by unfold and concat.------ >>> intercalateSuffix u a = Stream.unfoldMany u . Stream.intersperseMSuffix a--- >>> intersperseMSuffix = Stream.intercalateSuffix Unfold.identity--- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"------ >>> input = Stream.fromList ["abc", "def", "ghi"]--- >>> Stream.fold Fold.toList $ Stream.intercalateSuffix Unfold.fromList "\n" input--- "abc\ndef\nghi\n"----{-# INLINE intercalateSuffix #-}-intercalateSuffix :: Monad m-    => Unfold m b c -> b -> Stream m b -> Stream m c-intercalateSuffix unf seed = unfoldMany unf . intersperseMSuffix (return seed)---- > intercalate unf seed str = gintercalate unf str unf (repeatM seed)---- | 'intersperse' followed by unfold and concat.------ >>> intercalate u a = Stream.unfoldMany u . Stream.intersperse a--- >>> intersperse = Stream.intercalate Unfold.identity--- >>> unwords = Stream.intercalate Unfold.fromList " "------ >>> input = Stream.fromList ["abc", "def", "ghi"]--- >>> Stream.fold Fold.toList $ Stream.intercalate Unfold.fromList " " input--- "abc def ghi"----{-# INLINE intercalate #-}-intercalate :: Monad m-    => Unfold m b c -> b -> Stream m b -> Stream m c-intercalate unf seed str = unfoldMany unf $ intersperse seed str----------------------------------------------------------------------------------- Folding----------------------------------------------------------------------------------- | Apply a stream of folds to an input stream and emit the results in the--- output stream.------ /Unimplemented/----{-# INLINE foldSequence #-}-foldSequence-       :: -- Monad m =>-       Stream m (Fold m a b)-    -> Stream m a-    -> Stream m b-foldSequence _f _m = undefined--{-# ANN type FIterState Fuse #-}-data FIterState s f m a b-    = FIterInit s f-    | forall fs. FIterStream s (fs -> a -> m (FL.Step fs b)) fs (fs -> m b)-    | FIterYield b (FIterState s f m a b)-    | FIterStop---- | Iterate a fold generator on a stream. The initial value @b@ is used to--- generate the first fold, the fold is applied on the stream and the result of--- the fold is used to generate the next fold and so on.------ >>> import Data.Monoid (Sum(..))--- >>> f x = return (Fold.take 2 (Fold.sconcat x))--- >>> s = fmap Sum $ Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.foldIterateM f (pure 0) s--- [3,10,21,36,55,55]------ This is the streaming equivalent of monad like sequenced application of--- folds where next fold is dependent on the previous fold.------ /Pre-release/----{-# INLINE_NORMAL foldIterateM #-}-foldIterateM ::-       Monad m => (b -> m (FL.Fold m a b)) -> m b -> Stream m a -> Stream m b-foldIterateM func seed0 (Stream step state) =-    Stream stepOuter (FIterInit state seed0)--    where--    {-# INLINE iterStep #-}-    iterStep from st fstep extract = do-        res <- from-        return-            $ Skip-            $ case res of-                  FL.Partial fs -> FIterStream st fstep fs extract-                  FL.Done fb -> FIterYield fb $ FIterInit st (return fb)--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (FIterInit st seed) = do-        (FL.Fold fstep initial extract) <- seed >>= func-        iterStep initial st fstep extract-    stepOuter gst (FIterStream st fstep fs extract) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                iterStep (fstep fs x) s fstep extract-            Skip s -> return $ Skip $ FIterStream s fstep fs extract-            Stop -> do-                b <- extract fs-                return $ Skip $ FIterYield b FIterStop-    stepOuter _ (FIterYield a next) = return $ Yield a next-    stepOuter _ FIterStop = return Stop--{-# ANN type CIterState Fuse #-}-data CIterState s f fs b-    = CIterInit s f-    | CIterConsume s fs-    | CIterYield b (CIterState s f fs b)-    | CIterStop---- | Like 'foldIterateM' but using the 'Refold' type instead. This could be--- much more efficient due to stream fusion.------ /Internal/-{-# INLINE_NORMAL refoldIterateM #-}-refoldIterateM ::-       Monad m => Refold m b a b -> m b -> Stream m a -> Stream m b-refoldIterateM (Refold fstep finject fextract) initial (Stream step state) =-    Stream stepOuter (CIterInit state initial)--    where--    {-# INLINE iterStep #-}-    iterStep st action = do-        res <- action-        return-            $ Skip-            $ case res of-                  FL.Partial fs -> CIterConsume st fs-                  FL.Done fb -> CIterYield fb $ CIterInit st (return fb)--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (CIterInit st action) = do-        iterStep st (action >>= finject)-    stepOuter gst (CIterConsume st fs) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> iterStep s (fstep fs x)-            Skip s -> return $ Skip $ CIterConsume s fs-            Stop -> do-                b <- fextract fs-                return $ Skip $ CIterYield b CIterStop-    stepOuter _ (CIterYield a next) = return $ Yield a next-    stepOuter _ CIterStop = return Stop---- "n" elements at the end are dropped by the fold.-{-# INLINE sliceBy #-}-sliceBy :: Monad m => Fold m a Int -> Int -> Refold m (Int, Int) a (Int, Int)-sliceBy (Fold step1 initial1 extract1) n = Refold step inject extract--    where--    inject (i, len) = do-        r <- initial1-        return $ case r of-            Partial s -> Partial $ Tuple' (i + len + n) s-            Done l -> Done (i, l)--    step (Tuple' i s) x = do-        r <- step1 s x-        return $ case r of-            Partial s1 -> Partial $ Tuple' i s1-            Done len -> Done (i, len)--    extract (Tuple' i s) = (i,) <$> extract1 s--{-# INLINE sliceOnSuffix #-}-sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)-sliceOnSuffix predicate =-    -- Scan the stream with the given refold-    refoldIterateM-        (sliceBy (FL.takeEndBy_ predicate FL.length) 1)-        (return (-1, 0))----------------------------------------------------------------------------------- Parsing---------------------------------------------------------------------------------{-# ANN type ParseChunksState Fuse #-}-data ParseChunksState x inpBuf st pst =-      ParseChunksInit inpBuf st-    | ParseChunksInitBuf inpBuf-    | ParseChunksInitLeftOver inpBuf-    | ParseChunksStream st inpBuf !pst-    | ParseChunksStop inpBuf !pst-    | ParseChunksBuf inpBuf st inpBuf !pst-    | ParseChunksExtract inpBuf inpBuf !pst-    | ParseChunksYield x (ParseChunksState x inpBuf st pst)---- XXX return the remaining stream as part of the error.--- XXX This is in fact parseMany1 (a la foldMany1). Do we need a parseMany as--- well?-{-# INLINE_NORMAL parseManyD #-}-parseManyD-    :: Monad m-    => PRD.Parser a m b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseManyD (PRD.Parser pstep initial extract) (Stream step state) =-    Stream stepOuter (ParseChunksInit [] state)--    where--    {-# INLINE_LATE stepOuter #-}-    -- Buffer is empty, get the first element from the stream, initialize the-    -- fold and then go to stream processing loop.-    stepOuter gst (ParseChunksInit [] st) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                res <- initial-                case res of-                    PRD.IPartial ps ->-                        return $ Skip $ ParseChunksBuf [x] s [] ps-                    PRD.IDone pb ->-                        let next = ParseChunksInit [x] s-                         in return $ Skip $ ParseChunksYield (Right pb) next-                    PRD.IError err ->-                        return-                            $ Skip-                            $ ParseChunksYield-                                (Left (ParseError err))-                                (ParseChunksInitLeftOver [])-            Skip s -> return $ Skip $ ParseChunksInit [] s-            Stop   -> return Stop--    -- Buffer is not empty, go to buffered processing loop-    stepOuter _ (ParseChunksInit src st) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ Skip $ ParseChunksBuf src st [] ps-            PRD.IDone pb ->-                let next = ParseChunksInit src st-                 in return $ Skip $ ParseChunksYield (Right pb) next-            PRD.IError err ->-                return-                    $ Skip-                    $ ParseChunksYield-                        (Left (ParseError err))-                        (ParseChunksInitLeftOver [])--    -- This is simplified ParseChunksInit-    stepOuter _ (ParseChunksInitBuf src) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ Skip $ ParseChunksExtract src [] ps-            PRD.IDone pb ->-                let next = ParseChunksInitBuf src-                 in return $ Skip $ ParseChunksYield (Right pb) next-            PRD.IError err ->-                return-                    $ Skip-                    $ ParseChunksYield-                        (Left (ParseError err))-                        (ParseChunksInitLeftOver [])--    -- XXX we just discard any leftover input at the end-    stepOuter _ (ParseChunksInitLeftOver _) = return Stop--    -- Buffer is empty, process elements from the stream-    stepOuter gst (ParseChunksStream st buf pst) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                pRes <- pstep pst x-                case pRes of-                    PR.Partial 0 pst1 ->-                        return $ Skip $ ParseChunksStream s [] pst1-                    PR.Partial n pst1 -> do-                        assert (n <= length (x:buf)) (return ())-                        let src0 = Prelude.take n (x:buf)-                            src  = Prelude.reverse src0-                        return $ Skip $ ParseChunksBuf src s [] pst1-                    PR.Continue 0 pst1 ->-                        return $ Skip $ ParseChunksStream s (x:buf) pst1-                    PR.Continue n pst1 -> do-                        assert (n <= length (x:buf)) (return ())-                        let (src0, buf1) = splitAt n (x:buf)-                            src  = Prelude.reverse src0-                        return $ Skip $ ParseChunksBuf src s buf1 pst1-                    PR.Done 0 b -> do-                        return $ Skip $-                            ParseChunksYield (Right b) (ParseChunksInit [] s)-                    PR.Done n b -> do-                        assert (n <= length (x:buf)) (return ())-                        let src = Prelude.reverse (Prelude.take n (x:buf))-                        return $ Skip $-                            ParseChunksYield (Right b) (ParseChunksInit src s)-                    PR.Error err ->-                        return-                            $ Skip-                            $ ParseChunksYield-                                (Left (ParseError err))-                                (ParseChunksInitLeftOver [])-            Skip s -> return $ Skip $ ParseChunksStream s buf pst-            Stop -> return $ Skip $ ParseChunksStop buf pst--    -- go back to stream processing mode-    stepOuter _ (ParseChunksBuf [] s buf pst) =-        return $ Skip $ ParseChunksStream s buf pst--    -- buffered processing loop-    stepOuter _ (ParseChunksBuf (x:xs) s buf pst) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ Skip $ ParseChunksBuf xs s [] pst1-            PR.Partial n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ParseChunksBuf src s [] pst1-            PR.Continue 0 pst1 ->-                return $ Skip $ ParseChunksBuf xs s (x:buf) pst1-            PR.Continue n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let (src0, buf1) = splitAt n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ParseChunksBuf src s buf1 pst1-            PR.Done 0 b ->-                return-                    $ Skip-                    $ ParseChunksYield (Right b) (ParseChunksInit xs s)-            PR.Done n b -> do-                assert (n <= length (x:buf)) (return ())-                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs-                return $ Skip-                    $ ParseChunksYield (Right b) (ParseChunksInit src s)-            PR.Error err ->-                return-                    $ Skip-                    $ ParseChunksYield-                        (Left (ParseError err))-                        (ParseChunksInitLeftOver [])--    -- This is simplified ParseChunksBuf-    stepOuter _ (ParseChunksExtract [] buf pst) =-        return $ Skip $ ParseChunksStop buf pst--    stepOuter _ (ParseChunksExtract (x:xs) buf pst) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ Skip $ ParseChunksExtract xs [] pst1-            PR.Partial n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ParseChunksExtract src [] pst1-            PR.Continue 0 pst1 ->-                return $ Skip $ ParseChunksExtract xs (x:buf) pst1-            PR.Continue n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let (src0, buf1) = splitAt n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ParseChunksExtract src buf1 pst1-            PR.Done 0 b ->-                return-                    $ Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitBuf xs)-            PR.Done n b -> do-                assert (n <= length (x:buf)) (return ())-                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs-                return-                    $ Skip-                    $ ParseChunksYield (Right b) (ParseChunksInitBuf src)-            PR.Error err ->-                return-                    $ Skip-                    $ ParseChunksYield-                        (Left (ParseError err))-                        (ParseChunksInitLeftOver [])--    -- This is simplified ParseChunksExtract-    stepOuter _ (ParseChunksStop buf pst) = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: parseMany: Partial in extract"-            PR.Continue 0 pst1 ->-                return $ Skip $ ParseChunksStop buf pst1-            PR.Continue n pst1 -> do-                assert (n <= length buf) (return ())-                let (src0, buf1) = splitAt n buf-                    src  = Prelude.reverse src0-                return $ Skip $ ParseChunksExtract src buf1 pst1-            PR.Done 0 b -> do-                return $ Skip $-                    ParseChunksYield (Right b) (ParseChunksInitLeftOver [])-            PR.Done n b -> do-                assert (n <= length buf) (return ())-                let src = Prelude.reverse (Prelude.take n buf)-                return $ Skip $-                    ParseChunksYield (Right b) (ParseChunksInitBuf src)-            PR.Error err ->-                return-                    $ Skip-                    $ ParseChunksYield-                        (Left (ParseError err))-                        (ParseChunksInitLeftOver [])--    stepOuter _ (ParseChunksYield a next) = return $ Yield a next---- | Apply a 'Parser' repeatedly on a stream and emit the parsed values in the--- output stream.------ Example:------ >>> s = Stream.fromList [1..10]--- >>> parser = Parser.takeBetween 0 2 Fold.sum--- >>> Stream.fold Fold.toList $ Stream.parseMany parser s--- [Right 3,Right 7,Right 11,Right 15,Right 19]------ This is the streaming equivalent of the 'Streamly.Data.Parser.many' parse--- combinator.------ Known Issues: When the parser fails there is no way to get the remaining--- stream.----{-# INLINE parseMany #-}-parseMany-    :: Monad m-    => PR.Parser a m b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseMany = parseManyD---- | Apply a stream of parsers to an input stream and emit the results in the--- output stream.------ /Unimplemented/----{-# INLINE parseSequence #-}-parseSequence-       :: -- Monad m =>-       Stream m (PR.Parser a m b)-    -> Stream m a-    -> Stream m b-parseSequence _f _m = undefined---- XXX Change the parser arguments' order---- | @parseManyTill collect test stream@ tries the parser @test@ on the input,--- if @test@ fails it backtracks and tries @collect@, after @collect@ succeeds--- @test@ is tried again and so on. The parser stops when @test@ succeeds.  The--- output of @test@ is discarded and the output of @collect@ is emitted in the--- output stream. The parser fails if @collect@ fails.------ /Unimplemented/----{-# INLINE parseManyTill #-}-parseManyTill ::-    -- MonadThrow m =>-       PR.Parser a m b-    -> PR.Parser a m x-    -> Stream m a-    -> Stream m b-parseManyTill = undefined--{-# ANN type ConcatParseState Fuse #-}-data ConcatParseState c b inpBuf st p m a =-      ConcatParseInit inpBuf st p-    | ConcatParseInitBuf inpBuf p-    | ConcatParseInitLeftOver inpBuf-    | forall s. ConcatParseStop-        inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))-    | forall s. ConcatParseStream-        st inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))-    | forall s. ConcatParseBuf-        inpBuf st inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))-    | forall s. ConcatParseExtract-        inpBuf inpBuf (s -> a -> m (PRD.Step s b)) s (s -> m (PRD.Step s b))-    | ConcatParseYield c (ConcatParseState c b inpBuf st p m a)---- XXX Review the changes-{-# INLINE_NORMAL parseIterateD #-}-parseIterateD-    :: Monad m-    => (b -> PRD.Parser a m b)-    -> b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseIterateD func seed (Stream step state) =-    Stream stepOuter (ConcatParseInit [] state (func seed))--    where--    {-# INLINE_LATE stepOuter #-}-    -- Buffer is empty, go to stream processing loop-    stepOuter _ (ConcatParseInit [] st (PRD.Parser pstep initial extract)) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ Skip $ ConcatParseStream st [] pstep ps extract-            PRD.IDone pb ->-                let next = ConcatParseInit [] st (func pb)-                 in return $ Skip $ ConcatParseYield (Right pb) next-            PRD.IError err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    -- Buffer is not empty, go to buffered processing loop-    stepOuter _ (ConcatParseInit src st-                    (PRD.Parser pstep initial extract)) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ Skip $ ConcatParseBuf src st [] pstep ps extract-            PRD.IDone pb ->-                let next = ConcatParseInit src st (func pb)-                 in return $ Skip $ ConcatParseYield (Right pb) next-            PRD.IError err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    -- This is simplified ConcatParseInit-    stepOuter _ (ConcatParseInitBuf src-                    (PRD.Parser pstep initial extract)) = do-        res <- initial-        case res of-            PRD.IPartial ps ->-                return $ Skip $ ConcatParseExtract src [] pstep ps extract-            PRD.IDone pb ->-                let next = ConcatParseInitBuf src (func pb)-                 in return $ Skip $ ConcatParseYield (Right pb) next-            PRD.IError err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    -- XXX we just discard any leftover input at the end-    stepOuter _ (ConcatParseInitLeftOver _) = return Stop--    -- Buffer is empty process elements from the stream-    stepOuter gst (ConcatParseStream st buf pstep pst extract) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                pRes <- pstep pst x-                case pRes of-                    PR.Partial 0 pst1 ->-                        return $ Skip $ ConcatParseStream s [] pstep pst1 extract-                    PR.Partial n pst1 -> do-                        assert (n <= length (x:buf)) (return ())-                        let src0 = Prelude.take n (x:buf)-                            src  = Prelude.reverse src0-                        return $ Skip $ ConcatParseBuf src s [] pstep pst1 extract-                    -- PR.Continue 0 pst1 ->-                    --     return $ Skip $ ConcatParseStream s (x:buf) pst1-                    PR.Continue n pst1 -> do-                        assert (n <= length (x:buf)) (return ())-                        let (src0, buf1) = splitAt n (x:buf)-                            src  = Prelude.reverse src0-                        return $ Skip $ ConcatParseBuf src s buf1 pstep pst1 extract-                    -- XXX Specialize for Stop 0 common case?-                    PR.Done n b -> do-                        assert (n <= length (x:buf)) (return ())-                        let src = Prelude.reverse (Prelude.take n (x:buf))-                        return $ Skip $-                            ConcatParseYield (Right b) (ConcatParseInit src s (func b))-                    PR.Error err ->-                        return-                            $ Skip-                            $ ConcatParseYield-                                (Left (ParseError err))-                                (ConcatParseInitLeftOver [])-            Skip s -> return $ Skip $ ConcatParseStream s buf pstep pst extract-            Stop -> return $ Skip $ ConcatParseStop buf pstep pst extract--    -- go back to stream processing mode-    stepOuter _ (ConcatParseBuf [] s buf pstep ps extract) =-        return $ Skip $ ConcatParseStream s buf pstep ps extract--    -- buffered processing loop-    stepOuter _ (ConcatParseBuf (x:xs) s buf pstep pst extract) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ Skip $ ConcatParseBuf xs s [] pstep pst1 extract-            PR.Partial n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ConcatParseBuf src s [] pstep pst1 extract-         -- PR.Continue 0 pst1 -> return $ Skip $ ConcatParseBuf xs s (x:buf) pst1-            PR.Continue n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let (src0, buf1) = splitAt n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ConcatParseBuf src s buf1 pstep pst1 extract-            -- XXX Specialize for Stop 0 common case?-            PR.Done n b -> do-                assert (n <= length (x:buf)) (return ())-                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs-                return $ Skip $ ConcatParseYield (Right b)-                                    (ConcatParseInit src s (func b))-            PR.Error err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    -- This is simplified ConcatParseBuf-    stepOuter _ (ConcatParseExtract [] buf pstep pst extract) =-        return $ Skip $ ConcatParseStop buf pstep pst extract--    stepOuter _ (ConcatParseExtract (x:xs) buf pstep pst extract) = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 pst1 ->-                return $ Skip $ ConcatParseExtract xs [] pstep pst1 extract-            PR.Partial n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ConcatParseExtract src [] pstep pst1 extract-            PR.Continue 0 pst1 ->-                return $ Skip $ ConcatParseExtract xs (x:buf) pstep pst1 extract-            PR.Continue n pst1 -> do-                assert (n <= length (x:buf)) (return ())-                let (src0, buf1) = splitAt n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                return $ Skip $ ConcatParseExtract src buf1 pstep pst1 extract-            PR.Done 0 b ->-                 return $ Skip $ ConcatParseYield (Right b) (ConcatParseInitBuf xs (func b))-            PR.Done n b -> do-                assert (n <= length (x:buf)) (return ())-                let src = Prelude.reverse (Prelude.take n (x:buf)) ++ xs-                return $ Skip $ ConcatParseYield (Right b) (ConcatParseInitBuf src (func b))-            PR.Error err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    -- This is simplified ConcatParseExtract-    stepOuter _ (ConcatParseStop buf pstep pst extract) = do-        pRes <- extract pst-        case pRes of-            PR.Partial _ _ -> error "Bug: parseIterate: Partial in extract"-            PR.Continue 0 pst1 ->-                return $ Skip $ ConcatParseStop buf pstep pst1 extract-            PR.Continue n pst1 -> do-                assert (n <= length buf) (return ())-                let (src0, buf1) = splitAt n buf-                    src  = Prelude.reverse src0-                return $ Skip $ ConcatParseExtract src buf1 pstep pst1 extract-            PR.Done 0 b -> do-                return $ Skip $-                    ConcatParseYield (Right b) (ConcatParseInitLeftOver [])-            PR.Done n b -> do-                assert (n <= length buf) (return ())-                let src = Prelude.reverse (Prelude.take n buf)-                return $ Skip $-                    ConcatParseYield (Right b) (ConcatParseInitBuf src (func b))-            PR.Error err ->-                return-                    $ Skip-                    $ ConcatParseYield-                        (Left (ParseError err))-                        (ConcatParseInitLeftOver [])--    stepOuter _ (ConcatParseYield a next) = return $ Yield a next---- | Iterate a parser generating function on a stream. The initial value @b@ is--- used to generate the first parser, the parser is applied on the stream and--- the result is used to generate the next parser and so on.------ >>> import Data.Monoid (Sum(..))--- >>> s = Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ fmap getSum $ Stream.catRights $ Stream.parseIterate (\b -> Parser.takeBetween 0 2 (Fold.sconcat b)) (Sum 0) $ fmap Sum s--- [3,10,21,36,55,55]------ This is the streaming equivalent of monad like sequenced application of--- parsers where next parser is dependent on the previous parser.------ /Pre-release/----{-# INLINE parseIterate #-}-parseIterate-    :: Monad m-    => (b -> PR.Parser a m b)-    -> b-    -> Stream m a-    -> Stream m (Either ParseError b)-parseIterate = parseIterateD----------------------------------------------------------------------------------- Grouping---------------------------------------------------------------------------------data GroupByState st fs a b-    = GroupingInit st-    | GroupingDo st !fs-    | GroupingInitWith st !a-    | GroupingDoWith st !fs !a-    | GroupingYield !b (GroupByState st fs a b)-    | GroupingDone--{-# INLINE_NORMAL groupsBy #-}-groupsBy :: Monad m-    => (a -> a -> Bool)-    -> Fold m a b-    -> Stream m a-    -> Stream m b-{--groupsBy eq fld = parseMany (PRD.groupBy eq fld)--}-groupsBy cmp (Fold fstep initial done) (Stream step state) =-    Stream stepOuter (GroupingInit state)--    where--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (GroupingInit st) = do-        -- XXX Note that if the stream stops without yielding a single element-        -- in the group we discard the "initial" effect.-        res <- initial-        return-            $ case res of-                  FL.Partial s -> Skip $ GroupingDo st s-                  FL.Done b -> Yield b $ GroupingInit st-    stepOuter gst (GroupingDo st fs) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                r <- fstep fs x-                case r of-                    FL.Partial fs1 -> go SPEC x s fs1-                    FL.Done b -> return $ Yield b (GroupingInit s)-            Skip s -> return $ Skip $ GroupingDo s fs-            Stop -> return Stop--        where--        go !_ prev stt !acc = do-            res <- step (adaptState gst) stt-            case res of-                Yield x s -> do-                    if cmp x prev-                    then do-                        r <- fstep acc x-                        case r of-                            FL.Partial fs1 -> go SPEC prev s fs1-                            FL.Done b -> return $ Yield b (GroupingInit s)-                    else do-                        r <- done acc-                        return $ Yield r (GroupingInitWith s x)-                Skip s -> go SPEC prev s acc-                Stop -> done acc >>= \r -> return $ Yield r GroupingDone-    stepOuter _ (GroupingInitWith st x) = do-        res <- initial-        return-            $ case res of-                  FL.Partial s -> Skip $ GroupingDoWith st s x-                  FL.Done b -> Yield b $ GroupingInitWith st x-    stepOuter gst (GroupingDoWith st fs prev) = do-        res <- fstep fs prev-        case res of-            FL.Partial fs1 -> go SPEC st fs1-            FL.Done b -> return $ Yield b (GroupingInit st)--        where--        -- XXX code duplicated from the previous equation-        go !_ stt !acc = do-            res <- step (adaptState gst) stt-            case res of-                Yield x s -> do-                    if cmp x prev-                    then do-                        r <- fstep acc x-                        case r of-                            FL.Partial fs1 -> go SPEC s fs1-                            FL.Done b -> return $ Yield b (GroupingInit s)-                    else do-                        r <- done acc-                        return $ Yield r (GroupingInitWith s x)-                Skip s -> go SPEC s acc-                Stop -> done acc >>= \r -> return $ Yield r GroupingDone-    stepOuter _ (GroupingYield _ _) = error "groupsBy: Unreachable"-    stepOuter _ GroupingDone = return Stop--{-# INLINE_NORMAL groupsRollingBy #-}-groupsRollingBy :: Monad m-    => (a -> a -> Bool)-    -> Fold m a b-    -> Stream m a-    -> Stream m b-{--groupsRollingBy eq fld = parseMany (PRD.groupByRolling eq fld)--}-groupsRollingBy cmp (Fold fstep initial done) (Stream step state) =-    Stream stepOuter (GroupingInit state)--    where--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (GroupingInit st) = do-        -- XXX Note that if the stream stops without yielding a single element-        -- in the group we discard the "initial" effect.-        res <- initial-        return-            $ case res of-                  FL.Partial fs -> Skip $ GroupingDo st fs-                  FL.Done fb -> Yield fb $ GroupingInit st-    stepOuter gst (GroupingDo st fs) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                r <- fstep fs x-                case r of-                    FL.Partial fs1 -> go SPEC x s fs1-                    FL.Done fb -> return $ Yield fb (GroupingInit s)-            Skip s -> return $ Skip $ GroupingDo s fs-            Stop -> return Stop--        where--        go !_ prev stt !acc = do-            res <- step (adaptState gst) stt-            case res of-                Yield x s -> do-                    if cmp prev x-                    then do-                        r <- fstep acc x-                        case r of-                            FL.Partial fs1 -> go SPEC x s fs1-                            FL.Done b -> return $ Yield b (GroupingInit s)-                    else do-                        r <- done acc-                        return $ Yield r (GroupingInitWith s x)-                Skip s -> go SPEC prev s acc-                Stop -> done acc >>= \r -> return $ Yield r GroupingDone-    stepOuter _ (GroupingInitWith st x) = do-        res <- initial-        return-            $ case res of-                  FL.Partial s -> Skip $ GroupingDoWith st s x-                  FL.Done b -> Yield b $ GroupingInitWith st x-    stepOuter gst (GroupingDoWith st fs previous) = do-        res <- fstep fs previous-        case res of-            FL.Partial s -> go SPEC previous st s-            FL.Done b -> return $ Yield b (GroupingInit st)--        where--        -- XXX GHC: groupsBy has one less parameter in this go loop and it-        -- fuses. However, groupsRollingBy does not fuse, removing the prev-        -- parameter makes it fuse. Something needs to be fixed in GHC. The-        -- workaround for this is noted in the comments below.-        go !_ prev !stt !acc = do-            res <- step (adaptState gst) stt-            case res of-                Yield x s -> do-                    if cmp prev x-                    then do-                        r <- fstep acc x-                        case r of-                            FL.Partial fs1 -> go SPEC x s fs1-                            FL.Done b -> return $ Yield b (GroupingInit st)-                    else do-                        {--                        r <- done acc-                        return $ Yield r (GroupingInitWith s x)-                        -}-                        -- The code above does not let groupBy fuse. We use the-                        -- alternative code below instead.  Instead of jumping-                        -- to GroupingInitWith state, we unroll the code of-                        -- GroupingInitWith state here to help GHC with stream-                        -- fusion.-                        result <- initial-                        r <- done acc-                        return-                            $ Yield r-                            $ case result of-                                  FL.Partial fsi -> GroupingDoWith s fsi x-                                  FL.Done b -> GroupingYield b (GroupingInit s)-                Skip s -> go SPEC prev s acc-                Stop -> done acc >>= \r -> return $ Yield r GroupingDone-    stepOuter _ (GroupingYield r next) = return $ Yield r next-    stepOuter _ GroupingDone = return Stop----------------------------------------------------------------------------------- Splitting - by a predicate---------------------------------------------------------------------------------data WordsByState st fs b-    = WordsByInit st-    | WordsByDo st !fs-    | WordsByDone-    | WordsByYield !b (WordsByState st fs b)--{-# INLINE_NORMAL wordsBy #-}-wordsBy :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b-wordsBy predicate (Fold fstep initial done) (Stream step state) =-    Stream stepOuter (WordsByInit state)--    where--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ (WordsByInit st) = do-        res <- initial-        return-            $ case res of-                  FL.Partial s -> Skip $ WordsByDo st s-                  FL.Done b -> Yield b (WordsByInit st)--    stepOuter gst (WordsByDo st fs) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                if predicate x-                then do-                    resi <- initial-                    return-                        $ case resi of-                              FL.Partial fs1 -> Skip $ WordsByDo s fs1-                              FL.Done b -> Yield b (WordsByInit s)-                else do-                    r <- fstep fs x-                    case r of-                        FL.Partial fs1 -> go SPEC s fs1-                        FL.Done b -> return $ Yield b (WordsByInit s)-            Skip s    -> return $ Skip $ WordsByDo s fs-            Stop      -> return Stop--        where--        go !_ stt !acc = do-            res <- step (adaptState gst) stt-            case res of-                Yield x s -> do-                    if predicate x-                    then do-                        {--                        r <- done acc-                        return $ Yield r (WordsByInit s)-                        -}-                        -- The above code does not fuse well. Need to check why-                        -- GHC is not able to simplify it well.  Using the code-                        -- below, instead of jumping through the WordsByInit-                        -- state always, we directly go to WordsByDo state in-                        -- the common case of Partial.-                        resi <- initial-                        r <- done acc-                        return-                            $ Yield r-                            $ case resi of-                                  FL.Partial fs1 -> WordsByDo s fs1-                                  FL.Done b -> WordsByYield b (WordsByInit s)-                    else do-                        r <- fstep acc x-                        case r of-                            FL.Partial fs1 -> go SPEC s fs1-                            FL.Done b -> return $ Yield b (WordsByInit s)-                Skip s -> go SPEC s acc-                Stop -> done acc >>= \r -> return $ Yield r WordsByDone--    stepOuter _ WordsByDone = return Stop--    stepOuter _ (WordsByYield b next) = return $ Yield b next----------------------------------------------------------------------------------- Splitting on a sequence----------------------------------------------------------------------------------- String search algorithms:--- http://www-igm.univ-mlv.fr/~lecroq/string/index.html--{---- TODO can we unify the splitting operations using a splitting configuration--- like in the split package.----data SplitStyle = Infix | Suffix | Prefix deriving (Eq, Show)-data SplitOptions = SplitOptions-    { style    :: SplitStyle-    , withSep  :: Bool  -- ^ keep the separators in output-    -- , compact  :: Bool  -- ^ treat multiple consecutive separators as one-    -- , trimHead :: Bool  -- ^ drop blank at head-    -- , trimTail :: Bool  -- ^ drop blank at tail-    }--}---- XXX using "fs" as the last arg in Constructors may simplify the code a bit,--- because we can use the constructor directly without having to create "jump"--- functions.-{-# ANN type SplitOnSeqState Fuse #-}-data SplitOnSeqState rb rh ck w fs s b x =-      SplitOnSeqInit-    | SplitOnSeqYield b (SplitOnSeqState rb rh ck w fs s b x)-    | SplitOnSeqDone--    | SplitOnSeqEmpty !fs s--    | SplitOnSeqSingle !fs s x--    | SplitOnSeqWordInit !fs s-    | SplitOnSeqWordLoop !w s !fs-    | SplitOnSeqWordDone Int !fs !w--    | SplitOnSeqKRInit Int !fs s rb !rh-    | SplitOnSeqKRLoop fs s rb !rh !ck-    | SplitOnSeqKRCheck fs s rb !rh-    | SplitOnSeqKRDone Int !fs rb !rh--    | SplitOnSeqReinit (fs -> SplitOnSeqState rb rh ck w fs s b x)--{-# INLINE_NORMAL splitOnSeq #-}-splitOnSeq-    :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a)-    => Array a-    -> Fold m a b-    -> Stream m a-    -> Stream m b-splitOnSeq patArr (Fold fstep initial done) (Stream step state) =-    Stream stepOuter SplitOnSeqInit--    where--    patLen = A.length patArr-    maxIndex = patLen - 1-    elemBits = SIZE_OF(a) * 8--    -- For word pattern case-    wordMask :: Word-    wordMask = (1 `shiftL` (elemBits * patLen)) - 1--    elemMask :: Word-    elemMask = (1 `shiftL` elemBits) - 1--    wordPat :: Word-    wordPat = wordMask .&. A.foldl' addToWord 0 patArr--    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)--    -- For Rabin-Karp search-    k = 2891336453 :: Word32-    coeff = k ^ patLen--    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)--    deltaCksum cksum old new =-        addCksum cksum new - coeff * fromIntegral (fromEnum old)--    -- XXX shall we use a random starting hash or 1 instead of 0?-    patHash = A.foldl' addCksum 0 patArr--    skip = return . Skip--    nextAfterInit nextGen stepRes =-        case stepRes of-            FL.Partial s -> nextGen s-            FL.Done b -> SplitOnSeqYield b (SplitOnSeqReinit nextGen)--    {-# INLINE yieldProceed #-}-    yieldProceed nextGen fs =-        initial >>= skip . SplitOnSeqYield fs . nextAfterInit nextGen--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ SplitOnSeqInit = do-        res <- initial-        case res of-            FL.Partial acc ->-                if patLen == 0-                then return $ Skip $ SplitOnSeqEmpty acc state-                else if patLen == 1-                     then do-                         pat <- liftIO $ A.unsafeIndexIO 0 patArr-                         return $ Skip $ SplitOnSeqSingle acc state pat-                     else if SIZE_OF(a) * patLen-                               <= sizeOf (Proxy :: Proxy Word)-                          then return $ Skip $ SplitOnSeqWordInit acc state-                          else do-                              (rb, rhead) <- liftIO $ RB.new patLen-                              skip $ SplitOnSeqKRInit 0 acc state rb rhead-            FL.Done b -> skip $ SplitOnSeqYield b SplitOnSeqInit--    stepOuter _ (SplitOnSeqYield x next) = return $ Yield x next--    ----------------------------    -- Checkpoint-    -----------------------------    stepOuter _ (SplitOnSeqReinit nextGen) =-        initial >>= skip . nextAfterInit nextGen--    ----------------------------    -- Empty pattern-    -----------------------------    stepOuter gst (SplitOnSeqEmpty acc st) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                r <- fstep acc x-                b1 <--                    case r of-                        FL.Partial acc1 -> done acc1-                        FL.Done b -> return b-                let jump c = SplitOnSeqEmpty c s-                 in yieldProceed jump b1-            Skip s -> skip (SplitOnSeqEmpty acc s)-            Stop -> return Stop--    ------------------    -- Done-    -------------------    stepOuter _ SplitOnSeqDone = return Stop--    ------------------    -- Single Pattern-    -------------------    stepOuter gst (SplitOnSeqSingle fs st pat) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                let jump c = SplitOnSeqSingle c s pat-                if pat == x-                then done fs >>= yieldProceed jump-                else do-                    r <- fstep fs x-                    case r of-                        FL.Partial fs1 -> skip $ jump fs1-                        FL.Done b -> yieldProceed jump b-            Skip s -> return $ Skip $ SplitOnSeqSingle fs s pat-            Stop -> do-                r <- done fs-                return $ Skip $ SplitOnSeqYield r SplitOnSeqDone--    ----------------------------    -- Short Pattern - Shift Or-    -----------------------------    stepOuter _ (SplitOnSeqWordDone 0 fs _) = do-        r <- done fs-        skip $ SplitOnSeqYield r SplitOnSeqDone-    stepOuter _ (SplitOnSeqWordDone n fs wrd) = do-        let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))-        r <- fstep fs (toEnum $ fromIntegral old)-        case r of-            FL.Partial fs1 -> skip $ SplitOnSeqWordDone (n - 1) fs1 wrd-            FL.Done b -> do-                 let jump c = SplitOnSeqWordDone (n - 1) c wrd-                 yieldProceed jump b--    stepOuter gst (SplitOnSeqWordInit fs st0) =-        go SPEC 0 0 st0--        where--        {-# INLINE go #-}-        go !_ !idx !wrd !st = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    let wrd1 = addToWord wrd x-                    if idx == maxIndex-                    then do-                        if wrd1 .&. wordMask == wordPat-                        then do-                            let jump c = SplitOnSeqWordInit c s-                            done fs >>= yieldProceed jump-                        else skip $ SplitOnSeqWordLoop wrd1 s fs-                    else go SPEC (idx + 1) wrd1 s-                Skip s -> go SPEC idx wrd s-                Stop -> do-                    if idx /= 0-                    then skip $ SplitOnSeqWordDone idx fs wrd-                    else do-                        r <- done fs-                        skip $ SplitOnSeqYield r SplitOnSeqDone--    stepOuter gst (SplitOnSeqWordLoop wrd0 st0 fs0) =-        go SPEC wrd0 st0 fs0--        where--        {-# INLINE go #-}-        go !_ !wrd !st !fs = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    let jump c = SplitOnSeqWordInit c s-                        wrd1 = addToWord wrd x-                        old = (wordMask .&. wrd)-                                `shiftR` (elemBits * (patLen - 1))-                    r <- fstep fs (toEnum $ fromIntegral old)-                    case r of-                        FL.Partial fs1 -> do-                            if wrd1 .&. wordMask == wordPat-                            then done fs1 >>= yieldProceed jump-                            else go SPEC wrd1 s fs1-                        FL.Done b -> yieldProceed jump b-                Skip s -> go SPEC wrd s fs-                Stop -> skip $ SplitOnSeqWordDone patLen fs wrd--    --------------------------------    -- General Pattern - Karp Rabin-    ---------------------------------    stepOuter gst (SplitOnSeqKRInit idx fs st rb rh) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                rh1 <- liftIO $ RB.unsafeInsert rb rh x-                if idx == maxIndex-                then do-                    let fld = RB.unsafeFoldRing (RB.ringBound rb)-                    let !ringHash = fld addCksum 0 rb-                    if ringHash == patHash-                    then skip $ SplitOnSeqKRCheck fs s rb rh1-                    else skip $ SplitOnSeqKRLoop fs s rb rh1 ringHash-                else skip $ SplitOnSeqKRInit (idx + 1) fs s rb rh1-            Skip s -> skip $ SplitOnSeqKRInit idx fs s rb rh-            Stop -> do-                skip $ SplitOnSeqKRDone idx fs rb (RB.startOf rb)--    -- XXX The recursive "go" is more efficient than the state based recursion-    -- code commented out below. Perhaps its more efficient because of-    -- factoring out "rb" outside the loop.-    ---    stepOuter gst (SplitOnSeqKRLoop fs0 st0 rb rh0 cksum0) =-        go SPEC fs0 st0 rh0 cksum0--        where--        go !_ !fs !st !rh !cksum = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    old <- liftIO $ peek rh-                    let cksum1 = deltaCksum cksum old x-                    r <- fstep fs old-                    case r of-                        FL.Partial fs1 -> do-                            rh1 <- liftIO (RB.unsafeInsert rb rh x)-                            if cksum1 == patHash-                            then skip $ SplitOnSeqKRCheck fs1 s rb rh1-                            else go SPEC fs1 s rh1 cksum1-                        FL.Done b -> do-                            let rst = RB.startOf rb-                                jump c = SplitOnSeqKRInit 0 c s rb rst-                            yieldProceed jump b-                Skip s -> go SPEC fs s rh cksum-                Stop -> skip $ SplitOnSeqKRDone patLen fs rb rh--    -- XXX The following code is 5 times slower compared to the recursive loop-    -- based code above. Need to investigate why. One possibility is that the-    -- go loop above does not thread around the ring buffer (rb). This code may-    -- be causing the state to bloat and getting allocated on each iteration.-    -- We can check the cmm/asm code to confirm.  If so a good GHC solution to-    -- such problem is needed. One way to avoid this could be to use unboxed-    -- mutable state?-    {--    stepOuter gst (SplitOnSeqKRLoop fs st rb rh cksum) = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    old <- liftIO $ peek rh-                    let cksum1 = deltaCksum cksum old x-                    fs1 <- fstep fs old-                    if (cksum1 == patHash)-                    then do-                        r <- done fs1-                        skip $ SplitOnSeqYield r $ SplitOnSeqKRInit 0 s rb rh-                    else do-                        rh1 <- liftIO (RB.unsafeInsert rb rh x)-                        skip $ SplitOnSeqKRLoop fs1 s rb rh1 cksum1-                Skip s -> skip $ SplitOnSeqKRLoop fs s rb rh cksum-                Stop -> skip $ SplitOnSeqKRDone patLen fs rb rh-    -}--    stepOuter _ (SplitOnSeqKRCheck fs st rb rh) = do-        if RB.unsafeEqArray rb rh patArr-        then do-            r <- done fs-            let rst = RB.startOf rb-                jump c = SplitOnSeqKRInit 0 c st rb rst-            yieldProceed jump r-        else skip $ SplitOnSeqKRLoop fs st rb rh patHash--    stepOuter _ (SplitOnSeqKRDone 0 fs _ _) = do-        r <- done fs-        skip $ SplitOnSeqYield r SplitOnSeqDone-    stepOuter _ (SplitOnSeqKRDone n fs rb rh) = do-        old <- liftIO $ peek rh-        let rh1 = RB.advance rb rh-        r <- fstep fs old-        case r of-            FL.Partial fs1 -> skip $ SplitOnSeqKRDone (n - 1) fs1 rb rh1-            FL.Done b -> do-                 let jump c = SplitOnSeqKRDone (n - 1) c rb rh1-                 yieldProceed jump b--{-# ANN type SplitOnSuffixSeqState Fuse #-}-data SplitOnSuffixSeqState rb rh ck w fs s b x =-      SplitOnSuffixSeqInit-    | SplitOnSuffixSeqYield b (SplitOnSuffixSeqState rb rh ck w fs s b x)-    | SplitOnSuffixSeqDone--    | SplitOnSuffixSeqEmpty !fs s--    | SplitOnSuffixSeqSingleInit !fs s x-    | SplitOnSuffixSeqSingle !fs s x--    | SplitOnSuffixSeqWordInit !fs s-    | SplitOnSuffixSeqWordLoop !w s !fs-    | SplitOnSuffixSeqWordDone Int !fs !w--    | SplitOnSuffixSeqKRInit Int !fs s rb !rh-    | SplitOnSuffixSeqKRInit1 !fs s rb !rh-    | SplitOnSuffixSeqKRLoop fs s rb !rh !ck-    | SplitOnSuffixSeqKRCheck fs s rb !rh-    | SplitOnSuffixSeqKRDone Int !fs rb !rh--    | SplitOnSuffixSeqReinit-          (fs -> SplitOnSuffixSeqState rb rh ck w fs s b x)--{-# INLINE_NORMAL splitOnSuffixSeq #-}-splitOnSuffixSeq-    :: forall m a b. (MonadIO m, Storable a, Unbox a, Enum a, Eq a)-    => Bool-    -> Array a-    -> Fold m a b-    -> Stream m a-    -> Stream m b-splitOnSuffixSeq withSep patArr (Fold fstep initial done) (Stream step state) =-    Stream stepOuter SplitOnSuffixSeqInit--    where--    patLen = A.length patArr-    maxIndex = patLen - 1-    elemBits = SIZE_OF(a) * 8--    -- For word pattern case-    wordMask :: Word-    wordMask = (1 `shiftL` (elemBits * patLen)) - 1--    elemMask :: Word-    elemMask = (1 `shiftL` elemBits) - 1--    wordPat :: Word-    wordPat = wordMask .&. A.foldl' addToWord 0 patArr--    addToWord wd a = (wd `shiftL` elemBits) .|. fromIntegral (fromEnum a)--    nextAfterInit nextGen stepRes =-        case stepRes of-            FL.Partial s -> nextGen s-            FL.Done b ->-                SplitOnSuffixSeqYield b (SplitOnSuffixSeqReinit nextGen)--    {-# INLINE yieldProceed #-}-    yieldProceed nextGen fs =-        initial >>= skip . SplitOnSuffixSeqYield fs . nextAfterInit nextGen--    -- For single element pattern case-    {-# INLINE processYieldSingle #-}-    processYieldSingle pat x s fs = do-        let jump c = SplitOnSuffixSeqSingleInit c s pat-        if pat == x-        then do-            r <- if withSep then fstep fs x else return $ FL.Partial fs-            b1 <--                case r of-                    FL.Partial fs1 -> done fs1-                    FL.Done b -> return b-            yieldProceed jump b1-        else do-            r <- fstep fs x-            case r of-                FL.Partial fs1 -> skip $ SplitOnSuffixSeqSingle fs1 s pat-                FL.Done b -> yieldProceed jump b--    -- For Rabin-Karp search-    k = 2891336453 :: Word32-    coeff = k ^ patLen--    addCksum cksum a = cksum * k + fromIntegral (fromEnum a)--    deltaCksum cksum old new =-        addCksum cksum new - coeff * fromIntegral (fromEnum old)--    -- XXX shall we use a random starting hash or 1 instead of 0?-    patHash = A.foldl' addCksum 0 patArr--    skip = return . Skip--    {-# INLINE_LATE stepOuter #-}-    stepOuter _ SplitOnSuffixSeqInit = do-        res <- initial-        case res of-            FL.Partial fs ->-                if patLen == 0-                then skip $ SplitOnSuffixSeqEmpty fs state-                else if patLen == 1-                     then do-                         pat <- liftIO $ A.unsafeIndexIO 0 patArr-                         skip $ SplitOnSuffixSeqSingleInit fs state pat-                     else if SIZE_OF(a) * patLen-                               <= sizeOf (Proxy :: Proxy Word)-                          then skip $ SplitOnSuffixSeqWordInit fs state-                          else do-                              (rb, rhead) <- liftIO $ RB.new patLen-                              skip $ SplitOnSuffixSeqKRInit 0 fs state rb rhead-            FL.Done fb -> skip $ SplitOnSuffixSeqYield fb SplitOnSuffixSeqInit--    stepOuter _ (SplitOnSuffixSeqYield x next) = return $ Yield x next--    ----------------------------    -- Reinit-    -----------------------------    stepOuter _ (SplitOnSuffixSeqReinit nextGen) =-        initial >>= skip . nextAfterInit nextGen--    ----------------------------    -- Empty pattern-    -----------------------------    stepOuter gst (SplitOnSuffixSeqEmpty acc st) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> do-                let jump c = SplitOnSuffixSeqEmpty c s-                r <- fstep acc x-                b1 <--                    case r of-                        FL.Partial fs -> done fs-                        FL.Done b -> return b-                yieldProceed jump b1-            Skip s -> skip (SplitOnSuffixSeqEmpty acc s)-            Stop -> return Stop--    ------------------    -- Done-    -------------------    stepOuter _ SplitOnSuffixSeqDone = return Stop--    ------------------    -- Single Pattern-    -------------------    stepOuter gst (SplitOnSuffixSeqSingleInit fs st pat) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> processYieldSingle pat x s fs-            Skip s -> skip $ SplitOnSuffixSeqSingleInit fs s pat-            Stop -> return Stop--    stepOuter gst (SplitOnSuffixSeqSingle fs st pat) = do-        res <- step (adaptState gst) st-        case res of-            Yield x s -> processYieldSingle pat x s fs-            Skip s -> skip $ SplitOnSuffixSeqSingle fs s pat-            Stop -> do-                r <- done fs-                skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone--    ----------------------------    -- Short Pattern - Shift Or-    -----------------------------    stepOuter _ (SplitOnSuffixSeqWordDone 0 fs _) = do-        r <- done fs-        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone-    stepOuter _ (SplitOnSuffixSeqWordDone n fs wrd) = do-        let old = elemMask .&. (wrd `shiftR` (elemBits * (n - 1)))-        r <- fstep fs (toEnum $ fromIntegral old)-        case r of-            FL.Partial fs1 -> skip $ SplitOnSuffixSeqWordDone (n - 1) fs1 wrd-            FL.Done b -> do-                let jump c = SplitOnSuffixSeqWordDone (n - 1) c wrd-                yieldProceed jump b--    stepOuter gst (SplitOnSuffixSeqWordInit fs0 st0) = do-        res <- step (adaptState gst) st0-        case res of-            Yield x s -> do-                let wrd = addToWord 0 x-                r <- if withSep then fstep fs0 x else return $ FL.Partial fs0-                case r of-                    FL.Partial fs1 -> go SPEC 1 wrd s fs1-                    FL.Done b -> do-                        let jump c = SplitOnSuffixSeqWordInit c s-                        yieldProceed jump b-            Skip s -> skip (SplitOnSuffixSeqWordInit fs0 s)-            Stop -> return Stop--        where--        {-# INLINE go #-}-        go !_ !idx !wrd !st !fs = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    let jump c = SplitOnSuffixSeqWordInit c s-                    let wrd1 = addToWord wrd x-                    r <- if withSep then fstep fs x else return $ FL.Partial fs-                    case r of-                        FL.Partial fs1 ->-                            if idx /= maxIndex-                            then go SPEC (idx + 1) wrd1 s fs1-                            else if wrd1 .&. wordMask /= wordPat-                            then skip $ SplitOnSuffixSeqWordLoop wrd1 s fs1-                            else do done fs >>= yieldProceed jump-                        FL.Done b -> yieldProceed jump b-                Skip s -> go SPEC idx wrd s fs-                Stop -> skip $ SplitOnSuffixSeqWordDone idx fs wrd--    stepOuter gst (SplitOnSuffixSeqWordLoop wrd0 st0 fs0) =-        go SPEC wrd0 st0 fs0--        where--        {-# INLINE go #-}-        go !_ !wrd !st !fs = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    let jump c = SplitOnSuffixSeqWordInit c s-                        wrd1 = addToWord wrd x-                        old = (wordMask .&. wrd)-                                `shiftR` (elemBits * (patLen - 1))-                    r <--                        if withSep-                        then fstep fs x-                        else fstep fs (toEnum $ fromIntegral old)-                    case r of-                        FL.Partial fs1 ->-                            if wrd1 .&. wordMask == wordPat-                            then done fs1 >>= yieldProceed jump-                            else go SPEC wrd1 s fs1-                        FL.Done b -> yieldProceed jump b-                Skip s -> go SPEC wrd s fs-                Stop ->-                    if wrd .&. wordMask == wordPat-                    then return Stop-                    else if withSep-                    then do-                        r <- done fs-                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone-                    else skip $ SplitOnSuffixSeqWordDone patLen fs wrd--    --------------------------------    -- General Pattern - Karp Rabin-    ---------------------------------    stepOuter gst (SplitOnSuffixSeqKRInit idx0 fs st0 rb rh0) = do-        res <- step (adaptState gst) st0-        case res of-            Yield x s -> do-                rh1 <- liftIO $ RB.unsafeInsert rb rh0 x-                r <- if withSep then fstep fs x else return $ FL.Partial fs-                case r of-                    FL.Partial fs1 ->-                        skip $ SplitOnSuffixSeqKRInit1 fs1 s rb rh1-                    FL.Done b -> do-                        let rst = RB.startOf rb-                            jump c = SplitOnSuffixSeqKRInit 0 c s rb rst-                        yieldProceed jump b-            Skip s -> skip $ SplitOnSuffixSeqKRInit idx0 fs s rb rh0-            Stop -> return Stop--    stepOuter gst (SplitOnSuffixSeqKRInit1 fs0 st0 rb rh0) = do-        go SPEC 1 rh0 st0 fs0--        where--        go !_ !idx !rh st !fs = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    rh1 <- liftIO (RB.unsafeInsert rb rh x)-                    r <- if withSep then fstep fs x else return $ FL.Partial fs-                    case r of-                        FL.Partial fs1 ->-                            if idx /= maxIndex-                            then go SPEC (idx + 1) rh1 s fs1-                            else skip $-                                let fld = RB.unsafeFoldRing (RB.ringBound rb)-                                    !ringHash = fld addCksum 0 rb-                                 in if ringHash == patHash-                                    then SplitOnSuffixSeqKRCheck fs1 s rb rh1-                                    else SplitOnSuffixSeqKRLoop-                                            fs1 s rb rh1 ringHash-                        FL.Done b -> do-                            let rst = RB.startOf rb-                                jump c = SplitOnSuffixSeqKRInit 0 c s rb rst-                            yieldProceed jump b-                Skip s -> go SPEC idx rh s fs-                Stop -> do-                    -- do not issue a blank segment when we end at pattern-                    if (idx == maxIndex) && RB.unsafeEqArray rb rh patArr-                    then return Stop-                    else if withSep-                    then do-                        r <- done fs-                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone-                    else skip $ SplitOnSuffixSeqKRDone idx fs rb (RB.startOf rb)--    stepOuter gst (SplitOnSuffixSeqKRLoop fs0 st0 rb rh0 cksum0) =-        go SPEC fs0 st0 rh0 cksum0--        where--        go !_ !fs !st !rh !cksum = do-            res <- step (adaptState gst) st-            case res of-                Yield x s -> do-                    old <- liftIO $ peek rh-                    rh1 <- liftIO (RB.unsafeInsert rb rh x)-                    let cksum1 = deltaCksum cksum old x-                    r <- if withSep then fstep fs x else fstep fs old-                    case r of-                        FL.Partial fs1 ->-                            if cksum1 /= patHash-                            then go SPEC fs1 s rh1 cksum1-                            else skip $ SplitOnSuffixSeqKRCheck fs1 s rb rh1-                        FL.Done b -> do-                            let rst = RB.startOf rb-                                jump c = SplitOnSuffixSeqKRInit 0 c s rb rst-                            yieldProceed jump b-                Skip s -> go SPEC fs s rh cksum-                Stop ->-                    if RB.unsafeEqArray rb rh patArr-                    then return Stop-                    else if withSep-                    then do-                        r <- done fs-                        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone-                    else skip $ SplitOnSuffixSeqKRDone patLen fs rb rh--    stepOuter _ (SplitOnSuffixSeqKRCheck fs st rb rh) = do-        if RB.unsafeEqArray rb rh patArr-        then do-            r <- done fs-            let rst = RB.startOf rb-                jump c = SplitOnSuffixSeqKRInit 0 c st rb rst-            yieldProceed jump r-        else skip $ SplitOnSuffixSeqKRLoop fs st rb rh patHash--    stepOuter _ (SplitOnSuffixSeqKRDone 0 fs _ _) = do-        r <- done fs-        skip $ SplitOnSuffixSeqYield r SplitOnSuffixSeqDone-    stepOuter _ (SplitOnSuffixSeqKRDone n fs rb rh) = do-        old <- liftIO $ peek rh-        let rh1 = RB.advance rb rh-        r <- fstep fs old-        case r of-            FL.Partial fs1 -> skip $ SplitOnSuffixSeqKRDone (n - 1) fs1 rb rh1-            FL.Done b -> do-                let jump c = SplitOnSuffixSeqKRDone (n - 1) c rb rh1-                yieldProceed jump b---- Implement this as a fold or a parser instead.--- This can be implemented easily using Rabin Karp--- | Split post any one of the given patterns.------ /Unimplemented/-{-# INLINE splitOnSuffixSeqAny #-}-splitOnSuffixSeqAny :: -- (Monad m, Unboxed a, Integral a) =>-    [Array a] -> Fold m a b -> Stream m a -> Stream m b-splitOnSuffixSeqAny _subseq _f _m = undefined-    -- D.fromStreamD $ D.splitPostAny f subseq (D.toStreamD m)---- | Split on a prefixed separator element, dropping the separator.  The--- supplied 'Fold' is applied on the split segments.------ @--- > splitOnPrefix' p xs = Stream.toList $ Stream.splitOnPrefix p (Fold.toList) (Stream.fromList xs)--- > splitOnPrefix' (== '.') ".a.b"--- ["a","b"]--- @------ An empty stream results in an empty output stream:--- @--- > splitOnPrefix' (== '.') ""--- []--- @------ An empty segment consisting of only a prefix is folded to the default output--- of the fold:------ @--- > splitOnPrefix' (== '.') "."--- [""]------ > splitOnPrefix' (== '.') ".a.b."--- ["a","b",""]------ > splitOnPrefix' (== '.') ".a..b"--- ["a","","b"]------ @------ A prefix is optional at the beginning of the stream:------ @--- > splitOnPrefix' (== '.') "a"--- ["a"]------ > splitOnPrefix' (== '.') "a.b"--- ["a","b"]--- @------ 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:------ > Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id------ Assuming the input stream does not contain the separator:------ > Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.fromPure '.') Unfold.fromList === id------ /Unimplemented/-{-# INLINE splitOnPrefix #-}-splitOnPrefix :: -- (IsStream t, MonadCatch m) =>-    (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b-splitOnPrefix _predicate _f = undefined-    -- parseMany (Parser.sliceBeginBy predicate f)---- Int list examples for splitOn:------ >>> splitList [] [1,2,3,3,4]--- > [[1],[2],[3],[3],[4]]------ >>> splitList [5] [1,2,3,3,4]--- > [[1,2,3,3,4]]------ >>> splitList [1] [1,2,3,3,4]--- > [[],[2,3,3,4]]------ >>> splitList [4] [1,2,3,3,4]--- > [[1,2,3,3],[]]------ >>> splitList [2] [1,2,3,3,4]--- > [[1],[3,3,4]]------ >>> splitList [3] [1,2,3,3,4]--- > [[1,2],[],[4]]------ >>> splitList [3,3] [1,2,3,3,4]--- > [[1,2],[4]]------ >>> splitList [1,2,3,3,4] [1,2,3,3,4]--- > [[],[]]---- This can be implemented easily using Rabin Karp--- | Split on any one of the given patterns.------ /Unimplemented/----{-# INLINE splitOnAny #-}-splitOnAny :: -- (Monad m, Unboxed a, Integral a) =>-    [Array a] -> Fold m a b -> Stream m a -> Stream m b-splitOnAny _subseq _f _m =-    undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.toStreamD m)----------------------------------------------------------------------------------- Nested Container Transformation---------------------------------------------------------------------------------{-# ANN type SplitState Fuse #-}-data SplitState s arr-    = SplitInitial s-    | SplitBuffering s arr-    | SplitSplitting s arr-    | SplitYielding arr (SplitState s arr)-    | SplitFinishing---- XXX An alternative approach would be to use a partial fold (Fold m a b) to--- split using a splitBy like combinator. The Fold would consume upto the--- separator and return any leftover which can then be fed to the next fold.------ We can revisit this once we have partial folds/parsers.------ | Performs infix separator style splitting.-{-# INLINE_NORMAL splitInnerBy #-}-splitInnerBy-    :: Monad m-    => (f a -> m (f a, Maybe (f a)))  -- splitter-    -> (f a -> f a -> m (f a))        -- joiner-    -> Stream m (f a)-    -> Stream m (f a)-splitInnerBy splitter joiner (Stream step1 state1) =-    Stream step (SplitInitial state1)--    where--    {-# INLINE_LATE step #-}-    step gst (SplitInitial st) = do-        r <- step1 gst st-        case r of-            Yield x s -> do-                (x1, mx2) <- splitter x-                return $ case mx2 of-                    Nothing -> Skip (SplitBuffering s x1)-                    Just x2 -> Skip (SplitYielding x1 (SplitSplitting s x2))-            Skip s -> return $ Skip (SplitInitial s)-            Stop -> return Stop--    step gst (SplitBuffering st buf) = do-        r <- step1 gst st-        case r of-            Yield x s -> do-                (x1, mx2) <- splitter x-                buf' <- joiner buf x1-                return $ case mx2 of-                    Nothing -> Skip (SplitBuffering s buf')-                    Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))-            Skip s -> return $ Skip (SplitBuffering s buf)-            Stop -> return $ Skip (SplitYielding buf SplitFinishing)--    step _ (SplitSplitting st buf) = do-        (x1, mx2) <- splitter buf-        return $ case mx2 of-                Nothing -> Skip $ SplitBuffering st x1-                Just x2 -> Skip $ SplitYielding x1 (SplitSplitting st x2)--    step _ (SplitYielding x next) = return $ Yield x next-    step _ SplitFinishing = return Stop---- | Performs infix separator style splitting.-{-# INLINE_NORMAL splitInnerBySuffix #-}-splitInnerBySuffix-    :: (Monad m, Eq (f a), Monoid (f a))-    => (f a -> m (f a, Maybe (f a)))  -- splitter-    -> (f a -> f a -> m (f a))        -- joiner-    -> Stream m (f a)-    -> Stream m (f a)-splitInnerBySuffix splitter joiner (Stream step1 state1) =-    Stream step (SplitInitial state1)--    where--    {-# INLINE_LATE step #-}-    step gst (SplitInitial st) = do-        r <- step1 gst st-        case r of-            Yield x s -> do-                (x1, mx2) <- splitter x-                return $ case mx2 of-                    Nothing -> Skip (SplitBuffering s x1)-                    Just x2 -> Skip (SplitYielding x1 (SplitSplitting s x2))-            Skip s -> return $ Skip (SplitInitial s)-            Stop -> return Stop--    step gst (SplitBuffering st buf) = do-        r <- step1 gst st-        case r of-            Yield x s -> do-                (x1, mx2) <- splitter x-                buf' <- joiner buf x1-                return $ case mx2 of-                    Nothing -> Skip (SplitBuffering s buf')-                    Just x2 -> Skip (SplitYielding buf' (SplitSplitting s x2))-            Skip s -> return $ Skip (SplitBuffering s buf)-            Stop -> return $-                if buf == mempty-                then Stop-                else Skip (SplitYielding buf SplitFinishing)--    step _ (SplitSplitting st buf) = do-        (x1, mx2) <- splitter buf-        return $ case mx2 of-                Nothing -> Skip $ SplitBuffering st x1-                Just x2 -> Skip $ SplitYielding x1 (SplitSplitting st x2)--    step _ (SplitYielding x next) = return $ Yield x next-    step _ SplitFinishing = return Stop----------------------------------------------------------------------------------- Trimming----------------------------------------------------------------------------------- | Drop prefix from the input stream if present.------ Space: @O(1)@------ /Unimplemented/-{-# INLINE dropPrefix #-}-dropPrefix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropPrefix = error "Not implemented yet!"---- | Drop all matching infix from the input stream if present. Infix stream--- may be consumed multiple times.------ Space: @O(n)@ where n is the length of the infix.------ /Unimplemented/-{-# INLINE dropInfix #-}-dropInfix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropInfix = error "Not implemented yet!"---- | Drop suffix from the input stream if present. Suffix stream may be--- consumed multiple times.------ Space: @O(n)@ where n is the length of the suffix.------ /Unimplemented/-{-# INLINE dropSuffix #-}-dropSuffix ::-    -- (Monad m, Eq a) =>-    Stream m a -> Stream m a -> Stream m a-dropSuffix = error "Not implemented yet!"
− src/Streamly/Internal/Data/Stream/StreamD/Step.hs
@@ -1,39 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Step--- Copyright   : (c) 2018 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--module Streamly.Internal.Data.Stream.StreamD.Step-    (-    -- * The stream type-      Step (..)-    )-where--import Fusion.Plugin.Types (Fuse(..))---- | A stream is a succession of 'Step's. A 'Yield' produces a single value and--- the next state of the stream. 'Stop' indicates there are no more values in--- the stream.-{-# ANN type Step Fuse #-}-data Step s a = Yield a s | Skip s | Stop--instance Functor (Step s) where-    {-# INLINE fmap #-}-    fmap f (Yield x s) = Yield (f x) s-    fmap _ (Skip s) = Skip s-    fmap _ Stop = Stop--{--fromPure :: Monad m => a -> s -> m (Step s a)-fromPure a = return . Yield a--skip :: Monad m => s -> m (Step s a)-skip = return . Skip--stop :: Monad m => m (Step s a)-stop = return Stop--}
− src/Streamly/Internal/Data/Stream/StreamD/Top.hs
@@ -1,353 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Top--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Top level module that can depend on all other lower level Stream modules.--module Streamly.Internal.Data.Stream.StreamD.Top-    (-    -- * Transformation-    -- ** Sampling-    -- | Value agnostic filtering.-      strideFromThen--    -- * Nesting-    -- ** Set like operations-    -- | These are not exactly set operations because streams are not-    -- necessarily sets, they may have duplicated elements. These operations-    -- are generic i.e. they work on streams of unconstrained types, therefore,-    -- they have quadratic performance characterstics. For better performance-    -- using Set structures see the Streamly.Internal.Data.Stream.Container-    -- module.-    , filterInStreamGenericBy-    , deleteInStreamGenericBy-    , unionWithStreamGenericBy--    -- ** Set like operations on sorted streams-    , filterInStreamAscBy-    , deleteInStreamAscBy-    , unionWithStreamAscBy--    -- ** Join operations-    , joinInnerGeneric--    -- * Joins on sorted stream-    , joinInnerAscBy-    , joinLeftAscBy-    , joinOuterAscBy-    )-where--#include "inline.hs"--import Control.Monad.IO.Class (MonadIO(..))-import Data.IORef (newIORef, readIORef, modifyIORef')-import Streamly.Internal.Data.Fold.Type (Fold)-import Streamly.Internal.Data.Stream.Common ()-import Streamly.Internal.Data.Stream.StreamD.Type (Stream, cross)--import qualified Data.List as List-import qualified Streamly.Internal.Data.Fold as Fold-import qualified Streamly.Internal.Data.Stream.StreamD.Type as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Nesting as Stream-import qualified Streamly.Internal.Data.Stream.StreamD.Transform as Stream--import Prelude hiding (filter, zipWith, concatMap, concat)--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- Sampling----------------------------------------------------------------------------------- XXX We can implement this using addition instead of "mod" to make it more--- efficient.---- | @strideFromthen offset stride@ takes the element at @offset@ index and--- then every element at strides of @stride@.------ >>> Stream.fold Fold.toList $ Stream.strideFromThen 2 3 $ Stream.enumerateFromTo 0 10--- [2,5,8]----{-# INLINE strideFromThen #-}-strideFromThen :: Monad m => Int -> Int -> Stream m a -> Stream m a-strideFromThen offset stride =-    Stream.with Stream.indexed Stream.filter-        (\(i, _) -> i >= offset && (i - offset) `mod` stride == 0)----------------------------------------------------------------------------------- SQL Joins------------------------------------------------------------------------------------- Some references:--- * https://en.wikipedia.org/wiki/Relational_algebra--- * https://en.wikipedia.org/wiki/Join_(SQL)---- TODO: OrdSet/IntSet/hashmap based versions of these. With Eq only--- constraint, the best would be to use an Array with linear search. If the--- second stream is sorted we can also use a binary search, using Ord--- constraint or an ordering function.------ For Storables we can cache the second stream into an unboxed array for--- possibly faster access/compact representation?------ If we do not want to keep the stream in memory but always read it from the--- source (disk/network) every time we iterate through it then we can do that--- too by reading the stream every time, the stream must have immutable state--- in that case and the user is responsible for the behavior if the stream--- source changes during iterations. We can also use an Unfold instead of--- stream. We probably need a way to distinguish streams that can be read--- mutliple times without any interference (e.g. unfolding a stream using an--- immutable handle would work i.e. using pread/pwrite instead of maintaining--- an offset in the handle).---- XXX We can do this concurrently.--- XXX If the second stream is sorted and passed as an Array we could use--- binary search if we have an Ord instance or Ordering returning function. The--- time complexity would then become (m x log n).---- | Like 'cross' but emits only those tuples where @a == b@ using the--- supplied equality predicate.------ Definition:------ >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2------ You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if--- possible. @joinInnerOrd@ is an order of magnitude faster but may take more--- space for caching the second stream.------ See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused--- alternative.------ Time: O(m x n)------ /Pre-release/-{-# INLINE joinInnerGeneric #-}-joinInnerGeneric :: Monad m =>-    (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)-joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2-{--joinInnerGeneric eq s1 s2 = do-    -- ConcatMap works faster than bind-    Stream.concatMap (\a ->-        Stream.concatMap (\b ->-            if a `eq` b-            then Stream.fromPure (a, b)-            else Stream.nil-            ) s2-        ) s1--}---- | A more efficient 'joinInner' for sorted streams.------ Space: O(1)------ Time: O(m + n)------ /Unimplemented/-{-# INLINE joinInnerAscBy #-}-joinInnerAscBy ::-    (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)-joinInnerAscBy = undefined---- | A more efficient 'joinLeft' for sorted streams.------ Space: O(1)------ Time: O(m + n)------ /Unimplemented/-{-# INLINE joinLeftAscBy #-}-joinLeftAscBy :: -- Monad m =>-    (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)-joinLeftAscBy _eq _s1 _s2 = undefined---- | A more efficient 'joinOuter' for sorted streams.------ Space: O(1)------ Time: O(m + n)------ /Unimplemented/-{-# INLINE joinOuterAscBy #-}-joinOuterAscBy :: -- Monad m =>-       (a -> b -> Ordering)-    -> Stream m a-    -> Stream m b-    -> Stream m (Maybe a, Maybe b)-joinOuterAscBy _eq _s1 _s2 = undefined----------------------------------------------------------------------------------- Set operations (special joins)------------------------------------------------------------------------------------- TODO: OrdSet/IntSet/hashmap based versions of these. With Eq only constraint--- the best would be to use an Array with linear search. If the second stream--- is sorted we can also use a binary search, using Ord constraint.---- | Keep only those elements in the second stream that are present in the--- first stream too. The first stream is folded to a container using the--- supplied fold and then the elements in the container are looked up using the--- supplied lookup function.------ The first stream must be finite and must not block.-{-# INLINE filterStreamWith #-}-filterStreamWith :: Monad m =>-       Fold m a (f a)-    -> (a -> f a -> Bool)-    -> Stream m a-    -> Stream m a-    -> Stream m a-filterStreamWith fld member s1 s2 =-    Stream.concatEffect-        $ do-            xs <- Stream.fold fld s1-            return $ Stream.filter (`member` xs) s2---- | 'filterInStreamGenericBy' retains only those elements in the second stream that--- are present in the first stream.------ >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])--- [2,1,1]------ >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])--- [1,2,2]------ Similar to the list intersectBy operation but with the stream argument order--- flipped.------ The first stream must be finite and must not block. Second stream is--- processed only after the first stream is fully realized.------ Space: O(n) where @n@ is the number of elements in the second stream.------ Time: O(m x n) where @m@ is the number of elements in the first stream and--- @n@ is the number of elements in the second stream.------ /Pre-release/-{-# INLINE filterInStreamGenericBy #-}-filterInStreamGenericBy :: Monad m =>-    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a-filterInStreamGenericBy eq =-    -- XXX Use an (unboxed) array instead.-    filterStreamWith-        (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)-        (List.any . eq)---- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in--- ascending order. To use it on streams sorted in descending order pass an--- inverted comparison function returning GT for less than and LT for greater--- than.------ Space: O(1)------ Time: O(m+n)------ /Pre-release/-{-# INLINE filterInStreamAscBy #-}-filterInStreamAscBy :: Monad m =>-    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1---- | Delete all elements of the first stream from the seconds stream. If an--- element occurs multiple times in the first stream as many occurrences of it--- are deleted from the second stream.------ >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])--- [2]------ The following laws hold:------ > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2--- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2------ Same as the list 'Data.List.//' operation but with argument order flipped.------ The first stream must be finite and must not block. Second stream is--- processed only after the first stream is fully realized.------ Space: O(m) where @m@ is the number of elements in the first stream.------ Time: O(m x n) where @m@ is the number of elements in the first stream and--- @n@ is the number of elements in the second stream.------ /Pre-release/-{-# INLINE deleteInStreamGenericBy #-}-deleteInStreamGenericBy :: Monad m =>-    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a-deleteInStreamGenericBy eq s1 s2 =-    Stream.concatEffect-        $ do-            -- This may work well if s1 is small-            -- If s1 is big we can go through s1, deleting elements from s2 and-            -- not emitting an element if it was successfully deleted from s2.-            -- we will need a deleteBy that can return whether the element was-            -- deleted or not.-            xs <- Stream.fold Fold.toList s2-            let f = Fold.foldl' (flip (List.deleteBy eq)) xs-            fmap Stream.fromList $ Stream.fold f s1---- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.------ Space: O(1)------ /Unimplemented/-{-# INLINE deleteInStreamAscBy #-}-deleteInStreamAscBy :: -- (Monad m) =>-    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-deleteInStreamAscBy _eq _s1 _s2 = undefined---- XXX Remove the MonadIO constraint. We can just cache one stream and then--- implement using differenceEqBy.---- | This essentially appends to the second stream all the occurrences of--- elements in the first stream that are not already present in the second--- stream.------ Equivalent to the following except that @s2@ is evaluated only once:------ >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)------ Example:------ >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])--- [1,2,2,4,3]------ Space: O(n)------ Time: O(m x n)------ /Pre-release/-{-# INLINE unionWithStreamGenericBy #-}-unionWithStreamGenericBy :: MonadIO m =>-    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a-unionWithStreamGenericBy eq s1 s2 =-    Stream.concatEffect-        $ do-            xs <- Stream.fold Fold.toList  s1-            -- XXX we can use postscanlMAfter' instead of IORef-            ref <- liftIO $ newIORef $! List.nubBy eq xs-            let f x = do-                    liftIO $ modifyIORef' ref (List.deleteBy eq x)-                    return x-                s3 = Stream.concatEffect-                        $ do-                            xs1 <- liftIO $ readIORef ref-                            return $ Stream.fromList xs1-            return $ Stream.mapM f s2 `Stream.append` s3---- | A more efficient 'unionWithStreamGenericBy' for sorted streams.------ Space: O(1)------ /Unimplemented/-{-# INLINE unionWithStreamAscBy #-}-unionWithStreamAscBy :: -- (Monad m) =>-    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a-unionWithStreamAscBy _eq _s1 _s2 = undefined
− src/Streamly/Internal/Data/Stream/StreamD/Transform.hs
@@ -1,1945 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Transform--- Copyright   : (c) 2018 Composewell Technologies---               (c) Roman Leshchinskiy 2008-2010--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ "Streamly.Internal.Data.Pipe" might ultimately replace this module.---- A few functions in this module have been adapted from the vector package--- (c) Roman Leshchinskiy. See the notes in specific combinators.--module Streamly.Internal.Data.Stream.StreamD.Transform-    (-    -- * Piping-    -- | Pass through a 'Pipe'.-      transform--    -- * Mapping-    -- | Stateless one-to-one maps.-    , map-    , mapM-    , sequence--    -- * Mapping Effects-    , tap-    , tapOffsetEvery-    , trace-    , trace_--    -- * Folding-    , foldrS-    , foldlS--    -- * Scanning By 'Fold'-    , postscan-    , scan-    , scanMany--    -- * Splitting-    , splitOn--    -- * Scanning-    -- | Left scans. Stateful, mostly one-to-one maps.-    , scanlM'-    , scanlMAfter'-    , scanl'-    , scanlM-    , scanl-    , scanl1M'-    , scanl1'-    , scanl1M-    , scanl1--    , prescanl'-    , prescanlM'--    , postscanl-    , postscanlM-    , postscanl'-    , postscanlM'-    , postscanlMAfter'--    , postscanlx'-    , postscanlMx'-    , scanlMx'-    , scanlx'--    -- * Filtering-    -- | Produce a subset of the stream.-    , with-    , scanMaybe-    , filter-    , filterM-    , deleteBy-    , uniqBy-    , uniq-    , prune-    , repeated--    -- * Trimming-    -- | Produce a subset of the stream trimmed at ends.-    , take-    , takeWhile-    , takeWhileM-    , takeWhileLast-    , takeWhileAround-    , drop-    , dropWhile-    , dropWhileM-    , dropLast-    , dropWhileLast-    , dropWhileAround--    -- * Inserting Elements-    -- | Produce a superset of the stream.-    , insertBy-    , intersperse-    , intersperseM-    , intersperseMWith-    , intersperseMSuffix-    , intersperseMSuffixWith--    -- * Inserting Side Effects-    , intersperseM_-    , intersperseMSuffix_-    , intersperseMPrefix_--    , delay-    , delayPre-    , delayPost--    -- * Reordering-    -- | Produce strictly the same set but reordered.-    , reverse-    , reverseUnbox-    , reassembleBy--    -- * Position Indexing-    , indexed-    , indexedR--    -- * Time Indexing-    , timestampWith-    , timestamped-    , timeIndexWith-    , timeIndexed--    -- * Searching-    , findIndices-    , elemIndices-    , slicesBy--    -- * Rolling map-    -- | Map using the previous element.-    , rollingMap-    , rollingMapM-    , rollingMap2--    -- * Maybe Streams-    , mapMaybe-    , mapMaybeM-    , catMaybes--    -- * Either Streams-    , catLefts-    , catRights-    , catEithers-    )-where--#include "inline.hs"--import Control.Concurrent (threadDelay)-import Control.Monad (void)-import Control.Monad.IO.Class (MonadIO (liftIO))-import Data.Either (fromLeft, isLeft, isRight, fromRight)-import Data.Functor ((<&>))-import Data.Maybe (fromJust, isJust)-import Fusion.Plugin.Types (Fuse(..))--import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Pipe.Type (Pipe(..), PipeState(..))-import Streamly.Internal.Data.SVar.Type (adaptState)-import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64)-import Streamly.Internal.Data.Unboxed (Unbox)-import Streamly.Internal.System.IO (defaultChunkSize)---- import qualified Data.List as List-import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Fold as FL-import qualified Streamly.Internal.Data.Pipe.Type as Pipe-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K--import Prelude hiding-       ( drop, dropWhile, filter, map, mapM, reverse-       , scanl, scanl1, sequence, take, takeWhile, zipWith)--import Streamly.Internal.Data.Stream.StreamD.Generate-    (absTimesWith, relTimesWith)-import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- Piping----------------------------------------------------------------------------------- | Use a 'Pipe' to transform a stream.------ /Pre-release/----{-# INLINE_NORMAL transform #-}-transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b-transform (Pipe pstep1 pstep2 pstate) (Stream step state) =-    Stream step' (Consume pstate, state)--  where--    {-# INLINE_LATE step' #-}--    step' gst (Consume pst, st) = pst `seq` do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                res <- pstep1 pst x-                case res of-                    Pipe.Yield b pst' -> return $ Yield b (pst', s)-                    Pipe.Continue pst' -> return $ Skip (pst', s)-            Skip s -> return $ Skip (Consume pst, s)-            Stop   -> return Stop--    step' _ (Produce pst, st) = pst `seq` do-        res <- pstep2 pst-        case res of-            Pipe.Yield b pst' -> return $ Yield b (pst', st)-            Pipe.Continue pst' -> return $ Skip (pst', st)----------------------------------------------------------------------------------- Transformation Folds----------------------------------------------------------------------------------- Note, this is going to have horrible performance, because of the nature of--- the stream type (i.e. direct stream vs CPS). Its only for reference, it is--- likely be practically unusable.-{-# INLINE_NORMAL foldlS #-}-foldlS :: Monad m-    => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b-foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))-  where-    step' gst (Left (st, acc)) = do-        r <- step (adaptState gst) st-        return $ case r of-            Yield x s -> Skip (Left (s, fstep acc x))-            Skip s -> Skip (Left (s, acc))-            Stop   -> Skip (Right acc)--    step' gst (Right (Stream stp stt)) = do-        r <- stp (adaptState gst) stt-        return $ case r of-            Yield x s -> Yield x (Right (Stream stp s))-            Skip s -> Skip (Right (Stream stp s))-            Stop   -> Stop----------------------------------------------------------------------------------- Transformation by Mapping----------------------------------------------------------------------------------- |--- >>> sequence = Stream.mapM id------ Replace the elements of a stream of monadic actions with the outputs of--- those actions.------ >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]--- >>> Stream.fold Fold.drain $ Stream.sequence s--- abc----{-# INLINE_NORMAL sequence #-}-sequence :: Monad m => Stream m (m a) -> Stream m a-sequence (Stream step state) = Stream step' state-  where-    {-# INLINE_LATE step' #-}-    step' gst st = do-         r <- step (adaptState gst) st-         case r of-             Yield x s -> x >>= \a -> return (Yield a s)-             Skip s    -> return $ Skip s-             Stop      -> return Stop----------------------------------------------------------------------------------- Mapping side effects---------------------------------------------------------------------------------data TapState fs st a-    = TapInit | Tapping !fs st | TapDone st---- XXX Multiple yield points---- | Tap the data flowing through a stream into a 'Fold'. For example, you may--- add a tap to log the contents flowing through the stream. The fold is used--- only for effects, its result is discarded.------ @---                   Fold m a b---                       |--- -----stream m a ---------------stream m a----------- @------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainMapM print) s--- 1--- 2------ Compare with 'trace'.----{-# INLINE tap #-}-tap :: Monad m => Fold m a b -> Stream m a -> Stream m a-tap (Fold fstep initial extract) (Stream step state) = Stream step' TapInit--    where--    step' _ TapInit = do-        res <- initial-        return-            $ Skip-            $ case res of-                  FL.Partial s -> Tapping s state-                  FL.Done _ -> TapDone state-    step' gst (Tapping acc st) = do-        r <- step gst st-        case r of-            Yield x s -> do-                res <- fstep acc x-                return-                    $ Yield x-                    $ case res of-                          FL.Partial fs -> Tapping fs s-                          FL.Done _ -> TapDone s-            Skip s -> return $ Skip (Tapping acc s)-            Stop -> do-                void $ extract acc-                return Stop-    step' gst (TapDone st) = do-        r <- step gst st-        return-            $ case r of-                  Yield x s -> Yield x (TapDone s)-                  Skip s -> Skip (TapDone s)-                  Stop -> Stop--data TapOffState fs s a-    = TapOffInit-    | TapOffTapping !fs s Int-    | TapOffDone s---- XXX Multiple yield points-{-# INLINE_NORMAL tapOffsetEvery #-}-tapOffsetEvery :: Monad m-    => Int -> Int -> Fold m a b -> Stream m a -> Stream m a-tapOffsetEvery offset n (Fold fstep initial extract) (Stream step state) =-    Stream step' TapOffInit--    where--    {-# INLINE_LATE step' #-}-    step' _ TapOffInit = do-        res <- initial-        return-            $ Skip-            $ case res of-                  FL.Partial s -> TapOffTapping s state (offset `mod` n)-                  FL.Done _ -> TapOffDone state-    step' gst (TapOffTapping acc st count) = do-        r <- step gst st-        case r of-            Yield x s -> do-                next <--                    if count <= 0-                    then do-                        res <- fstep acc x-                        return-                            $ case res of-                                  FL.Partial sres ->-                                    TapOffTapping sres s (n - 1)-                                  FL.Done _ -> TapOffDone s-                    else return $ TapOffTapping acc s (count - 1)-                return $ Yield x next-            Skip s -> return $ Skip (TapOffTapping acc s count)-            Stop -> do-                void $ extract acc-                return Stop-    step' gst (TapOffDone st) = do-        r <- step gst st-        return-            $ case r of-                  Yield x s -> Yield x (TapOffDone s)-                  Skip s -> Skip (TapOffDone s)-                  Stop -> Stop---- | Apply a monadic function to each element flowing through the stream and--- discard the results.------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.trace print s--- 1--- 2------ Compare with 'tap'.----{-# INLINE trace #-}-trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a-trace f = mapM (\x -> void (f x) >> return x)---- | Perform a side effect before yielding each element of the stream and--- discard the results.------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s--- "got here"--- "got here"------ Same as 'intersperseMPrefix_' but always serial.------ See also: 'trace'------ /Pre-release/-{-# INLINE trace_ #-}-trace_ :: Monad m => m b -> Stream m a -> Stream m a-trace_ eff = mapM (\x -> eff >> return x)----------------------------------------------------------------------------------- Scanning with a Fold---------------------------------------------------------------------------------data ScanState s f = ScanInit s | ScanDo s !f | ScanDone---- | Postscan a stream using the given monadic fold.------ The following example extracts the input stream up to a point where the--- running average of elements is no more than 10:------ >>> import Data.Maybe (fromJust)--- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)--- >>> s = Stream.enumerateFromTo 1.0 100.0--- >>> :{---  Stream.fold Fold.toList---   $ fmap (fromJust . fst)---   $ Stream.takeWhile (\(_,x) -> x <= 10)---   $ Stream.postscan (Fold.tee Fold.latest avg) s--- :}--- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]----{-# INLINE_NORMAL postscan #-}-postscan :: Monad m => FL.Fold m a b -> Stream m a -> Stream m b-postscan (FL.Fold fstep initial extract) (Stream sstep state) =-    Stream step (ScanInit state)--    where--    {-# INLINE_LATE step #-}-    step _ (ScanInit st) = do-        res <- initial-        return-            $ case res of-                  FL.Partial fs -> Skip $ ScanDo st fs-                  FL.Done b -> Yield b ScanDone-    step gst (ScanDo st fs) = do-        res <- sstep (adaptState gst) st-        case res of-            Yield x s -> do-                r <- fstep fs x-                case r of-                    FL.Partial fs1 -> do-                        !b <- extract fs1-                        return $ Yield b $ ScanDo s fs1-                    FL.Done b -> return $ Yield b ScanDone-            Skip s -> return $ Skip $ ScanDo s fs-            Stop -> return Stop-    step _ ScanDone = return Stop--{-# INLINE scanWith #-}-scanWith :: Monad m-    => Bool -> Fold m a b -> Stream m a -> Stream m b-scanWith restart (Fold fstep initial extract) (Stream sstep state) =-    Stream step (ScanInit state)--    where--    {-# INLINE runStep #-}-    runStep st action = do-        res <- action-        case res of-            FL.Partial fs -> do-                !b <- extract fs-                return $ Yield b $ ScanDo st fs-            FL.Done b ->-                let next = if restart then ScanInit st else ScanDone-                 in return $ Yield b next--    {-# INLINE_LATE step #-}-    step _ (ScanInit st) = runStep st initial-    step gst (ScanDo st fs) = do-        res <- sstep (adaptState gst) st-        case res of-            Yield x s -> runStep s (fstep fs x)-            Skip s -> return $ Skip $ ScanDo s fs-            Stop -> return Stop-    step _ ScanDone = return Stop---- XXX It may be useful to have a version of scan where we can keep the--- accumulator independent of the value emitted. So that we do not necessarily--- have to keep a value in the accumulator which we are not using. We can pass--- an extraction function that will take the accumulator and the current value--- of the element and emit the next value in the stream. That will also make it--- possible to modify the accumulator after using it. In fact, the step function--- can return new accumulator and the value to be emitted. The signature would--- be more like mapAccumL.---- | Strict left scan. Scan a stream using the given monadic fold.------ >>> s = Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s--- [0,1,3,6]------ See also: 'usingStateT'------- EXPLANATION:--- >>> scanl' step z = Stream.scan (Fold.foldl' step z)------ Like 'map', 'scanl'' too is a one to one transformation,--- however it adds an extra element.------ >>> s = Stream.fromList [1,2,3,4]--- >>> Stream.fold Fold.toList $ scanl' (+) 0 s--- [0,1,3,6,10]------ >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s--- [[],[1],[2,1],[3,2,1],[4,3,2,1]]------ The output of 'scanl'' is the initial value of the accumulator followed by--- all the intermediate steps and the final result of 'foldl''.------ By streaming the accumulated state after each fold step, we can share the--- state across multiple stages of stream composition. Each stage can modify or--- extend the state, do some processing with it and emit it for the next stage,--- thus modularizing the stream processing. This can be useful in--- stateful or event-driven programming.------ Consider the following monolithic example, computing the sum and the product--- of the elements in a stream in one go using a @foldl'@:------ >>> foldl' step z = Stream.fold (Fold.foldl' step z)--- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s--- (10,24)------ Using @scanl'@ we can make it modular by computing the sum in the first--- stage and passing it down to the next stage for computing the product:------ >>> :{---   foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)---   $ scanl' (\(s, _) x -> (s + x, x)) (0,1)---   $ Stream.fromList [1,2,3,4]--- :}--- (10,24)------ IMPORTANT: 'scanl'' evaluates the accumulator to WHNF.  To avoid building--- lazy expressions inside the accumulator, it is recommended that a strict--- data structure is used for accumulator.----{-# INLINE_NORMAL scan #-}-scan :: Monad m-    => FL.Fold m a b -> Stream m a -> Stream m b-scan = scanWith False---- | Like 'scan' but restarts scanning afresh when the scanning fold--- terminates.----{-# INLINE_NORMAL scanMany #-}-scanMany :: Monad m-    => FL.Fold m a b -> Stream m a -> Stream m b-scanMany = scanWith True----------------------------------------------------------------------------------- Scanning - Prescans----------------------------------------------------------------------------------- Adapted from the vector package.------ XXX Is a prescan useful, discarding the last step does not sound useful?  I--- am not sure about the utility of this function, so this is implemented but--- not exposed. We can expose it if someone provides good reasons why this is--- useful.------ XXX We have to execute the stream one step ahead to know that we are at the--- last step.  The vector implementation of prescan executes the last fold step--- but does not yield the result. This means we have executed the effect but--- discarded value. This does not sound right. In this implementation we are--- not executing the last fold step.-{-# INLINE_NORMAL prescanlM' #-}-prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b-prescanlM' f mz (Stream step state) = Stream step' (state, mz)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, prev) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                acc <- prev-                return $ Yield acc (s, f acc x)-            Skip s -> return $ Skip (s, prev)-            Stop   -> return Stop--{-# INLINE prescanl' #-}-prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b-prescanl' f z = prescanlM' (\a b -> return (f a b)) (return z)----------------------------------------------------------------------------------- Monolithic postscans (postscan followed by a map)----------------------------------------------------------------------------------- The performance of a modular postscan followed by a map seems to be--- equivalent to this monolithic scan followed by map therefore we may not need--- this implementation. We just have it for performance comparison and in case--- modular version does not perform well in some situation.----{-# INLINE_NORMAL postscanlMx' #-}-postscanlMx' :: Monad m-    => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b-postscanlMx' fstep begin done (Stream step state) = do-    Stream step' (state, begin)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, acc) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                old <- acc-                y <- fstep old x-                v <- done y-                v `seq` y `seq` return (Yield v (s, return y))-            Skip s -> return $ Skip (s, acc)-            Stop   -> return Stop--{-# INLINE_NORMAL postscanlx' #-}-postscanlx' :: Monad m-    => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b-postscanlx' fstep begin done =-    postscanlMx' (\b a -> return (fstep b a)) (return begin) (return . done)---- XXX do we need consM strict to evaluate the begin value?-{-# INLINE scanlMx' #-}-scanlMx' :: Monad m-    => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b-scanlMx' fstep begin done s =-    (begin >>= \x -> x `seq` done x) `consM` postscanlMx' fstep begin done s--{-# INLINE scanlx' #-}-scanlx' :: Monad m-    => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b-scanlx' fstep begin done =-    scanlMx' (\b a -> return (fstep b a)) (return begin) (return . done)----------------------------------------------------------------------------------- postscans----------------------------------------------------------------------------------- Adapted from the vector package.-{-# INLINE_NORMAL postscanlM' #-}-postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b-postscanlM' fstep begin (Stream step state) =-    Stream step' Nothing-  where-    {-# INLINE_LATE step' #-}-    step' _ Nothing = do-        !x <- begin-        return $ Skip (Just (state, x))--    step' gst (Just (st, acc)) =  do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                !y <- fstep acc x-                return $ Yield y (Just (s, y))-            Skip s -> return $ Skip (Just (s, acc))-            Stop   -> return Stop--{-# INLINE_NORMAL postscanl' #-}-postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a-postscanl' f seed = postscanlM' (\a b -> return (f a b)) (return seed)--{-# ANN type PScanAfterState Fuse #-}-data PScanAfterState m st acc =-      PScanAfterStep st (m acc)-    | PScanAfterYield acc (PScanAfterState m st acc)-    | PScanAfterStop---- We can possibly have the "done" function as a Maybe to provide an option to--- emit or not emit the accumulator when the stream stops.------ TBD: use a single Yield point----{-# INLINE_NORMAL postscanlMAfter' #-}-postscanlMAfter' :: Monad m-    => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b-postscanlMAfter' fstep initial done (Stream step1 state1) = do-    Stream step (PScanAfterStep state1 initial)--    where--    {-# INLINE_LATE step #-}-    step gst (PScanAfterStep st acc) = do-        r <- step1 (adaptState gst) st-        case r of-            Yield x s -> do-                !old <- acc-                !y <- fstep old x-                return (Skip $ PScanAfterYield y (PScanAfterStep s (return y)))-            Skip s -> return $ Skip $ PScanAfterStep s acc-            -- Strictness is important for fusion-            Stop -> do-                !v <- acc-                !res <- done v-                return (Skip $ PScanAfterYield res PScanAfterStop)-    step _ (PScanAfterYield acc next) = return $ Yield acc next-    step _ PScanAfterStop = return Stop--{-# INLINE_NORMAL postscanlM #-}-postscanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b-postscanlM fstep begin (Stream step state) = Stream step' Nothing-  where-    {-# INLINE_LATE step' #-}-    step' _ Nothing = do-        r <- begin-        return $ Skip (Just (state, r))--    step' gst (Just (st, acc)) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                y <- fstep acc x-                return (Yield y (Just (s, y)))-            Skip s -> return $ Skip (Just (s, acc))-            Stop   -> return Stop--{-# INLINE_NORMAL postscanl #-}-postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a-postscanl f seed = postscanlM (\a b -> return (f a b)) (return seed)---- | Like 'scanl'' but with a monadic step function and a monadic seed.----{-# INLINE_NORMAL scanlM' #-}-scanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b-scanlM' fstep begin (Stream step state) = Stream step' Nothing-  where-    {-# INLINE_LATE step' #-}-    step' _ Nothing = do-        !x <- begin-        return $ Yield x (Just (state, x))-    step' gst (Just (st, acc)) =  do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                !y <- fstep acc x-                return $ Yield y (Just (s, y))-            Skip s -> return $ Skip (Just (s, acc))-            Stop   -> return Stop---- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except--- that it provides an additional @done@ function to be applied on the--- accumulator when the stream stops. The result of @done@ is also emitted in--- the stream.------ This function can be used to allocate a resource in the beginning of the--- scan and release it when the stream ends or to flush the internal state of--- the scan at the end.------ /Pre-release/----{-# INLINE scanlMAfter' #-}-scanlMAfter' :: Monad m-    => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b-scanlMAfter' fstep initial done s =-    initial `consM` postscanlMAfter' fstep initial done s---- >>> scanl' f z xs = z `Stream.cons` postscanl' f z xs---- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,--- however it adds an extra element.------ >>> Stream.toList $ Stream.scanl' (+) 0 $ Stream.fromList [1,2,3,4]--- [0,1,3,6,10]------ >>> Stream.toList $ Stream.scanl' (flip (:)) [] $ Stream.fromList [1,2,3,4]--- [[],[1],[2,1],[3,2,1],[4,3,2,1]]------ The output of 'scanl'' is the initial value of the accumulator followed by--- all the intermediate steps and the final result of 'foldl''.------ By streaming the accumulated state after each fold step, we can share the--- state across multiple stages of stream composition. Each stage can modify or--- extend the state, do some processing with it and emit it for the next stage,--- thus modularizing the stream processing. This can be useful in--- stateful or event-driven programming.------ Consider the following monolithic example, computing the sum and the product--- of the elements in a stream in one go using a @foldl'@:------ >>> Stream.fold (Fold.foldl' (\(s, p) x -> (s + x, p * x)) (0,1)) $ Stream.fromList [1,2,3,4]--- (10,24)------ Using @scanl'@ we can make it modular by computing the sum in the first--- stage and passing it down to the next stage for computing the product:------ >>> :{---   Stream.fold (Fold.foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1))---   $ Stream.scanl' (\(s, _) x -> (s + x, x)) (0,1)---   $ Stream.fromList [1,2,3,4]--- :}--- (10,24)------ IMPORTANT: 'scanl'' evaluates the accumulator to WHNF.  To avoid building--- lazy expressions inside the accumulator, it is recommended that a strict--- data structure is used for accumulator.------ >>> scanl' step z = Stream.scan (Fold.foldl' step z)--- >>> scanl' f z xs = Stream.scanlM' (\a b -> return (f a b)) (return z) xs------ See also: 'usingStateT'----{-# INLINE scanl' #-}-scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b-scanl' f seed = scanlM' (\a b -> return (f a b)) (return seed)--{-# INLINE_NORMAL scanlM #-}-scanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b-scanlM fstep begin (Stream step state) = Stream step' Nothing-  where-    {-# INLINE_LATE step' #-}-    step' _ Nothing = do-        x <- begin-        return $ Yield x (Just (state, x))-    step' gst (Just (st, acc)) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                y <- fstep acc x-                return $ Yield y (Just (s, y))-            Skip s -> return $ Skip (Just (s, acc))-            Stop   -> return Stop--{-# INLINE scanl #-}-scanl :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b-scanl f seed = scanlM (\a b -> return (f a b)) (return seed)---- Adapted from the vector package-{-# INLINE_NORMAL scanl1M #-}-scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a-scanl1M fstep (Stream step state) = Stream step' (state, Nothing)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, Nothing) = do-        r <- step gst st-        case r of-            Yield x s -> return $ Yield x (s, Just x)-            Skip s -> return $ Skip (s, Nothing)-            Stop   -> return Stop--    step' gst (st, Just acc) = do-        r <- step gst st-        case r of-            Yield y s -> do-                z <- fstep acc y-                return $ Yield z (s, Just z)-            Skip s -> return $ Skip (s, Just acc)-            Stop   -> return Stop--{-# INLINE scanl1 #-}-scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a-scanl1 f = scanl1M (\x y -> return (f x y))---- Adapted from the vector package---- | Like 'scanl1'' but with a monadic step function.----{-# INLINE_NORMAL scanl1M' #-}-scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a-scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, Nothing) = do-        r <- step gst st-        case r of-            Yield x s -> x `seq` return $ Yield x (s, Just x)-            Skip s -> return $ Skip (s, Nothing)-            Stop   -> return Stop--    step' gst (st, Just acc) = acc `seq` do-        r <- step gst st-        case r of-            Yield y s -> do-                z <- fstep acc y-                z `seq` return $ Yield z (s, Just z)-            Skip s -> return $ Skip (s, Just acc)-            Stop   -> return Stop---- | Like 'scanl'' but for a non-empty stream. The first element of the stream--- is used as the initial value of the accumulator. Does nothing if the stream--- is empty.------ >>> Stream.toList $ Stream.scanl1' (+) $ Stream.fromList [1,2,3,4]--- [1,3,6,10]----{-# INLINE scanl1' #-}-scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a-scanl1' f = scanl1M' (\x y -> return (f x y))------------------------------------------------------------------------------------ Filtering------------------------------------------------------------------------------------ | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a--- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a--- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with--- index or time.------ >>> filterWithIndex = Stream.with Stream.indexed Stream.filter------ /Pre-release/-{-# INLINE with #-}-with :: Monad m =>-       (Stream m a -> Stream m (s, a))-    -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))-    -> (((s, a) -> b) -> Stream m a -> Stream m a)-with f comb g = fmap snd . comb g . f---- Adapted from the vector package---- | Same as 'filter' but with a monadic predicate.------ >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing--- >>> filterM p = Stream.mapMaybeM (f p)----{-# INLINE_NORMAL filterM #-}-filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a-filterM f (Stream step state) = Stream step' state-  where-    {-# INLINE_LATE step' #-}-    step' gst st = do-        r <- step gst st-        case r of-            Yield x s -> do-                b <- f x-                return $ if b-                         then Yield x s-                         else Skip s-            Skip s -> return $ Skip s-            Stop   -> return Stop---- | Include only those elements that pass a predicate.------ >>> filter p = Stream.filterM (return . p)--- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)--- >>> filter p = Stream.scanMaybe (Fold.filtering p)----{-# INLINE filter #-}-filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a-filter f = filterM (return . f)--- filter p = scanMaybe (FL.filtering p)---- | Drop repeated elements that are adjacent to each other using the supplied--- comparison function.------ >>> uniq = Stream.uniqBy (==)------ To strip duplicate path separators:------ >>> input = Stream.fromList "//a//b"--- >>> f x y = x == '/' && y == '/'--- >>> Stream.fold Fold.toList $ Stream.uniqBy f input--- "/a/b"------ Space: @O(1)@------ /Pre-release/----{-# INLINE uniqBy #-}-uniqBy :: Monad m =>-    (a -> a -> Bool) -> Stream m a -> Stream m a--- uniqBy eq = scanMaybe (FL.uniqBy eq)-uniqBy eq = catMaybes . rollingMap f--    where--    f pre curr =-        case pre of-            Nothing -> Just curr-            Just x -> if x `eq` curr then Nothing else Just curr---- Adapted from the vector package---- | Drop repeated elements that are adjacent to each other.------ >>> uniq = Stream.uniqBy (==)----{-# INLINE_NORMAL uniq #-}-uniq :: (Eq a, Monad m) => Stream m a -> Stream m a--- uniq = scanMaybe FL.uniq-uniq (Stream step state) = Stream step' (Nothing, state)-  where-    {-# INLINE_LATE step' #-}-    step' gst (Nothing, st) = do-        r <- step gst st-        case r of-            Yield x s -> return $ Yield x (Just x, s)-            Skip  s   -> return $ Skip  (Nothing, s)-            Stop      -> return Stop-    step' gst (Just x, st)  = do-         r <- step gst st-         case r of-             Yield y s | x == y   -> return $ Skip (Just x, s)-                       | otherwise -> return $ Yield y (Just y, s)-             Skip  s   -> return $ Skip (Just x, s)-             Stop      -> return Stop---- | Deletes the first occurrence of the element in the stream that satisfies--- the given equality predicate.------ >>> input = Stream.fromList [1,3,3,5]--- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input--- [1,3,5]----{-# INLINE_NORMAL deleteBy #-}-deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a--- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)-deleteBy eq x (Stream step state) = Stream step' (state, False)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, False) = do-        r <- step gst st-        case r of-            Yield y s -> return $-                if eq x y then Skip (s, True) else Yield y (s, False)-            Skip s -> return $ Skip (s, False)-            Stop   -> return Stop--    step' gst (st, True) = do-        r <- step gst st-        case r of-            Yield y s -> return $ Yield y (s, True)-            Skip s -> return $ Skip (s, True)-            Stop   -> return Stop---- | Strip all leading and trailing occurrences of an element passing a--- predicate and make all other consecutive occurrences uniq.------ >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)------ @--- > Stream.prune isSpace (Stream.fromList "  hello      world!   ")--- "hello world!"------ @------ Space: @O(1)@------ /Unimplemented/-{-# INLINE prune #-}-prune ::-    -- (Monad m, Eq a) =>-    (a -> Bool) -> Stream m a -> Stream m a-prune = error "Not implemented yet!"---- Possible implementation:--- @repeated =---      Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@------ 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a--- single element.---- | Emit only repeated elements, once.------ /Unimplemented/-repeated :: -- (Monad m, Eq a) =>-    Stream m a -> Stream m a-repeated = undefined----------------------------------------------------------------------------------- Trimming----------------------------------------------------------------------------------- | Take all consecutive elements at the end of the stream for which the--- predicate is true.------ O(n) space, where n is the number elements taken.------ /Unimplemented/-{-# INLINE takeWhileLast #-}-takeWhileLast :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m---- | Like 'takeWhile' and 'takeWhileLast' combined.------ O(n) space, where n is the number elements taken from the end.------ /Unimplemented/-{-# INLINE takeWhileAround #-}-takeWhileAround :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m---- Adapted from the vector package---- | Discard first 'n' elements from the stream and take the rest.----{-# INLINE_NORMAL drop #-}-drop :: Monad m => Int -> Stream m a -> Stream m a-drop n (Stream step state) = Stream step' (state, Just n)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, Just i)-      | i > 0 = do-          r <- step gst st-          return $-            case r of-              Yield _ s -> Skip (s, Just (i - 1))-              Skip s    -> Skip (s, Just i)-              Stop      -> Stop-      | otherwise = return $ Skip (st, Nothing)--    step' gst (st, Nothing) = do-      r <- step gst st-      return $-        case r of-          Yield x s -> Yield x (s, Nothing)-          Skip  s   -> Skip (s, Nothing)-          Stop      -> Stop---- Adapted from the vector package-data DropWhileState s a-    = DropWhileDrop s-    | DropWhileYield a s-    | DropWhileNext s---- | Same as 'dropWhile' but with a monadic predicate.----{-# INLINE_NORMAL dropWhileM #-}-dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a--- dropWhileM p = scanMaybe (FL.droppingWhileM p)-dropWhileM f (Stream step state) = Stream step' (DropWhileDrop state)-  where-    {-# INLINE_LATE step' #-}-    step' gst (DropWhileDrop st) = do-        r <- step gst st-        case r of-            Yield x s -> do-                b <- f x-                if b-                then return $ Skip (DropWhileDrop s)-                else return $ Skip (DropWhileYield x s)-            Skip s -> return $ Skip (DropWhileDrop s)-            Stop -> return Stop--    step' gst (DropWhileNext st) =  do-        r <- step gst st-        case r of-            Yield x s -> return $ Skip (DropWhileYield x s)-            Skip s    -> return $ Skip (DropWhileNext s)-            Stop      -> return Stop--    step' _ (DropWhileYield x st) = return $ Yield x (DropWhileNext st)---- | Drop elements in the stream as long as the predicate succeeds and then--- take the rest of the stream.----{-# INLINE dropWhile #-}-dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a--- dropWhile p = scanMaybe (FL.droppingWhile p)-dropWhile f = dropWhileM (return . f)---- | Drop @n@ elements at the end of the stream.------ O(n) space, where n is the number elements dropped.------ /Unimplemented/-{-# INLINE dropLast #-}-dropLast :: -- Monad m =>-    Int -> Stream m a -> Stream m a-dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m---- | Drop all consecutive elements at the end of the stream for which the--- predicate is true.------ O(n) space, where n is the number elements dropped.------ /Unimplemented/-{-# INLINE dropWhileLast #-}-dropWhileLast :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m---- | Like 'dropWhile' and 'dropWhileLast' combined.------ O(n) space, where n is the number elements dropped from the end.------ /Unimplemented/-{-# INLINE dropWhileAround #-}-dropWhileAround :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m----------------------------------------------------------------------------------- Inserting Elements----------------------------------------------------------------------------------- | @insertBy cmp elem stream@ inserts @elem@ before the first element in--- @stream@ that is less than @elem@ when compared using @cmp@.------ >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)------ >>> input = Stream.fromList [1,3,5]--- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input--- [1,2,3,5]----{-# INLINE_NORMAL insertBy #-}-insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a-insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, False, _) = do-        r <- step gst st-        case r of-            Yield x s -> case cmp a x of-                GT -> return $ Yield x (s, False, Nothing)-                _  -> return $ Yield a (s, True, Just x)-            Skip s -> return $ Skip (s, False, Nothing)-            Stop   -> return $ Yield a (st, True, Nothing)--    step' _ (_, True, Nothing) = return Stop--    step' gst (st, True, Just prev) = do-        r <- step gst st-        case r of-            Yield x s -> return $ Yield prev (s, True, Just x)-            Skip s    -> return $ Skip (s, True, Just prev)-            Stop      -> return $ Yield prev (st, True, Nothing)--data LoopState x s = FirstYield s-                   | InterspersingYield s-                   | YieldAndCarry x s---- intersperseM = intersperseMWith 1---- | Insert an effect and its output before consuming an element of a stream--- except the first one.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input--- h.,e.,l.,l.,o"h,e,l,l,o"------ Be careful about the order of effects. In the above example we used trace--- after the intersperse, if we use it before the intersperse the output would--- be he.l.l.o."h,e,l,l,o".------ >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input--- he.l.l.o."h,e,l,l,o"----{-# INLINE_NORMAL intersperseM #-}-intersperseM :: Monad m => m a -> Stream m a -> Stream m a-intersperseM m (Stream step state) = Stream step' (FirstYield state)-  where-    {-# INLINE_LATE step' #-}-    step' gst (FirstYield st) = do-        r <- step gst st-        return $-            case r of-                Yield x s -> Skip (YieldAndCarry x s)-                Skip s -> Skip (FirstYield s)-                Stop -> Stop--    step' gst (InterspersingYield st) = do-        r <- step gst st-        case r of-            Yield x s -> do-                a <- m-                return $ Yield a (YieldAndCarry x s)-            Skip s -> return $ Skip $ InterspersingYield s-            Stop -> return Stop--    step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)---- | Insert a pure value between successive elements of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input--- "h,e,l,l,o"----{-# INLINE intersperse #-}-intersperse :: Monad m => a -> Stream m a -> Stream m a-intersperse a = intersperseM (return a)---- | Insert a side effect before consuming an element of a stream except the--- first one.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input--- h.e.l.l.o------ /Pre-release/-{-# INLINE_NORMAL intersperseM_ #-}-intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseM_ m (Stream step1 state1) = Stream step (Left (pure (), state1))-  where-    {-# INLINE_LATE step #-}-    step gst (Left (eff, st)) = do-        r <- step1 gst st-        case r of-            Yield x s -> eff >> return (Yield x (Right s))-            Skip s -> return $ Skip (Left (eff, s))-            Stop -> return Stop--    step _ (Right st) = return $ Skip $ Left (void m, st)---- | Intersperse a monadic action into the input stream after every @n@--- elements.------ >> input = Stream.fromList "hello"--- >> Stream.fold Fold.toList $ Stream.intersperseMWith 2 (return ',') input--- "he,ll,o"------ /Unimplemented/-{-# INLINE intersperseMWith #-}-intersperseMWith :: -- Monad m =>-    Int -> m a -> Stream m a -> Stream m a-intersperseMWith _n _f _xs = undefined--data SuffixState s a-    = SuffixElem s-    | SuffixSuffix s-    | SuffixYield a (SuffixState s a)---- | Insert an effect and its output after consuming an element of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMSuffix (putChar '.' >> return ',') input--- h.,e.,l.,l.,o.,"h,e,l,l,o,"------ /Pre-release/-{-# INLINE_NORMAL intersperseMSuffix #-}-intersperseMSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a-intersperseMSuffix action (Stream step state) = Stream step' (SuffixElem state)-    where-    {-# INLINE_LATE step' #-}-    step' gst (SuffixElem st) = do-        r <- step gst st-        return $ case r of-            Yield x s -> Skip (SuffixYield x (SuffixSuffix s))-            Skip s -> Skip (SuffixElem s)-            Stop -> Stop--    step' _ (SuffixSuffix st) = do-        action >>= \r -> return $ Skip (SuffixYield r (SuffixElem st))--    step' _ (SuffixYield x next) = return $ Yield x next---- | Insert a side effect after consuming an element of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffix_ (threadDelay 1000000) input--- "hello"------ /Pre-release/----{-# INLINE_NORMAL intersperseMSuffix_ #-}-intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseMSuffix_ m (Stream step1 state1) = Stream step (Left state1)-  where-    {-# INLINE_LATE step #-}-    step gst (Left st) = do-        r <- step1 gst st-        case r of-            Yield x s -> return $ Yield x (Right s)-            Skip s -> return $ Skip $ Left s-            Stop -> return Stop--    step _ (Right st) = m >> return (Skip (Left st))--data SuffixSpanState s a-    = SuffixSpanElem s Int-    | SuffixSpanSuffix s-    | SuffixSpanYield a (SuffixSpanState s a)-    | SuffixSpanLast-    | SuffixSpanStop---- | Like 'intersperseMSuffix' but intersperses an effectful action into the--- input stream after every @n@ elements and after the last element.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffixWith 2 (return ',') input--- "he,ll,o,"------ /Pre-release/----{-# INLINE_NORMAL intersperseMSuffixWith #-}-intersperseMSuffixWith :: forall m a. Monad m-    => Int -> m a -> Stream m a -> Stream m a-intersperseMSuffixWith n action (Stream step state) =-    Stream step' (SuffixSpanElem state n)-    where-    {-# INLINE_LATE step' #-}-    step' gst (SuffixSpanElem st i) | i > 0 = do-        r <- step gst st-        return $ case r of-            Yield x s -> Skip (SuffixSpanYield x (SuffixSpanElem s (i - 1)))-            Skip s -> Skip (SuffixSpanElem s i)-            Stop -> if i == n then Stop else Skip SuffixSpanLast-    step' _ (SuffixSpanElem st _) = return $ Skip (SuffixSpanSuffix st)--    step' _ (SuffixSpanSuffix st) = do-        action >>= \r -> return $ Skip (SuffixSpanYield r (SuffixSpanElem st n))--    step' _ SuffixSpanLast = do-        action >>= \r -> return $ Skip (SuffixSpanYield r SuffixSpanStop)--    step' _ (SuffixSpanYield x next) = return $ Yield x next--    step' _ SuffixSpanStop = return Stop---- | Insert a side effect before consuming an element of a stream.------ Definition:------ >>> intersperseMPrefix_ m = Stream.mapM (\x -> void m >> return x)------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMPrefix_ (putChar '.' >> return ',') input--- .h.e.l.l.o"hello"------ Same as 'trace_'.------ /Pre-release/----{-# INLINE intersperseMPrefix_ #-}-intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseMPrefix_ m = mapM (\x -> void m >> return x)----------------------------------------------------------------------------------- Inserting Time----------------------------------------------------------------------------------- XXX This should be in Prelude, should we export this as a helper function?---- | Block the current thread for specified number of seconds.-{-# INLINE sleep #-}-sleep :: MonadIO m => Double -> m ()-sleep n = liftIO $ threadDelay $ round $ n * 1000000---- | Introduce a delay of specified seconds between elements of the stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delay = Stream.intersperseM_ . sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delay 1 input--- 1--- 2--- 3----{-# INLINE delay #-}-delay :: MonadIO m => Double -> Stream m a -> Stream m a-delay = intersperseM_ . sleep---- | Introduce a delay of specified seconds after consuming an element of a--- stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delayPost = Stream.intersperseMSuffix_ . sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPost 1 input--- 1--- 2--- 3------ /Pre-release/----{-# INLINE delayPost #-}-delayPost :: MonadIO m => Double -> Stream m a -> Stream m a-delayPost n = intersperseMSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000---- | Introduce a delay of specified seconds before consuming an element of a--- stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delayPre = Stream.intersperseMPrefix_. sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPre 1 input--- 1--- 2--- 3------ /Pre-release/----{-# INLINE delayPre #-}-delayPre :: MonadIO m => Double -> Stream m a -> Stream m a-delayPre = intersperseMPrefix_. sleep----------------------------------------------------------------------------------- Reordering----------------------------------------------------------------------------------- | Returns the elements of the stream in reverse order.  The stream must be--- finite. Note that this necessarily buffers the entire stream in memory.------ Definition:------ >>> reverse m = Stream.concatEffect $ Stream.fold Fold.toListRev m >>= return . Stream.fromList----{-# INLINE_NORMAL reverse #-}-reverse :: Monad m => Stream m a -> Stream m a-reverse m = concatEffect $ fold FL.toListRev m <&> fromList-{--reverse m = Stream step Nothing-    where-    {-# INLINE_LATE step #-}-    step _ Nothing = do-        xs <- foldl' (flip (:)) [] m-        return $ Skip (Just xs)-    step _ (Just (x:xs)) = return $ Yield x (Just xs)-    step _ (Just []) = return Stop--}---- | Like 'reverse' but several times faster, requires an 'Unbox' instance.------ /O(n) space/------ /Pre-release/-{-# INLINE reverseUnbox #-}-reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a-reverseUnbox =-    A.flattenArraysRev -- unfoldMany A.readRev-        . fromStreamK-        . K.reverse-        . toStreamK-        . A.chunksOf defaultChunkSize---- | Buffer until the next element in sequence arrives. The function argument--- determines the difference in sequence numbers. This could be useful in--- implementing sequenced streams, for example, TCP reassembly.------ /Unimplemented/----{-# INLINE reassembleBy #-}-reassembleBy-    :: -- Monad m =>-       Fold m a b-    -> (a -> a -> Int)-    -> Stream m a-    -> Stream m b-reassembleBy = undefined----------------------------------------------------------------------------------- Position Indexing----------------------------------------------------------------------------------- Adapted from the vector package---- |--- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)--- >>> indexed = Stream.postscan f--- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)--- >>> indexedR n = fmap (\(i, a) -> (n - i, a)) . indexed------ Pair each element in a stream with its index, starting from index 0.------ >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"--- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]----{-# INLINE_NORMAL indexed #-}-indexed :: Monad m => Stream m a -> Stream m (Int, a)--- indexed = scanMaybe FL.indexing-indexed (Stream step state) = Stream step' (state, 0)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, i) = i `seq` do-         r <- step (adaptState gst) st-         case r of-             Yield x s -> return $ Yield (i, x) (s, i+1)-             Skip    s -> return $ Skip (s, i)-             Stop      -> return Stop---- Adapted from the vector package---- |--- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)--- >>> indexedR n = Stream.postscan (f n)------ >>> s n = Stream.enumerateFromThen n (n - 1)--- >>> indexedR n = Stream.zipWith (,) (s n)------ Pair each element in a stream with its index, starting from the--- given index @n@ and counting down.------ >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"--- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]----{-# INLINE_NORMAL indexedR #-}-indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)--- indexedR n = scanMaybe (FL.indexingRev n)-indexedR m (Stream step state) = Stream step' (state, m)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, i) = i `seq` do-         r <- step (adaptState gst) st-         case r of-             Yield x s -> let i' = i - 1-                          in return $ Yield (i, x) (s, i')-             Skip    s -> return $ Skip (s, i)-             Stop      -> return Stop------------------------------------------------------------------------------------ Time Indexing------------------------------------------------------------------------------------ Note: The timestamp stream must be the second stream in the zip so that the--- timestamp is generated after generating the stream element and not before.--- If we do not do that then the following example will generate the same--- timestamp for first two elements:------ Stream.fold Fold.toList $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3---- | Pair each element in a stream with an absolute timestamp, using a clock of--- specified granularity.  The timestamp is generated just before the element--- is consumed.------ >>> Stream.fold Fold.toList $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(AbsTime (TimeSpec {sec = ..., nsec = ...}),1),(AbsTime (TimeSpec {sec = ..., nsec = ...}),2),(AbsTime (TimeSpec {sec = ..., nsec = ...}),3)]------ /Pre-release/----{-# INLINE timestampWith #-}-timestampWith :: (MonadIO m)-    => Double -> Stream m a -> Stream m (AbsTime, a)-timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)---- TBD: check performance vs a custom implementation without using zipWith.------ /Pre-release/----{-# INLINE timestamped #-}-timestamped :: (MonadIO m)-    => Stream m a -> Stream m (AbsTime, a)-timestamped = timestampWith 0.01---- | Pair each element in a stream with relative times starting from 0, using a--- clock with the specified granularity. The time is measured just before the--- element is consumed.------ >>> Stream.fold Fold.toList $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]------ /Pre-release/----{-# INLINE timeIndexWith #-}-timeIndexWith :: (MonadIO m)-    => Double -> Stream m a -> Stream m (RelTime64, a)-timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)---- | Pair each element in a stream with relative times starting from 0, using a--- 10 ms granularity clock. The time is measured just before the element is--- consumed.------ >>> Stream.fold Fold.toList $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]------ /Pre-release/----{-# INLINE timeIndexed #-}-timeIndexed :: (MonadIO m)-    => Stream m a -> Stream m (RelTime64, a)-timeIndexed = timeIndexWith 0.01----------------------------------------------------------------------------------- Searching----------------------------------------------------------------------------------- | Find all the indices where the element in the stream satisfies the given--- predicate.------ >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)----{-# INLINE_NORMAL findIndices #-}-findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int-findIndices p (Stream step state) = Stream step' (state, 0)-  where-    {-# INLINE_LATE step' #-}-    step' gst (st, i) = i `seq` do-      r <- step (adaptState gst) st-      return $ case r of-          Yield x s -> if p x then Yield i (s, i+1) else Skip (s, i+1)-          Skip s -> Skip (s, i)-          Stop   -> Stop---- | Find all the indices where the value of the element in the stream is equal--- to the given value.------ >>> elemIndices a = Stream.findIndices (== a)----{-# INLINE elemIndices #-}-elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int-elemIndices a = findIndices (== a)--{-# INLINE_NORMAL slicesBy #-}-slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)-slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))--    where--    {-# INLINE_LATE step #-}-    step gst (Just (st, i, len)) = i `seq` len `seq` do-      r <- step1 (adaptState gst) st-      return-        $ case r of-              Yield x s ->-                if p x-                then Yield (i, len + 1) (Just (s, i + len + 1, 0))-                else Skip (Just (s, i, len + 1))-              Skip s -> Skip (Just (s, i, len))-              Stop -> if len == 0 then Stop else Yield (i, len) Nothing-    step _ Nothing = return Stop----------------------------------------------------------------------------------- Rolling map---------------------------------------------------------------------------------data RollingMapState s a = RollingMapGo s a---- | Like 'rollingMap' but with an effectful map function.------ /Pre-release/----{-# INLINE rollingMapM #-}-rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b--- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)-rollingMapM f (Stream step1 state1) = Stream step (RollingMapGo state1 Nothing)--    where--    step gst (RollingMapGo s1 curr) = do-        r <- step1 (adaptState gst) s1-        case r of-            Yield x s -> do-                !res <- f curr x-                return $ Yield res $ RollingMapGo s (Just x)-            Skip s -> return $ Skip $ RollingMapGo s curr-            Stop   -> return Stop---- rollingMap is a special case of an incremental sliding fold. It can be--- written as:------ > fld f = slidingWindow 1 (Fold.foldl' (\_ (x,y) -> f y x)--- > rollingMap f = Stream.postscan (fld f) undefined---- | Apply a function on every two successive elements of a stream. The first--- argument of the map function is the previous element and the second argument--- is the current element. When the current element is the first element, the--- previous element is 'Nothing'.------ /Pre-release/----{-# INLINE rollingMap #-}-rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b--- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)-rollingMap f = rollingMapM (\x y -> return $ f x y)---- | Like 'rollingMap' but requires at least two elements in the stream,--- returns an empty stream otherwise.------ This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.------ /Pre-release/----{-# INLINE rollingMap2 #-}-rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b-rollingMap2 f = catMaybes . rollingMap g--    where--    g Nothing _ = Nothing-    g (Just x) y = Just (f x y)----------------------------------------------------------------------------------- Maybe Streams----------------------------------------------------------------------------------- XXX Will this always fuse properly?---- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'--- elements, and return a stream of values extracted from 'Just'.------ Equivalent to:------ >>> mapMaybe f = Stream.catMaybes . fmap f----{-# INLINE_NORMAL mapMaybe #-}-mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b-mapMaybe f = fmap fromJust . filter isJust . map f---- | Like 'mapMaybe' but maps a monadic function.------ Equivalent to:------ >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f------ >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)----{-# INLINE_NORMAL mapMaybeM #-}-mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b-mapMaybeM f = fmap fromJust . filter isJust . mapM f---- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.------ >>> catMaybes = Stream.mapMaybe id--- >>> catMaybes = fmap fromJust . Stream.filter isJust------ /Pre-release/----{-# INLINE catMaybes #-}-catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a--- catMaybes = fmap fromJust . filter isJust-catMaybes (Stream step state) = Stream step1 state--    where--    {-# INLINE_LATE step1 #-}-    step1 gst st = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> do-                return-                    $ case x of-                        Just a -> Yield a s-                        Nothing -> Skip s-            Skip s -> return $ Skip s-            Stop -> return Stop---- | Use a filtering fold on a stream.------ >>> scanMaybe f = Stream.catMaybes . Stream.postscan f----{-# INLINE scanMaybe #-}-scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b-scanMaybe f = catMaybes . postscan f----------------------------------------------------------------------------------- Either streams----------------------------------------------------------------------------------- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.------ >>> catLefts = fmap (fromLeft undefined) . Stream.filter isLeft------ /Pre-release/----{-# INLINE catLefts #-}-catLefts :: Monad m => Stream m (Either a b) -> Stream m a-catLefts = fmap (fromLeft undefined) . filter isLeft---- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.------ >>> catRights = fmap (fromRight undefined) . Stream.filter isRight------ /Pre-release/----{-# INLINE catRights #-}-catRights :: Monad m => Stream m (Either a b) -> Stream m b-catRights = fmap (fromRight undefined) . filter isRight---- | Remove the either wrapper and flatten both lefts and as well as rights in--- the output stream.------ >>> catEithers = fmap (either id id)------ /Pre-release/----{-# INLINE catEithers #-}-catEithers :: Monad m => Stream m (Either a a) -> Stream m a-catEithers = fmap (either id id)----------------------------------------------------------------------------------- Splitting----------------------------------------------------------------------------------- | Split on an infixed separator element, dropping the separator.  The--- supplied 'Fold' is applied on the split segments.  Splits the stream on--- separator elements determined by the supplied predicate, separator is--- considered as infixed between two segments:------ >>> splitOn' p xs = Stream.fold Fold.toList $ Stream.splitOn p Fold.toList (Stream.fromList xs)--- >>> splitOn' (== '.') "a.b"--- ["a","b"]------ An empty stream is folded to the default value of the fold:------ >>> splitOn' (== '.') ""--- [""]------ If one or both sides of the separator are missing then the empty segment on--- that side is folded to the default output of the fold:------ >>> splitOn' (== '.') "."--- ["",""]------ >>> splitOn' (== '.') ".a"--- ["","a"]------ >>> splitOn' (== '.') "a."--- ["a",""]------ >>> splitOn' (== '.') "a..b"--- ["a","","b"]------ splitOn is an inverse of intercalating single element:------ > Stream.intercalate (Stream.fromPure '.') Unfold.fromList . Stream.splitOn (== '.') Fold.toList === id------ Assuming the input stream does not contain the separator:------ > Stream.splitOn (== '.') Fold.toList . Stream.intercalate (Stream.fromPure '.') Unfold.fromList === id----{-# INLINE splitOn #-}-splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b-splitOn predicate f =-    -- We can express the infix splitting in terms of optional suffix split-    -- fold.  After applying a suffix split fold repeatedly if the last segment-    -- ends with a suffix then we need to return the default output of the fold-    -- after that to make it an infix split.-    ---    -- Alternately, we can also express it using an optional prefix split fold.-    -- If the first segment starts with a prefix then we need to emit the-    -- default output of the fold before that to make it an infix split, and-    -- then apply prefix split fold repeatedly.-    ---    -- Since a suffix split fold can be easily expressed using a-    -- non-backtracking fold, we use that.-    foldManyPost (FL.takeEndBy_ predicate f)
− src/Streamly/Internal/Data/Stream/StreamD/Transformer.hs
@@ -1,182 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Transformer--- Copyright   : (c) 2018 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ Transform the underlying monad of a stream using a monad transfomer.--module Streamly.Internal.Data.Stream.StreamD.Transformer-    (-      foldlT-    , foldrT--    -- * Transform Inner Monad-    , liftInner-    , runReaderT-    , usingReaderT-    , evalStateT-    , runStateT-    , usingStateT-    )-where--#include "inline.hs"--import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Monad.Trans.Reader (ReaderT)-import Control.Monad.Trans.State.Strict (StateT)-import GHC.Types (SPEC(..))-import Streamly.Internal.Data.SVar.Type (defState, adaptState)--import qualified Control.Monad.Trans.Reader as Reader-import qualified Control.Monad.Trans.State.Strict as State--import Streamly.Internal.Data.Stream.StreamD.Type--#include "DocTestDataStream.hs"---- | Lazy left fold to a transformer monad.----{-# INLINE_NORMAL foldlT #-}-foldlT :: (Monad m, Monad (s m), MonadTrans s)-    => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b-foldlT fstep begin (Stream step state) = go SPEC begin state-  where-    go !_ acc st = do-        r <- lift $ step defState st-        case r of-            Yield x s -> go SPEC (fstep acc x) s-            Skip s -> go SPEC acc s-            Stop   -> acc---- | Right fold to a transformer monad.  This is the most general right fold--- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'--- implementation can be more efficient:------ >>> foldrS = Stream.foldrT------ >>> step f x xs = lift $ f x (runIdentityT xs)--- >>> foldrM f z s = runIdentityT $ Stream.foldrT (step f) (lift z) s------ 'foldrT' can be used to translate streamly streams to other transformer--- monads e.g.  to a different streaming type.------ /Pre-release/-{-# INLINE_NORMAL foldrT #-}-foldrT :: (Monad m, Monad (t m), MonadTrans t)-    => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b-foldrT f final (Stream step state) = go SPEC state-  where-    {-# INLINE_LATE go #-}-    go !_ st = do-          r <- lift $ step defState st-          case r of-            Yield x s -> f x (go SPEC s)-            Skip s    -> go SPEC s-            Stop      -> final------------------------------------------------------------------------------------ Transform Inner Monad------------------------------------------------------------------------------------ | Lift the inner monad @m@ of @Stream m a@ to @t m@ where @t@ is a monad--- transformer.----{-# INLINE_NORMAL liftInner #-}-liftInner :: (Monad m, MonadTrans t, Monad (t m))-    => Stream m a -> Stream (t m) a-liftInner (Stream step state) = Stream step' state-    where-    {-# INLINE_LATE step' #-}-    step' gst st = do-        r <- lift $ step (adaptState gst) st-        return $ case r of-            Yield x s -> Yield x s-            Skip s    -> Skip s-            Stop      -> Stop----------------------------------------------------------------------------------- Sharing read only state in a stream----------------------------------------------------------------------------------- | Evaluate the inner monad of a stream as 'ReaderT'.----{-# INLINE_NORMAL runReaderT #-}-runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a-runReaderT env (Stream step state) = Stream step' (state, env)-    where-    {-# INLINE_LATE step' #-}-    step' gst (st, action) = do-        sv <- action-        r <- Reader.runReaderT (step (adaptState gst) st) sv-        return $ case r of-            Yield x s -> Yield x (s, return sv)-            Skip  s   -> Skip (s, return sv)-            Stop      -> Stop---- | Run a stream transformation using a given environment.----{-# INLINE usingReaderT #-}-usingReaderT-    :: Monad m-    => m r-    -> (Stream (ReaderT r m) a -> Stream (ReaderT r m) a)-    -> Stream m a-    -> Stream m a-usingReaderT r f xs = runReaderT r $ f $ liftInner xs----------------------------------------------------------------------------------- Sharing read write state in a stream----------------------------------------------------------------------------------- | Evaluate the inner monad of a stream as 'StateT'.------ >>> evalStateT s = fmap snd . Stream.runStateT s----{-# INLINE_NORMAL evalStateT #-}-evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a-evalStateT initial (Stream step state) = Stream step' (state, initial)-    where-    {-# INLINE_LATE step' #-}-    step' gst (st, action) = do-        sv <- action-        (r, !sv') <- State.runStateT (step (adaptState gst) st) sv-        return $ case r of-            Yield x s -> Yield x (s, return sv')-            Skip  s   -> Skip (s, return sv')-            Stop      -> Stop---- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting--- state and value pair after each step.----{-# INLINE_NORMAL runStateT #-}-runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)-runStateT initial (Stream step state) = Stream step' (state, initial)-    where-    {-# INLINE_LATE step' #-}-    step' gst (st, action) = do-        sv <- action-        (r, !sv') <- State.runStateT (step (adaptState gst) st) sv-        return $ case r of-            Yield x s -> Yield (sv', x) (s, return sv')-            Skip  s   -> Skip (s, return sv')-            Stop      -> Stop---- | Run a stateful (StateT) stream transformation using a given state.------ >>> usingStateT s f = Stream.evalStateT s . f . Stream.liftInner------ See also: 'scan'----{-# INLINE usingStateT #-}-usingStateT-    :: Monad m-    => m s-    -> (Stream (StateT s m) a -> Stream (StateT s m) a)-    -> Stream m a-    -> Stream m a-usingStateT s f = evalStateT s . f . liftInner
− src/Streamly/Internal/Data/Stream/StreamD/Type.hs
@@ -1,2074 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE UndecidableInstances #-}---- |--- Module      : Streamly.Internal.Data.Stream.StreamD.Type--- Copyright   : (c) 2018 Composewell Technologies---               (c) Roman Leshchinskiy 2008-2010--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC---- The stream type is inspired by the vector package.  A few functions in this--- module have been originally adapted from the vector package (c) Roman--- Leshchinskiy. See the notes in specific functions.--module Streamly.Internal.Data.Stream.StreamD.Type-    (-    -- * The stream type-      Step (..)-    -- XXX UnStream is exported to avoid a performance issue in some-    -- combinators if we use the pattern synonym "Stream".-    , Stream (Stream, UnStream)--    -- * CrossStream type wrapper-    , CrossStream-    , unCross-    , mkCross--    -- * Conversion to StreamK-    , fromStreamK-    , toStreamK--    -- * From Unfold-    , unfold--    -- * Construction-    -- ** Primitives-    , nilM-    , consM--    -- ** From Values-    , fromPure-    , fromEffect--    -- ** From Containers-    , Streamly.Internal.Data.Stream.StreamD.Type.fromList--    -- * Elimination-    -- ** Primitives-    , uncons--    -- ** Strict Left Folds-    , Streamly.Internal.Data.Stream.StreamD.Type.fold-    , foldBreak-    , foldAddLazy-    , foldAdd-    , foldEither--    , Streamly.Internal.Data.Stream.StreamD.Type.foldl'-    , foldlM'-    , foldlx'-    , foldlMx'--    -- ** Lazy Right Folds-    , foldrM-    , foldrMx-    , Streamly.Internal.Data.Stream.StreamD.Type.foldr-    , foldrS--    -- ** Specific Folds-    , drain-    , Streamly.Internal.Data.Stream.StreamD.Type.toList--    -- * Mapping-    , map-    , mapM--    -- * Stateful Filters-    , take-    , takeWhile-    , takeWhileM-    , takeEndBy-    , takeEndByM--    -- * Combining Two Streams-    -- ** Zipping-    , zipWithM-    , zipWith--    -- ** Cross Product-    , crossApply-    , crossApplyFst-    , crossApplySnd-    , crossWith-    , cross--    -- * Unfold Many-    , ConcatMapUState (..)-    , unfoldMany--    -- * Concat-    , concatEffect-    , concatMap-    , concatMapM-    , concat--    -- * Unfold Iterate-    , unfoldIterateDfs-    , unfoldIterateBfs-    , unfoldIterateBfsRev--    -- * Concat Iterate-    , concatIterateScan-    , concatIterateDfs-    , concatIterateBfs-    , concatIterateBfsRev--    -- * Fold Many-    , FoldMany (..) -- for inspection testing-    , FoldManyPost (..)-    , foldMany-    , foldManyPost-    , groupsOf-    , refoldMany--    -- * Fold Iterate-    , reduceIterateBfs-    , foldIterateBfs--    -- * Multi-stream folds-    , eqBy-    , cmpBy-    )-where--#include "inline.hs"--import Control.Applicative (liftA2)-import Control.Monad.Catch (MonadThrow, throwM)-import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Monad.IO.Class (MonadIO(..))-import Data.Foldable (Foldable(foldl'), fold, foldr)-import Data.Functor (($>))-import Data.Functor.Identity (Identity(..))-import Data.Maybe (fromMaybe)-import Data.Semigroup (Endo(..))-import Fusion.Plugin.Types (Fuse(..))-import GHC.Base (build)-import GHC.Exts (IsList(..), IsString(..), oneShot)-import GHC.Types (SPEC(..))-import Prelude hiding (map, mapM, take, concatMap, takeWhile, zipWith, concat)-import Text.Read-       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec-       , readListPrecDefault)--import Streamly.Internal.BaseCompat ((#.))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)-import Streamly.Internal.Data.Refold.Type (Refold(..))-import Streamly.Internal.Data.Stream.StreamD.Step (Step (..))-import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)-import Streamly.Internal.Data.Unfold.Type (Unfold(..))--import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K-#ifdef USE_UNFOLDS_EVERYWHERE-import qualified Streamly.Internal.Data.Unfold.Type as Unfold-#endif--#include "DocTestDataStream.hs"----------------------------------------------------------------------------------- The direct style stream type----------------------------------------------------------------------------------- gst = global state---- | A stream consists of a step function that generates the next step given a--- current state, and the current state.-data Stream m a =-    forall s. UnStream (State K.StreamK m a -> s -> m (Step s a)) s---- XXX This causes perf trouble when pattern matching with "Stream"  in a--- recursive way, e.g. in uncons, foldBreak, concatMap. We need to get rid of--- this.-unShare :: Stream m a -> Stream m a-unShare (UnStream step state) = UnStream step' state-    where step' gst = step (adaptState gst)--pattern Stream :: (State K.StreamK m a -> s -> m (Step s a)) -> s -> Stream m a-pattern Stream step state <- (unShare -> UnStream step state)-    where Stream = UnStream--{-# COMPLETE Stream #-}----------------------------------------------------------------------------------- Primitives----------------------------------------------------------------------------------- | A stream that terminates without producing any output, but produces a side--- effect.------ >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))--- "nil"--- []------ /Pre-release/-{-# INLINE_NORMAL nilM #-}-nilM :: Applicative m => m b -> Stream m a-nilM m = Stream (\_ _ -> m $> Stop) ()---- | Like 'cons' but fuses an effect instead of a pure value.-{-# INLINE_NORMAL consM #-}-consM :: Applicative m => m a -> Stream m a -> Stream m a-consM m (Stream step state) = Stream step1 Nothing--    where--    {-# INLINE_LATE step1 #-}-    step1 _ Nothing = (`Yield` Just state) <$> m-    step1 gst (Just st) = do-          (\case-            Yield a s -> Yield a (Just s)-            Skip  s   -> Skip (Just s)-            Stop      -> Stop) <$> step gst st---- | Decompose a stream into its head and tail. If the stream is empty, returns--- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is--- the head of the stream and @ma@ its tail.------ Properties:------ >>> Nothing <- Stream.uncons Stream.nil--- >>> Just ("a", t) <- Stream.uncons (Stream.cons "a" Stream.nil)------ This can be used to consume the stream in an imperative manner one element--- at a time, as it just breaks down the stream into individual elements and we--- can loop over them as we deem fit. For example, this can be used to convert--- a streamly stream into other stream types.------ All the folds in this module can be expressed in terms of 'uncons', however,--- this is generally less efficient than specific folds because it takes apart--- the stream one element at a time, therefore, does not take adavantage of--- stream fusion.------ 'foldBreak' is a more general way of consuming a stream piecemeal.------ >>> :{--- uncons xs = do---     r <- Stream.foldBreak Fold.one xs---     return $ case r of---         (Nothing, _) -> Nothing---         (Just h, t) -> Just (h, t)--- :}----{-# INLINE_NORMAL uncons #-}-uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))-uncons (UnStream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield x s -> return $ Just (x, Stream step s)-            Skip  s   -> go SPEC s-            Stop      -> return Nothing----------------------------------------------------------------------------------- From 'Unfold'---------------------------------------------------------------------------------data UnfoldState s = UnfoldNothing | UnfoldJust s---- | Convert an 'Unfold' into a stream by supplying it an input seed.------ >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello")--- >>> Stream.fold Fold.drain s--- hello--- hello--- 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----------------------------------------------------------------------------------- From Values----------------------------------------------------------------------------------- | Create a singleton stream from a pure value.------ >>> fromPure a = a `Stream.cons` Stream.nil--- >>> fromPure = pure--- >>> fromPure = Stream.fromEffect . pure----{-# INLINE_NORMAL fromPure #-}-fromPure :: Applicative m => a -> Stream m a-fromPure x = Stream (\_ s -> pure $ step undefined s) True-  where-    {-# INLINE_LATE step #-}-    step _ True  = Yield x False-    step _ False = Stop---- | Create a singleton stream from a monadic action.------ >>> fromEffect m = m `Stream.consM` Stream.nil--- >>> fromEffect = Stream.sequence . Stream.fromPure------ >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")--- hello----{-# INLINE_NORMAL fromEffect #-}-fromEffect :: Applicative m => m a -> Stream m a-fromEffect m = Stream step True--    where--    {-# INLINE_LATE step #-}-    step _ True  = (`Yield` False) <$> m-    step _ False = pure Stop----------------------------------------------------------------------------------- From Containers----------------------------------------------------------------------------------- Adapted from the vector package.---- | Construct a stream from a list of pure values.-{-# INLINE_LATE fromList #-}-fromList :: Applicative m => [a] -> Stream m a-#ifdef USE_UNFOLDS_EVERYWHERE-fromList = unfold Unfold.fromList-#else-fromList = Stream step-  where-    {-# INLINE_LATE step #-}-    step _ (x:xs) = pure $ Yield x xs-    step _ []     = pure Stop-#endif----------------------------------------------------------------------------------- Conversions From/To----------------------------------------------------------------------------------- | Convert a CPS encoded StreamK to direct style step encoded StreamD-{-# INLINE_LATE fromStreamK #-}-fromStreamK :: Applicative m => K.StreamK m a -> Stream m a-fromStreamK = Stream step-    where-    step gst m1 =-        let stop       = pure Stop-            single a   = pure $ Yield a K.nil-            yieldk a r = pure $ Yield a r-         in K.foldStreamShared gst yieldk single stop m1---- | Convert a direct style step encoded StreamD to a CPS encoded StreamK-{-# INLINE_LATE toStreamK #-}-toStreamK :: Monad m => Stream m a -> K.StreamK m a-toStreamK (Stream step state) = go state-    where-    go st = K.MkStream $ \gst yld _ stp ->-      let go' ss = do-           r <- step gst ss-           case r of-               Yield x s -> yld x (go s)-               Skip  s   -> go' s-               Stop      -> stp-      in go' st--#ifndef DISABLE_FUSION-{-# RULES "fromStreamK/toStreamK fusion"-    forall s. toStreamK (fromStreamK s) = s #-}-{-# RULES "toStreamK/fromStreamK fusion"-    forall s. fromStreamK (toStreamK s) = s #-}-#endif----------------------------------------------------------------------------------- Running a 'Fold'----------------------------------------------------------------------------------- >>> fold f = Fold.extractM . Stream.foldAddLazy f--- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f--- >>> fold f = Fold.extractM <=< Stream.foldAdd f---- | Fold a stream using the supplied left 'Fold' and reducing the resulting--- expression strictly at each step. The behavior is similar to 'foldl''. A--- 'Fold' can terminate early without consuming the full stream. See the--- documentation of individual 'Fold's for termination behavior.------ Definitions:------ >>> fold f = fmap fst . Stream.foldBreak f--- >>> fold f = Stream.parse (Parser.fromFold f)------ Example:------ >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)--- 5050----{-# INLINE_NORMAL fold #-}-fold :: Monad m => Fold m a b -> Stream m a -> m b-fold fld strm = do-    (b, _) <- foldBreak fld strm-    return b---- | Fold resulting in either breaking the stream or continuation of the fold.--- Instead of supplying the input stream in one go we can run the fold multiple--- times, each time supplying the next segment of the input stream. If the fold--- has not yet finished it returns a fold that can be run again otherwise it--- returns the fold result and the residual stream.------ /Internal/-{-# INLINE_NORMAL foldEither #-}-foldEither :: Monad m =>-    Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))-foldEither (Fold fstep begin done) (UnStream step state) = do-    res <- begin-    case res of-        FL.Partial fs -> go SPEC fs state-        FL.Done fb -> return $! Right (fb, Stream step state)--    where--    {-# INLINE go #-}-    go !_ !fs st = do-        r <- step defState st-        case r of-            Yield x s -> do-                res <- fstep fs x-                case res of-                    FL.Done b -> return $! Right (b, Stream step s)-                    FL.Partial fs1 -> go SPEC fs1 s-            Skip s -> go SPEC fs s-            Stop -> return $! Left (Fold fstep (return $ FL.Partial fs) done)---- | Like 'fold' but also returns the remaining stream. The resulting stream--- would be 'Stream.nil' if the stream finished before the fold.----{-# INLINE_NORMAL foldBreak #-}-foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)-foldBreak fld strm = do-    r <- foldEither fld strm-    case r of-        Right res -> return res-        Left (Fold _ initial extract) -> do-            res <- initial-            case res of-                FL.Done _ -> error "foldBreak: unreachable state"-                FL.Partial s -> do-                    b <- extract s-                    return (b, nil)--    where--    nil = Stream (\_ _ -> return Stop) ()---- | Append a stream to a fold lazily to build an accumulator incrementally.------ Example, to continue folding a list of streams on the same sum fold:------ >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]--- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams--- >>> Stream.fold f Stream.nil--- 55----{-# INLINE_NORMAL foldAddLazy #-}-foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b-foldAddLazy (Fold fstep finitial fextract) (Stream sstep state) =-    Fold fstep initial fextract--    where--    initial = do-        res <- finitial-        case res of-            FL.Partial fs -> go SPEC fs state-            FL.Done fb -> return $ FL.Done fb--    {-# INLINE go #-}-    go !_ !fs st = do-        r <- sstep defState st-        case r of-            Yield x s -> do-                res <- fstep fs x-                case res of-                    FL.Done b -> return $ FL.Done b-                    FL.Partial fs1 -> go SPEC fs1 s-            Skip s -> go SPEC fs s-            Stop -> return $ FL.Partial fs---- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce---- |--- >>> foldAdd = flip Fold.addStream----foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)-foldAdd f =-    Streamly.Internal.Data.Stream.StreamD.Type.fold (FL.duplicate f)----------------------------------------------------------------------------------- Right Folds----------------------------------------------------------------------------------- Adapted from the vector package.------ XXX Use of SPEC constructor in folds causes 2x performance degradation in--- one shot operations, but helps immensely in operations composed of multiple--- combinators or the same combinator many times. There seems to be an--- opportunity to optimize here, can we get both, better perf for single ops--- as well as composed ops? Without SPEC, all single operation benchmarks--- become 2x faster.---- The way we want a left fold to be strict, dually we want the right fold to--- be lazy.  The correct signature of the fold function to keep it lazy must be--- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter--- earlier, which is incorrect. In the latter signature we have to feed the--- value to the fold function after evaluating the monadic action, depending on--- the bind behavior of the monad, the action may get evaluated immediately--- introducing unnecessary strictness to the fold. If the implementation is--- lazy the following example, must work:------ S.foldrM (\x t -> if x then return t else return False) (return True)---  (S.fromList [False,undefined] :: Stream IO Bool)---- | Right associative/lazy pull fold. @foldrM build final stream@ constructs--- an output structure using the step function @build@. @build@ is invoked with--- the next input element and the remaining (lazy) tail of the output--- structure. It builds a lazy output expression using the two. When the "tail--- structure" in the output expression is evaluated it calls @build@ again thus--- lazily consuming the input @stream@ until either the output expression built--- by @build@ is free of the "tail" or the input is exhausted in which case--- @final@ is used as the terminating case for the output structure. For more--- details see the description in the previous section.------ Example, determine if any element is 'odd' in a stream:------ >>> s = Stream.fromList (2:4:5:undefined)--- >>> step x xs = if odd x then return True else xs--- >>> Stream.foldrM step (return False) s--- True----{-# INLINE_NORMAL foldrM #-}-foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b-foldrM f z (Stream step state) = go SPEC state-  where-    {-# INLINE_LATE go #-}-    go !_ st = do-          r <- step defState st-          case r of-            Yield x s -> f x (go SPEC s)-            Skip s    -> go SPEC s-            Stop      -> z--{-# INLINE_NORMAL foldrMx #-}-foldrMx :: Monad m-    => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b-foldrMx fstep final convert (Stream step state) = convert $ go SPEC state-  where-    {-# INLINE_LATE go #-}-    go !_ st = do-          r <- step defState st-          case r of-            Yield x s -> fstep x (go SPEC s)-            Skip s    -> go SPEC s-            Stop      -> final---- XXX Should we make all argument strict wherever we use SPEC?---- Note that foldr works on pure values, therefore it becomes necessarily--- strict when the monad m is strict. In that case it cannot terminate early,--- it would evaluate all of its input.  Though, this should work fine with lazy--- monads. For example, if "any" is implemented using "foldr" instead of--- "foldrM" it performs the same with Identity monad but performs 1000x slower--- with IO monad.---- | Right fold, lazy for lazy monads and pure streams, and strict for strict--- monads.------ Please avoid using this routine in strict monads like IO unless you need a--- strict right fold. This is provided only for use in lazy monads (e.g.--- Identity) or pure streams. Note that with this signature it is not possible--- to implement a lazy foldr when the monad @m@ is strict. In that case it--- would be strict in its accumulator and therefore would necessarily consume--- all its input.------ >>> foldr f z = Stream.foldrM (\a b -> f a <$> b) (return z)------ Note: This is similar to Fold.foldr' (the right fold via left fold), but--- could be more efficient.----{-# INLINE_NORMAL foldr #-}-foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b-foldr f z = foldrM (liftA2 f . return) (return z)---- this performs horribly, should not be used-{-# INLINE_NORMAL foldrS #-}-foldrS-    :: Monad m-    => (a -> Stream m b -> Stream m b)-    -> Stream m b-    -> Stream m a-    -> Stream m b-foldrS f final (Stream step state) = go SPEC state-  where-    {-# INLINE_LATE go #-}-    go !_ st = concatEffect $ fmap g $ step defState st--    g r =-        case r of-          Yield x s -> f x (go SPEC s)-          Skip s    -> go SPEC s-          Stop      -> final----------------------------------------------------------------------------------- Left Folds----------------------------------------------------------------------------------- XXX run begin action only if the stream is not empty.-{-# INLINE_NORMAL foldlMx' #-}-foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b-foldlMx' fstep begin done (Stream step state) =-    begin >>= \x -> go SPEC x state-  where-    -- XXX !acc?-    {-# INLINE_LATE go #-}-    go !_ acc st = acc `seq` do-        r <- step defState st-        case r of-            Yield x s -> do-                acc' <- fstep acc x-                go SPEC acc' s-            Skip s -> go SPEC acc s-            Stop   -> done acc--{-# INLINE foldlx' #-}-foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b-foldlx' fstep begin done =-    foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done)---- Adapted from the vector package.--- XXX implement in terms of foldlMx'?-{-# INLINE_NORMAL foldlM' #-}-foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b-foldlM' fstep mbegin (Stream step state) = do-    begin <- mbegin-    go SPEC begin state-  where-    {-# INLINE_LATE go #-}-    go !_ acc st = acc `seq` do-        r <- step defState st-        case r of-            Yield x s -> do-                acc' <- fstep acc x-                go SPEC acc' s-            Skip s -> go SPEC acc s-            Stop   -> return acc--{-# INLINE foldl' #-}-foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b-foldl' fstep begin = foldlM' (\b a -> return (fstep b a)) (return begin)----------------------------------------------------------------------------------- Special folds----------------------------------------------------------------------------------- >>> drain = mapM_ (\_ -> return ())---- |--- Definitions:------ >>> drain = Stream.fold Fold.drain--- >>> drain = Stream.foldrM (\_ xs -> xs) (return ())------ Run a stream, discarding the results.----{-# INLINE_LATE drain #-}-drain :: Monad m => Stream m a -> m ()--- drain = foldrM (\_ xs -> xs) (return ())-drain (Stream step state) = go SPEC state-  where-    go !_ st = do-        r <- step defState st-        case r of-            Yield _ s -> go SPEC s-            Skip s    -> go SPEC s-            Stop      -> return ()----------------------------------------------------------------------------------- To Containers----------------------------------------------------------------------------------- This toList impl is faster (30% on streaming-benchmarks) than the--- corresponding left fold. The left fold retains an additional argument in the--- recursive loop.------ Core for the right fold loop:------ main_$s$wgo1---   = \ sc_s3e6 sc1_s3e5 ->---       case ># sc1_s3e5 100000# of {---         __DEFAULT ->---           case main_$s$wgo1 sc_s3e6 (+# sc1_s3e5 1#) of------ Core for the left fold loop:------  main_$s$wgo1---   = \ sc_s3oT sc1_s3oS sc2_s3oR ->---       case sc2_s3oR of fs2_a2lw { __DEFAULT ->---       case ># sc1_s3oS 100000# of {---         __DEFAULT ->---           let { wild_a2og = I# sc1_s3oS } in---           main_$s$wgo1---             sc_s3oT (+# sc1_s3oS 1#) (\ x_X9 -> fs2_a2lw (: wild_a2og x_X9));---- |--- Definitions:------ >>> toList = Stream.foldr (:) []--- >>> toList = Stream.fold Fold.toList------ Convert a stream into a list in the underlying monad. The list can be--- consumed lazily in a lazy monad (e.g. 'Identity'). In a strict monad (e.g.--- IO) the whole list is generated and buffered before it can be consumed.------ /Warning!/ working on large lists accumulated as buffers in memory could be--- very inefficient, consider using "Streamly.Data.Array" instead.------ Note that this could a bit more efficient compared to @Stream.fold--- Fold.toList@, and it can fuse with pure list consumers.----{-# INLINE_NORMAL toList #-}-toList :: Monad m => Stream m a -> m [a]-toList = Streamly.Internal.Data.Stream.StreamD.Type.foldr (:) []---- Use foldr/build fusion to fuse with list consumers--- This can be useful when using the IsList instance-{-# INLINE_LATE toListFB #-}-toListFB :: (a -> b -> b) -> b -> Stream Identity a -> b-toListFB c n (Stream step state) = go state-  where-    go st = case runIdentity (step defState st) of-             Yield x s -> x `c` go s-             Skip s    -> go s-             Stop      -> n--{-# RULES "toList Identity" Streamly.Internal.Data.Stream.StreamD.Type.toList = toListId #-}-{-# INLINE_EARLY toListId #-}-toListId :: Stream Identity a -> Identity [a]-toListId s = Identity $ build (\c n -> toListFB c n s)----------------------------------------------------------------------------------- Multi-stream folds----------------------------------------------------------------------------------- Adapted from the vector package.---- | Compare two streams for equality-{-# INLINE_NORMAL eqBy #-}-eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool-eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2-  where-    eq_loop0 !_ s1 s2 = do-      r <- step1 defState s1-      case r of-        Yield x s1' -> eq_loop1 SPEC x s1' s2-        Skip    s1' -> eq_loop0 SPEC   s1' s2-        Stop        -> eq_null s2--    eq_loop1 !_ x s1 s2 = do-      r <- step2 defState s2-      case r of-        Yield y s2'-          | eq x y    -> eq_loop0 SPEC   s1 s2'-          | otherwise -> return False-        Skip    s2'   -> eq_loop1 SPEC x s1 s2'-        Stop          -> return False--    eq_null s2 = do-      r <- step2 defState s2-      case r of-        Yield _ _ -> return False-        Skip s2'  -> eq_null s2'-        Stop      -> return True---- Adapted from the vector package.---- | Compare two streams lexicographically.-{-# INLINE_NORMAL cmpBy #-}-cmpBy-    :: Monad m-    => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering-cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2-  where-    cmp_loop0 !_ s1 s2 = do-      r <- step1 defState s1-      case r of-        Yield x s1' -> cmp_loop1 SPEC x s1' s2-        Skip    s1' -> cmp_loop0 SPEC   s1' s2-        Stop        -> cmp_null s2--    cmp_loop1 !_ x s1 s2 = do-      r <- step2 defState s2-      case r of-        Yield y s2' -> case x `cmp` y of-                         EQ -> cmp_loop0 SPEC s1 s2'-                         c  -> return c-        Skip    s2' -> cmp_loop1 SPEC x s1 s2'-        Stop        -> return GT--    cmp_null s2 = do-      r <- step2 defState s2-      case r of-        Yield _ _ -> return LT-        Skip s2'  -> cmp_null s2'-        Stop      -> return EQ----------------------------------------------------------------------------------- Transformations----------------------------------------------------------------------------------- Adapted from the vector package.---- |--- >>> mapM f = Stream.sequence . fmap f------ Apply a monadic function to each element of the stream and replace it with--- the output of the resulting action.------ >>> s = Stream.fromList ["a", "b", "c"]--- >>> Stream.fold Fold.drain $ Stream.mapM putStr s--- abc----{-# INLINE_NORMAL mapM #-}-mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b-mapM f (Stream step state) = Stream step' state-  where-    {-# INLINE_LATE step' #-}-    step' gst st = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> f x >>= \a -> return $ Yield a s-            Skip s    -> return $ Skip s-            Stop      -> return Stop--{-# INLINE map #-}-map :: Monad m => (a -> b) -> Stream m a -> Stream m b-map f = mapM (return . f)---- (Functor m) based implementation of fmap does not fuse well in--- streaming-benchmarks. XXX need to investigate why.-instance Monad m => Functor (Stream m) where-    {-# INLINE fmap #-}-    fmap = map--    {-# INLINE (<$) #-}-    (<$) = fmap . const----------------------------------------------------------------------------------- Lists----------------------------------------------------------------------------------- XXX Show instance is 10x slower compared to read, we can do much better.--- The list show instance itself is really slow.---- XXX The default definitions of "<" in the Ord instance etc. do not perform--- well, because they do not get inlined. Need to add INLINE in Ord class in--- base?--instance IsList (Stream Identity a) where-    type (Item (Stream Identity a)) = a--    {-# INLINE fromList #-}-    fromList = Streamly.Internal.Data.Stream.StreamD.Type.fromList--    {-# INLINE toList #-}-    toList = runIdentity . Streamly.Internal.Data.Stream.StreamD.Type.toList--instance Eq a => Eq (Stream Identity a) where-    {-# INLINE (==) #-}-    (==) xs ys = runIdentity $ eqBy (==) xs ys--instance Ord a => Ord (Stream Identity a) where-    {-# INLINE compare #-}-    compare xs ys = runIdentity $ cmpBy compare xs ys--    {-# INLINE (<) #-}-    x < y =-        case compare x y of-            LT -> True-            _ -> False--    {-# INLINE (<=) #-}-    x <= y =-        case compare x y of-            GT -> False-            _ -> True--    {-# INLINE (>) #-}-    x > y =-        case compare x y of-            GT -> True-            _ -> False--    {-# INLINE (>=) #-}-    x >= y =-        case compare x y of-            LT -> False-            _ -> True--    {-# INLINE max #-}-    max x y = if x <= y then y else x--    {-# INLINE min #-}-    min x y = if x <= y then x else y--instance Show a => Show (Stream Identity a) where-    showsPrec p dl = showParen (p > 10) $-        showString "fromList " . shows (GHC.Exts.toList dl)--instance Read a => Read (Stream Identity a) where-    readPrec = parens $ prec 10 $ do-        Ident "fromList" <- lexP-        Streamly.Internal.Data.Stream.StreamD.Type.fromList <$> readPrec--    readListPrec = readListPrecDefault--instance (a ~ Char) => IsString (Stream Identity a) where-    {-# INLINE fromString #-}-    fromString = Streamly.Internal.Data.Stream.StreamD.Type.fromList------------------------------------------------------------------------------------ Foldable------------------------------------------------------------------------------------ The default Foldable instance has several issues:--- 1) several definitions do not have INLINE on them, so we provide---    re-implementations with INLINE pragmas.--- 2) the definitions of sum/product/maximum/minimum are inefficient as they---    use right folds, they cannot run in constant memory. We provide---    implementations using strict left folds here.---- There is no Traversable instance because, there is no scalable cons for--- StreamD, use toList and fromList instead.--instance (Foldable m, Monad m) => Foldable (Stream m) where--    {-# INLINE foldMap #-}-    foldMap f =-        Data.Foldable.fold-            . Streamly.Internal.Data.Stream.StreamD.Type.foldr (mappend . f) mempty--    {-# INLINE foldr #-}-    foldr f z t = appEndo (foldMap (Endo #. f) t) z--    {-# INLINE foldl' #-}-    foldl' f z0 xs = Data.Foldable.foldr f' id xs z0-        where f' x k = oneShot $ \z -> k $! f z x--    {-# INLINE length #-}-    length = Data.Foldable.foldl' (\n _ -> n + 1) 0--    {-# INLINE elem #-}-    elem = any . (==)--    {-# INLINE maximum #-}-    maximum =-          fromMaybe (errorWithoutStackTrace "maximum: empty stream")-        . toMaybe-        . Data.Foldable.foldl' getMax Nothing'--        where--        getMax Nothing' x = Just' x-        getMax (Just' mx) x = Just' $! max mx x--    {-# INLINE minimum #-}-    minimum =-          fromMaybe (errorWithoutStackTrace "minimum: empty stream")-        . toMaybe-        . Data.Foldable.foldl' getMin Nothing'--        where--        getMin Nothing' x = Just' x-        getMin (Just' mn) x = Just' $! min mn x--    {-# INLINE sum #-}-    sum = Data.Foldable.foldl' (+) 0--    {-# INLINE product #-}-    product = Data.Foldable.foldl' (*) 1------------------------------------------------------------------------------------ Filtering------------------------------------------------------------------------------------ Adapted from the vector package.---- | Take first 'n' elements from the stream and discard the rest.----{-# INLINE_NORMAL take #-}-take :: Applicative m => Int -> Stream m a -> Stream m a-take n (Stream step state) = n `seq` Stream step' (state, 0)--    where--    {-# INLINE_LATE step' #-}-    step' gst (st, i) | i < n = do-        (\case-            Yield x s -> Yield x (s, i + 1)-            Skip s    -> Skip (s, i)-            Stop      -> Stop) <$> step gst st-    step' _ (_, _) = pure Stop---- Adapted from the vector package.---- | Same as 'takeWhile' but with a monadic predicate.----{-# INLINE_NORMAL takeWhileM #-}-takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a--- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))-takeWhileM f (Stream step state) = Stream step' state-  where-    {-# INLINE_LATE step' #-}-    step' gst st = do-        r <- step gst st-        case r of-            Yield x s -> do-                b <- f x-                return $ if b then Yield x s else Stop-            Skip s -> return $ Skip s-            Stop   -> return Stop---- | End the stream as soon as the predicate fails on an element.----{-# INLINE takeWhile #-}-takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a-takeWhile f = takeWhileM (return . f)---- Like takeWhile but with an inverted condition and also taking--- the matching element.--{-# INLINE_NORMAL takeEndByM #-}-takeEndByM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a-takeEndByM f (Stream step state) = Stream step' (Just state)-  where-    {-# INLINE_LATE step' #-}-    step' gst (Just st) = do-        r <- step gst st-        case r of-            Yield x s -> do-                b <- f x-                return $-                    if not b-                    then Yield x (Just s)-                    else Yield x Nothing-            Skip s -> return $ Skip (Just s)-            Stop   -> return Stop--    step' _ Nothing = return Stop--{-# INLINE takeEndBy #-}-takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a-takeEndBy f = takeEndByM (return . f)----------------------------------------------------------------------------------- Zipping----------------------------------------------------------------------------------- | Like 'zipWith' but using a monadic zipping function.----{-# INLINE_NORMAL zipWithM #-}-zipWithM :: Monad m-    => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c-zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)-  where-    {-# INLINE_LATE step #-}-    step gst (sa, sb, Nothing) = do-        r <- stepa (adaptState gst) sa-        return $-          case r of-            Yield x sa' -> Skip (sa', sb, Just x)-            Skip sa'    -> Skip (sa', sb, Nothing)-            Stop        -> Stop--    step gst (sa, sb, Just x) = do-        r <- stepb (adaptState gst) sb-        case r of-            Yield y sb' -> do-                z <- f x y-                return $ Yield z (sa, sb', Nothing)-            Skip sb' -> return $ Skip (sa, sb', Just x)-            Stop     -> return Stop--{-# RULES "zipWithM xs xs"-    forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}---- | Stream @a@ is evaluated first, followed by stream @b@, the resulting--- elements @a@ and @b@ are then zipped using the supplied zip function and the--- result @c@ is yielded to the consumer.------ If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends--- first, the element @a@ from previous evaluation of stream @a@ is discarded.------ >>> s1 = Stream.fromList [1,2,3]--- >>> s2 = Stream.fromList [4,5,6]--- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2--- [5,7,9]----{-# INLINE zipWith #-}-zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c-zipWith f = zipWithM (\a b -> return (f a b))----------------------------------------------------------------------------------- Combine N Streams - concatAp----------------------------------------------------------------------------------- | Apply a stream of functions to a stream of values and flatten the results.------ Note that the second stream is evaluated multiple times.------ >>> crossApply = Stream.crossWith id----{-# INLINE_NORMAL crossApply #-}-crossApply :: Functor f => Stream f (a -> b) -> Stream f a -> Stream f b-crossApply (Stream stepa statea) (Stream stepb stateb) =-    Stream step' (Left statea)--    where--    {-# INLINE_LATE step' #-}-    step' gst (Left st) = fmap-        (\case-            Yield f s -> Skip (Right (f, s, stateb))-            Skip    s -> Skip (Left s)-            Stop      -> Stop)-        (stepa (adaptState gst) st)-    step' gst (Right (f, os, st)) = fmap-        (\case-            Yield a s -> Yield (f a) (Right (f, os, s))-            Skip s    -> Skip (Right (f,os, s))-            Stop      -> Skip (Left os))-        (stepb (adaptState gst) st)--{-# INLINE_NORMAL crossApplySnd #-}-crossApplySnd :: Functor f => Stream f a -> Stream f b -> Stream f b-crossApplySnd (Stream stepa statea) (Stream stepb stateb) =-    Stream step (Left statea)--    where--    {-# INLINE_LATE step #-}-    step gst (Left st) =-        fmap-            (\case-                 Yield _ s -> Skip (Right (s, stateb))-                 Skip s -> Skip (Left s)-                 Stop -> Stop)-            (stepa (adaptState gst) st)-    step gst (Right (ostate, st)) =-        fmap-            (\case-                 Yield b s -> Yield b (Right (ostate, s))-                 Skip s -> Skip (Right (ostate, s))-                 Stop -> Skip (Left ostate))-            (stepb gst st)--{-# INLINE_NORMAL crossApplyFst #-}-crossApplyFst :: Functor f => Stream f a -> Stream f b -> Stream f a-crossApplyFst (Stream stepa statea) (Stream stepb stateb) =-    Stream step (Left statea)--    where--    {-# INLINE_LATE step #-}-    step gst (Left st) =-        fmap-            (\case-                 Yield b s -> Skip (Right (s, stateb, b))-                 Skip s -> Skip (Left s)-                 Stop -> Stop)-            (stepa gst st)-    step gst (Right (ostate, st, b)) =-        fmap-            (\case-                 Yield _ s -> Yield b (Right (ostate, s, b))-                 Skip s -> Skip (Right (ostate, s, b))-                 Stop -> Skip (Left ostate))-            (stepb (adaptState gst) st)--{--instance Applicative f => Applicative (Stream f) where-    {-# INLINE pure #-}-    pure = fromPure--    {-# INLINE (<*>) #-}-    (<*>) = crossApply--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)--    {-# INLINE (*>) #-}-    (*>) = crossApplySnd--    {-# INLINE (<*) #-}-    (<*) = crossApplyFst--}---- |--- Definition:------ >>> crossWith f m1 m2 = fmap f m1 `Stream.crossApply` m2------ Note that the second stream is evaluated multiple times.----{-# INLINE crossWith #-}-crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c-crossWith f m1 m2 = fmap f m1 `crossApply` m2---- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible--- combinations of the tuple @(a, b)@.------ Definition:------ >>> cross = Stream.crossWith (,)------ The second stream is evaluated multiple times. If that is not desired it can--- be cached in an 'Data.Array.Array' and then generated from the array before--- calling this function. Caching may also improve performance if the stream is--- expensive to evaluate.------ See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused--- alternative.------ Time: O(m x n)------ /Pre-release/-{-# INLINE cross #-}-cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)-cross = crossWith (,)----------------------------------------------------------------------------------- Combine N Streams - unfoldMany---------------------------------------------------------------------------------{-# ANN type ConcatMapUState Fuse #-}-data ConcatMapUState o i =-      ConcatMapUOuter o-    | ConcatMapUInner o i---- | @unfoldMany unfold stream@ uses @unfold@ to map the input stream elements--- to streams and then flattens the generated streams into a single output--- stream.---- This is like 'concatMap' but uses an unfold with an explicit state to--- generate the stream instead of a 'Stream' type generator. This allows better--- optimization via fusion.  This can be many times more efficient than--- 'concatMap'.---- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike--- 'concatMap' this can fuse the 'Unfold' code with the inner loop and--- therefore provide many times better performance.----{-# INLINE_NORMAL unfoldMany #-}-unfoldMany :: Monad m => Unfold m a b -> Stream m a -> Stream m b-unfoldMany (Unfold istep inject) (Stream ostep ost) =-    Stream step (ConcatMapUOuter ost)-  where-    {-# INLINE_LATE step #-}-    step gst (ConcatMapUOuter o) = do-        r <- ostep (adaptState gst) o-        case r of-            Yield a o' -> do-                i <- inject a-                i `seq` return (Skip (ConcatMapUInner o' i))-            Skip o' -> return $ Skip (ConcatMapUOuter o')-            Stop -> return Stop--    step _ (ConcatMapUInner o i) = do-        r <- istep i-        return $ case r of-            Yield x i' -> Yield x (ConcatMapUInner o i')-            Skip i'    -> Skip (ConcatMapUInner o i')-            Stop       -> Skip (ConcatMapUOuter o)----------------------------------------------------------------------------------- Combine N Streams - concatMap----------------------------------------------------------------------------------- Adapted from the vector package.---- | Map a stream producing monadic function on each element of the stream--- and then flatten the results into a single stream. Since the stream--- generation function is monadic, unlike 'concatMap', it can produce an--- effect at the beginning of each iteration of the inner loop.------ See 'unfoldMany' for a fusible alternative.----{-# INLINE_NORMAL concatMapM #-}-concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b-concatMapM f (Stream step state) = Stream step' (Left state)-  where-    {-# INLINE_LATE step' #-}-    step' gst (Left st) = do-        r <- step (adaptState gst) st-        case r of-            Yield a s -> do-                b_stream <- f a-                return $ Skip (Right (b_stream, s))-            Skip s -> return $ Skip (Left s)-            Stop -> return Stop--    -- XXX flattenArrays is 5x faster than "concatMap fromArray". if somehow we-    -- can get inner_step to inline and fuse here we can perhaps get the same-    -- performance using "concatMap fromArray".-    ---    -- XXX using the pattern synonym "Stream" causes a major performance issue-    -- here even if the synonym does not include an adaptState call. Need to-    -- find out why. Is that something to be fixed in GHC?-    step' gst (Right (UnStream inner_step inner_st, st)) = do-        r <- inner_step (adaptState gst) inner_st-        case r of-            Yield b inner_s ->-                return $ Yield b (Right (Stream inner_step inner_s, st))-            Skip inner_s ->-                return $ Skip (Right (Stream inner_step inner_s, st))-            Stop -> return $ Skip (Left st)---- | Map a stream producing function on each element of the stream and then--- flatten the results into a single stream.------ >>> concatMap f = Stream.concatMapM (return . f)--- >>> concatMap f = Stream.concat . fmap f--- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)------ See 'unfoldMany' for a fusible alternative.----{-# INLINE concatMap #-}-concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b-concatMap f = concatMapM (return . f)---- | Flatten a stream of streams to a single stream.------ >>> concat = Stream.concatMap id------ /Pre-release/-{-# INLINE concat #-}-concat :: Monad m => Stream m (Stream m a) -> Stream m a-concat = concatMap id---- XXX The idea behind this rule is to rewrite any calls to "concatMap--- fromArray" automatically to flattenArrays which is much faster.  However, we--- need an INLINE_EARLY on concatMap for this rule to fire. But if we use--- INLINE_EARLY on concatMap or fromArray then direct uses of--- "concatMap fromArray" (without the RULE) become much slower, this means--- "concatMap f" in general would become slower. Need to find a solution to--- this.------ {-# RULES "concatMap Array.toStreamD"---      concatMap Array.toStreamD = Array.flattenArray #-}---- >>> concatEffect = Stream.concat . lift    -- requires (MonadTrans t)--- >>> concatEffect = join . lift             -- requires (MonadTrans t, Monad (Stream m))---- | Given a stream value in the underlying monad, lift and join the underlying--- monad with the stream monad.------ >>> concatEffect = Stream.concat . Stream.fromEffect--- >>> concatEffect eff = Stream.concatMapM (\() -> eff) (Stream.fromPure ())------ See also: 'concat', 'sequence'----{-# INLINE concatEffect #-}-concatEffect :: Monad m => m (Stream m a) -> Stream m a-concatEffect generator = concatMapM (\() -> generator) (fromPure ())--{---- NOTE: even though concatMap for StreamD is 4x faster compared to StreamK,--- the monad instance does not seem to be significantly faster.-instance Monad m => Monad (Stream m) where-    {-# INLINE return #-}-    return = pure--    {-# INLINE (>>=) #-}-    (>>=) = flip concatMap--    {-# INLINE (>>) #-}-    (>>) = (*>)--}----------------------------------------------------------------------------------- Traversing a tree top down----------------------------------------------------------------------------------- Next stream is to be generated by the return value of the previous stream. A--- general intuitive way of doing that could be to use an appending monad--- instance for streams where the result of the previous stream is used to--- generate the next one. In the first pass we can just emit the values in the--- stream and keep building a buffered list/stream, once done we can then--- process the buffered stream.---- | Generate a stream from an initial state, scan and concat the stream,--- generate a stream again from the final state of the previous scan and repeat--- the process.-{-# INLINE_NORMAL concatIterateScan #-}-concatIterateScan :: Monad m =>-       (b -> a -> m b)-    -> (b -> m (Maybe (b, Stream m a)))-    -> b-    -> Stream m a-concatIterateScan scanner generate initial = Stream step (Left initial)--    where--    {-# INLINE_LATE step #-}-    step _ (Left acc) = do-        r <- generate acc-        case r of-            Nothing -> return Stop-            Just v -> return $ Skip (Right v)--    step gst (Right (st, UnStream inner_step inner_st)) = do-        r <- inner_step (adaptState gst) inner_st-        case r of-            Yield b inner_s -> do-                acc <- scanner st b-                return $ Yield b (Right (acc, Stream inner_step inner_s))-            Skip inner_s ->-                return $ Skip (Right (st, Stream inner_step inner_s))-            Stop -> return $ Skip (Left st)---- Note: The iterate function returns a Maybe Stream instead of returning a nil--- stream for indicating a leaf node. This is to optimize so that we do not--- have to store any state. This makes the stored state proportional to the--- number of non-leaf nodes rather than total number of nodes.---- | Same as 'concatIterateBfs' except that the traversal of the last--- element on a level is emitted first and then going backwards up to the first--- element (reversed ordering). This may be slightly faster than--- 'concatIterateBfs'.----{-# INLINE_NORMAL concatIterateBfsRev #-}-concatIterateBfsRev :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateBfsRev f stream = Stream step (stream, [])--    where--    {-# INLINE_LATE step #-}-    step gst (UnStream step1 st, xs) = do-        r <- step1 (adaptState gst) st-        case r of-            Yield a s -> do-                let xs1 =-                        case f a of-                            Nothing -> xs-                            Just x -> x:xs-                return $ Yield a (Stream step1 s, xs1)-            Skip s -> return $ Skip (Stream step1 s, xs)-            Stop ->-                case xs of-                    (y:ys) -> return $ Skip (y, ys)-                    [] -> return Stop---- | Similar to 'concatIterateDfs' except that it traverses the stream in--- breadth first style (BFS). First, all the elements in the input stream are--- emitted, and then their traversals are emitted.------ Example, list a directory tree using BFS:------ >>> f = either (Just . Dir.readEitherPaths) (const Nothing)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateBfs f input------ /Pre-release/-{-# INLINE_NORMAL concatIterateBfs #-}-concatIterateBfs :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateBfs f stream = Stream step (stream, [], [])--    where--    {-# INLINE_LATE step #-}-    step gst (UnStream step1 st, xs, ys) = do-        r <- step1 (adaptState gst) st-        case r of-            Yield a s -> do-                let ys1 =-                        case f a of-                            Nothing -> ys-                            Just y -> y:ys-                return $ Yield a (Stream step1 s, xs, ys1)-            Skip s -> return $ Skip (Stream step1 s, xs, ys)-            Stop ->-                case xs of-                    (x:xs1) -> return $ Skip (x, xs1, ys)-                    [] ->-                        case reverse ys of-                            (x:xs1) -> return $ Skip (x, xs1, [])-                            [] -> return Stop---- | Traverse the stream in depth first style (DFS). Map each element in the--- input stream to a stream and flatten, recursively map the resulting elements--- as well to a stream and flatten until no more streams are generated.------ Example, list a directory tree using DFS:------ >>> f = either (Just . Dir.readEitherPaths) (const Nothing)--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.concatIterateDfs f input------ This is equivalent to using @concatIterateWith StreamK.append@.------ /Pre-release/-{-# INLINE_NORMAL concatIterateDfs #-}-concatIterateDfs :: Monad m =>-       (a -> Maybe (Stream m a))-    -> Stream m a-    -> Stream m a-concatIterateDfs f stream = Stream step (stream, [])--    where--    {-# INLINE_LATE step #-}-    step gst (UnStream step1 st, xs) = do-        r <- step1 (adaptState gst) st-        case r of-            Yield a s -> do-                let st1 =-                        case f a of-                            Nothing -> (Stream step1 s, xs)-                            Just x -> (x, Stream step1 s:xs)-                return $ Yield a st1-            Skip s -> return $ Skip (Stream step1 s, xs)-            Stop ->-                case xs of-                    (y:ys) -> return $ Skip (y, ys)-                    [] -> return Stop--{-# ANN type IterateUnfoldState Fuse #-}-data IterateUnfoldState o i =-      IterateUnfoldOuter o-    | IterateUnfoldInner o i [i]---- | Same as @concatIterateDfs@ but more efficient due to stream fusion.------ Example, list a directory tree using DFS:------ >>> f = Unfold.either Dir.eitherReaderPaths Unfold.nil--- >>> input = Stream.fromPure (Left ".")--- >>> ls = Stream.unfoldIterateDfs f input------ /Pre-release/-{-# INLINE_NORMAL unfoldIterateDfs #-}-unfoldIterateDfs :: Monad m =>-       Unfold m a a-    -> Stream m a-    -> Stream m a-unfoldIterateDfs (Unfold istep inject) (Stream ostep ost) =-    Stream step (IterateUnfoldOuter ost)--    where--    {-# 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--    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)-            Stop ->-                case ii of-                    (y:ys) -> return $ Skip (IterateUnfoldInner o y ys)-                    [] -> return $ Skip (IterateUnfoldOuter o)--{-# ANN type IterateUnfoldBFSRevState Fuse #-}-data IterateUnfoldBFSRevState o i =-      IterateUnfoldBFSRevOuter o [i]-    | IterateUnfoldBFSRevInner i [i]---- | Like 'unfoldIterateBfs' but processes the children in reverse order,--- therefore, may be slightly faster.------ /Pre-release/-{-# INLINE_NORMAL unfoldIterateBfsRev #-}-unfoldIterateBfsRev :: Monad m =>-       Unfold m a a-    -> Stream m a-    -> Stream m a-unfoldIterateBfsRev (Unfold istep inject) (Stream ostep ost) =-    Stream step (IterateUnfoldBFSRevOuter ost [])--    where--    {-# 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)-            Stop ->-                case ii of-                    (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys)-                    [] -> return 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)-            Stop ->-                case ii of-                    (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys)-                    [] -> return Stop--{-# ANN type IterateUnfoldBFSState Fuse #-}-data IterateUnfoldBFSState o i =-      IterateUnfoldBFSOuter o [i]-    | IterateUnfoldBFSInner i [i] [i]---- | Like 'unfoldIterateDfs' but uses breadth first style traversal.------ /Pre-release/-{-# INLINE_NORMAL unfoldIterateBfs #-}-unfoldIterateBfs :: Monad m =>-       Unfold m a a-    -> Stream m a-    -> Stream m a-unfoldIterateBfs (Unfold istep inject) (Stream ostep ost) =-    Stream step (IterateUnfoldBFSOuter ost [])--    where--    {-# 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)-            Stop ->-                case reverse rii of-                    (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys [])-                    [] -> return 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)-            Stop ->-                case ii of-                    (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys rii)-                    [] ->-                        case reverse rii of-                            (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys [])-                            [] -> return Stop----------------------------------------------------------------------------------- Folding a tree bottom up----------------------------------------------------------------------------------- | Binary BFS style reduce, folds a level entirely using the supplied fold--- function, collecting the outputs as next level of the tree, then repeats the--- same process on the next level. The last elements of a previously folded--- level are folded first.-{-# INLINE_NORMAL reduceIterateBfs #-}-reduceIterateBfs :: Monad m =>-    (a -> a -> m a) -> Stream m a -> m (Maybe a)-reduceIterateBfs f (Stream step state) = go SPEC state [] Nothing--    where--    go _ st xs Nothing = do-        r <- step defState st-        case r of-            Yield x1 s -> go SPEC s xs (Just x1)-            Skip s -> go SPEC s xs Nothing-            Stop ->-                case xs of-                    [] -> return Nothing-                    _ -> goBuf SPEC xs []-    go _ st xs (Just x1) = do-        r2 <- step defState st-        case r2 of-            Yield x2 s -> do-                x <- f x1 x2-                go SPEC s (x:xs) Nothing-            Skip s -> go SPEC s xs (Just x1)-            Stop ->-                case xs of-                    [] -> return (Just x1)-                    _ -> goBuf SPEC (x1:xs) []--    goBuf _ [] ys = goBuf SPEC ys []-    goBuf _ [x1] ys = do-        case ys of-            [] -> return (Just x1)-            (x2:xs) -> do-                y <- f x1 x2-                goBuf SPEC xs [y]-    goBuf _ (x1:x2:xs) ys = do-        y <- f x1 x2-        goBuf SPEC xs (y:ys)---- | N-Ary BFS style iterative fold, if the input stream finished before the--- fold then it returns Left otherwise Right. If the fold returns Left we--- terminate.------ /Unimplemented/-foldIterateBfs ::-    Fold m a (Either a a) -> Stream m a -> m (Maybe a)-foldIterateBfs = undefined----------------------------------------------------------------------------------- Grouping/Splitting----------------------------------------------------------------------------------- s = stream state, fs = fold state-{-# ANN type FoldManyPost Fuse #-}-data FoldManyPost s fs b a-    = FoldManyPostStart s-    | FoldManyPostLoop s fs-    | FoldManyPostYield b (FoldManyPost s fs b a)-    | FoldManyPostDone---- XXX Need a more intuitive name, and need to reconcile the names--- foldMany/fold/parse/parseMany/parseManyPost etc.---- XXX foldManyPost keeps the last fold always partial. if the last fold is--- complete then another fold is applied on empty input. This is used for--- applying folds like takeEndBy such that the last element is not the--- separator (infix style). But that looks like a hack. We should remove this--- and use a custom combinator for infix parsing.---- | Like 'foldMany' but evaluates the fold even if the fold did not receive--- any input, therefore, always results in a non-empty output even on an empty--- stream (default result of the fold).------ Example, empty stream:------ >>> f = Fold.take 2 Fold.sum--- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f--- >>> fmany $ Stream.fromList []--- [0]------ Example, last fold empty:------ >>> fmany $ Stream.fromList [1..4]--- [3,7,0]------ Example, last fold non-empty:------ >>> fmany $ Stream.fromList [1..5]--- [3,7,5]------ Note that using a closed fold e.g. @Fold.take 0@, would result in an--- infinite stream without consuming the input.------ /Pre-release/----{-# INLINE_NORMAL foldManyPost #-}-foldManyPost :: Monad m => Fold m a b -> Stream m a -> Stream m b-foldManyPost (Fold fstep initial extract) (Stream step state) =-    Stream step' (FoldManyPostStart state)--    where--    {-# INLINE consume #-}-    consume x s fs = do-        res <- fstep fs x-        return-            $ Skip-            $ case res of-                  FL.Done b -> FoldManyPostYield b (FoldManyPostStart s)-                  FL.Partial ps -> FoldManyPostLoop s ps--    {-# INLINE_LATE step' #-}-    step' _ (FoldManyPostStart st) = do-        r <- initial-        return-            $ Skip-            $ case r of-                  FL.Done b -> FoldManyPostYield b (FoldManyPostStart st)-                  FL.Partial fs -> FoldManyPostLoop st fs-    step' gst (FoldManyPostLoop st fs) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> consume x s fs-            Skip s -> return $ Skip (FoldManyPostLoop s fs)-            Stop -> do-                b <- extract fs-                return $ Skip (FoldManyPostYield b FoldManyPostDone)-    step' _ (FoldManyPostYield b next) = return $ Yield b next-    step' _ FoldManyPostDone = return Stop--{-# ANN type FoldMany Fuse #-}-data FoldMany s fs b a-    = FoldManyStart s-    | FoldManyFirst fs s-    | FoldManyLoop s fs-    | FoldManyYield b (FoldMany s fs b a)-    | FoldManyDone---- XXX Nested foldMany does not fuse.---- | Apply a 'Fold' repeatedly on a stream and emit the results in the output--- stream.------ Definition:------ >>> foldMany f = Stream.parseMany (Parser.fromFold f)------ Example, empty stream:------ >>> f = Fold.take 2 Fold.sum--- >>> fmany = Stream.fold Fold.toList . Stream.foldMany f--- >>> fmany $ Stream.fromList []--- []------ Example, last fold empty:------ >>> fmany $ Stream.fromList [1..4]--- [3,7]------ Example, last fold non-empty:------ >>> fmany $ Stream.fromList [1..5]--- [3,7,5]------ Note that using a closed fold e.g. @Fold.take 0@, would result in an--- infinite stream on a non-empty input stream.----{-# INLINE_NORMAL foldMany #-}-foldMany :: Monad m => Fold m a b -> Stream m a -> Stream m b-foldMany (Fold fstep initial extract) (Stream step state) =-    Stream step' (FoldManyStart state)--    where--    {-# INLINE consume #-}-    consume x s fs = do-        res <- fstep fs x-        return-            $ Skip-            $ case res of-                  FL.Done b -> FoldManyYield b (FoldManyStart s)-                  FL.Partial ps -> FoldManyLoop s ps--    {-# INLINE_LATE step' #-}-    step' _ (FoldManyStart st) = do-        r <- initial-        return-            $ Skip-            $ case r of-                  FL.Done b -> FoldManyYield b (FoldManyStart st)-                  FL.Partial fs -> FoldManyFirst fs st-    step' gst (FoldManyFirst fs st) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> consume x s fs-            Skip s -> return $ Skip (FoldManyFirst fs s)-            Stop -> return Stop-    step' gst (FoldManyLoop st fs) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> consume x s fs-            Skip s -> return $ Skip (FoldManyLoop s fs)-            Stop -> do-                b <- extract fs-                return $ Skip (FoldManyYield b FoldManyDone)-    step' _ (FoldManyYield b next) = return $ Yield b next-    step' _ FoldManyDone = return Stop--{-# INLINE groupsOf #-}-groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b-groupsOf n f = foldMany (FL.take n f)---- Keep the argument order consistent with refoldIterateM.---- | Like 'foldMany' but for the 'Refold' type.  The supplied action is used as--- the initial value for each refold.------ /Internal/-{-# INLINE_NORMAL refoldMany #-}-refoldMany :: Monad m => Refold m x a b -> m x -> Stream m a -> Stream m b-refoldMany (Refold fstep inject extract) action (Stream step state) =-    Stream step' (FoldManyStart state)--    where--    {-# INLINE consume #-}-    consume x s fs = do-        res <- fstep fs x-        return-            $ Skip-            $ case res of-                  FL.Done b -> FoldManyYield b (FoldManyStart s)-                  FL.Partial ps -> FoldManyLoop s ps--    {-# INLINE_LATE step' #-}-    step' _ (FoldManyStart st) = do-        r <- action >>= inject-        return-            $ Skip-            $ case r of-                  FL.Done b -> FoldManyYield b (FoldManyStart st)-                  FL.Partial fs -> FoldManyFirst fs st-    step' gst (FoldManyFirst fs st) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> consume x s fs-            Skip s -> return $ Skip (FoldManyFirst fs s)-            Stop -> return Stop-    step' gst (FoldManyLoop st fs) = do-        r <- step (adaptState gst) st-        case r of-            Yield x s -> consume x s fs-            Skip s -> return $ Skip (FoldManyLoop s fs)-            Stop -> do-                b <- extract fs-                return $ Skip (FoldManyYield b FoldManyDone)-    step' _ (FoldManyYield b next) = return $ Yield b next-    step' _ FoldManyDone = return Stop----------------------------------------------------------------------------------- Stream with a cross product style monad instance----------------------------------------------------------------------------------- XXX CrossStream performs better than the CrossStreamK when nesting two--- loops, however, CrossStreamK seems to be better for more than two nestings,--- need to do more perf investigation.---- | A newtype wrapper for the 'Stream' type with a cross product style monad--- instance.------ A 'Monad' bind behaves like a @for@ loop:------ >>> :{--- Stream.fold Fold.toList $ Stream.unCross $ do---     x <- Stream.mkCross $ Stream.fromList [1,2]---     -- Perform the following actions for each x in the stream---     return x--- :}--- [1,2]------ Nested monad binds behave like nested @for@ loops:------ >>> :{--- Stream.fold Fold.toList $ Stream.unCross $ do---     x <- Stream.mkCross $ Stream.fromList [1,2]---     y <- Stream.mkCross $ Stream.fromList [3,4]---     -- Perform the following actions for each x, for each y---     return (x, y)--- :}--- [(1,3),(1,4),(2,3),(2,4)]----newtype CrossStream m a = CrossStream {unCrossStream :: Stream m a}-        deriving (Functor, Foldable)--{-# INLINE mkCross #-}-mkCross :: Stream m a -> CrossStream m a-mkCross = CrossStream--{-# INLINE unCross #-}-unCross :: CrossStream m a -> Stream m a-unCross = unCrossStream---- Pure (Identity monad) stream instances-deriving instance IsList (CrossStream Identity a)-deriving instance (a ~ Char) => IsString (CrossStream Identity a)-deriving instance Eq a => Eq (CrossStream Identity a)-deriving instance Ord a => Ord (CrossStream Identity a)---- Do not use automatic derivation for this to show as "fromList" rather than--- "fromList Identity".-instance Show a => Show (CrossStream Identity a) where-    {-# INLINE show #-}-    show (CrossStream xs) = show xs--instance Read a => Read (CrossStream Identity a) where-    {-# INLINE readPrec #-}-    readPrec = fmap CrossStream readPrec----------------------------------------------------------------------------------- Applicative----------------------------------------------------------------------------------- Note: we need to define all the typeclass operations because we want to--- INLINE them.-instance Monad m => Applicative (CrossStream m) where-    {-# INLINE pure #-}-    pure x = CrossStream (fromPure x)--    {-# INLINE (<*>) #-}-    (CrossStream s1) <*> (CrossStream s2) =-        CrossStream (crossApply s1 s2)--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)--    {-# INLINE (*>) #-}-    (CrossStream s1) *> (CrossStream s2) =-        CrossStream (crossApplySnd s1 s2)--    {-# INLINE (<*) #-}-    (CrossStream s1) <* (CrossStream s2) =-        CrossStream (crossApplyFst s1 s2)----------------------------------------------------------------------------------- Monad---------------------------------------------------------------------------------instance Monad m => Monad (CrossStream m) where-    return = pure--    -- Benchmarks better with StreamD bind and pure:-    -- toList, filterAllout, *>, *<, >> (~2x)-    ----    -- Benchmarks better with CPS bind and pure:-    -- Prime sieve (25x)-    -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)-    ---    {-# INLINE (>>=) #-}-    (>>=) (CrossStream m) f = CrossStream (concatMap (unCrossStream . f) m)--    {-# INLINE (>>) #-}-    (>>) = (*>)----------------------------------------------------------------------------------- Transformers---------------------------------------------------------------------------------instance (MonadIO m) => MonadIO (CrossStream m) where-    liftIO x = CrossStream (fromEffect $ liftIO x)--instance MonadTrans CrossStream where-    {-# INLINE lift #-}-    lift x = CrossStream (fromEffect x)--instance (MonadThrow m) => MonadThrow (CrossStream m) where-    throwM = lift . throwM
− src/Streamly/Internal/Data/Stream/StreamDK.hs
@@ -1,52 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.StreamDK--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--------- This module has the following problems due to rewrite rules:------ * Rewrite rules lead to optimization problems, blocking fusion in some--- cases, specifically when combining multiple operations e.g. (filter . drop).--- * Rewrite rules lead to problems when calling a function recursively. For--- example, the StreamD version of foldBreak cannot be used recursively when--- wrapped in rewrite rules because each recursive call adds a roundtrip--- conversion from D to K and back to D. We can use the StreamK versions of--- these though because the rewrite rule gets eliminated in that case.--- * If we have a unified module, we need two different versions of several--- operations e.g. appendK and appendD, both are useful in different cases.----module Streamly.Internal.Data.Stream.StreamDK-    ( module Streamly.Internal.Data.Stream.Type-    , module Streamly.Internal.Data.Stream.Bottom-    , module Streamly.Internal.Data.Stream.Eliminate-    , module Streamly.Internal.Data.Stream.Exception-    , module Streamly.Internal.Data.Stream.Expand-    , module Streamly.Internal.Data.Stream.Generate-    , module Streamly.Internal.Data.Stream.Lift-    , module Streamly.Internal.Data.Stream.Reduce-    , module Streamly.Internal.Data.Stream.Transform-    , module Streamly.Internal.Data.Stream.Cross-    , module Streamly.Internal.Data.Stream.Zip--    -- modules having dependencies on libraries other than base-    , module Streamly.Internal.Data.Stream.Transformer-    )-where--import Streamly.Internal.Data.Stream.Bottom-import Streamly.Internal.Data.Stream.Cross-import Streamly.Internal.Data.Stream.Eliminate-import Streamly.Internal.Data.Stream.Exception-import Streamly.Internal.Data.Stream.Expand-import Streamly.Internal.Data.Stream.Generate-import Streamly.Internal.Data.Stream.Lift-import Streamly.Internal.Data.Stream.Reduce-import Streamly.Internal.Data.Stream.Transform-import Streamly.Internal.Data.Stream.Type-import Streamly.Internal.Data.Stream.Zip--import Streamly.Internal.Data.Stream.Transformer
− src/Streamly/Internal/Data/Stream/StreamK.hs
@@ -1,1372 +0,0 @@-{-# LANGUAGE CPP #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamK--- Copyright   : (c) 2017 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Stream.StreamK-    (-    -- * Setup-    -- | To execute the code examples provided in this module in ghci, please-    -- run the following commands first.-    ---    -- $setup--    -- * The stream type-      Stream-    , StreamK(..)-    , fromStream-    , toStream--    , CrossStreamK-    , unCross-    , mkCross--    -- * Construction Primitives-    , mkStream-    , nil-    , nilM-    , cons-    , (.:)--    -- * Elimination Primitives-    , foldStream-    , foldStreamShared--    -- * Transformation Primitives-    , unShare--    -- * Deconstruction-    , uncons--    -- * Generation-    -- ** Unfolds-    , unfoldr-    , unfoldrM--    -- ** Specialized Generation-    , repeat-    , repeatM-    , replicate-    , replicateM-    , fromIndices-    , fromIndicesM-    , iterate-    , iterateM--    -- ** Conversions-    , fromPure-    , fromEffect-    , fromFoldable-    , fromList--    -- * foldr/build-    , foldrS-    , foldrSM-    , buildS-    , augmentS--    -- * Elimination-    -- ** General Folds-    , foldr-    , foldr1-    , foldrM--    , foldl'-    , foldlM'-    , foldlS-    , foldlx'-    , foldlMx'-    , fold-    , foldBreak-    , foldEither-    , foldConcat-    , parseDBreak-    , parseD-    , parseBreakChunks-    , parseChunks--    -- ** Specialized Folds-    , drain-    , null-    , head-    , tail-    , init-    , elem-    , notElem-    , all-    , any-    , last-    , minimum-    , minimumBy-    , maximum-    , maximumBy-    , findIndices-    , lookup-    , findM-    , find-    , (!!)--    -- ** Map and Fold-    , mapM_--    -- ** Conversions-    , toList-    , hoist--    -- * Transformation-    -- ** By folding (scans)-    , scanl'-    , scanlx'--    -- ** Filtering-    , filter-    , take-    , takeWhile-    , drop-    , dropWhile--    -- ** Mapping-    , map-    , mapM-    , sequence--    -- ** Inserting-    , intersperseM-    , intersperse-    , insertBy--    -- ** Deleting-    , deleteBy--    -- ** Reordering-    , reverse-    , sortBy--    -- ** Map and Filter-    , mapMaybe--    -- ** Zipping-    , zipWith-    , zipWithM--    -- ** Merging-    , mergeBy-    , mergeByM--    -- ** Nesting-    , crossApplyWith-    , crossApply-    , crossApplySnd-    , crossApplyFst-    , crossWith--    , concatMapWith-    , concatMap-    , concatEffect-    , bindWith-    , concatIterateWith-    , concatIterateLeftsWith-    , concatIterateScanWith--    , mergeMapWith-    , mergeIterateWith--    -- ** Transformation comprehensions-    , the--    -- * Semigroup Style Composition-    , append-    , interleave--    -- * Utilities-    , consM-    , mfix-    )-where--#include "ArrayMacros.h"-#include "inline.hs"-#include "assert.hs"--import Control.Monad (void, join)-import Data.Proxy (Proxy(..))-import GHC.Types (SPEC(..))-import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Fold.Type (Fold(..))-import Streamly.Internal.Data.Producer.Type (Producer(..))-import Streamly.Internal.Data.SVar.Type (adaptState, defState)-import Streamly.Internal.Data.Unboxed (sizeOf, Unbox)-import Streamly.Internal.Data.Parser.ParserK.Type (ParserK)--import qualified Streamly.Internal.Data.Array.Type as Array-import qualified Streamly.Internal.Data.Fold.Type as FL-import qualified Streamly.Internal.Data.Parser as Parser-import qualified Streamly.Internal.Data.Parser.ParserD.Type as PR-import qualified Streamly.Internal.Data.Parser.ParserK.Type as ParserK-import qualified Streamly.Internal.Data.Stream.StreamD as Stream-import qualified Prelude--import Prelude-       hiding (foldl, foldr, last, map, mapM, mapM_, repeat, sequence,-               take, filter, all, any, takeWhile, drop, dropWhile, minimum,-               maximum, elem, notElem, null, head, tail, init, zipWith, lookup,-               foldr1, (!!), replicate, reverse, concatMap, iterate, splitAt)--import Streamly.Internal.Data.Stream.StreamK.Type-import Streamly.Internal.Data.Parser.ParserD (ParseError(..))--#include "DocTestDataStreamK.hs"--{-# INLINE fromStream #-}-fromStream :: Monad m => Stream.Stream m a -> StreamK m a-fromStream = Stream.toStreamK--{-# INLINE toStream #-}-toStream :: Applicative m => StreamK m a -> Stream.Stream m a-toStream = Stream.fromStreamK------------------------------------------------------------------------------------ Generation----------------------------------------------------------------------------------{---- Generalization of concurrent streams/SVar via unfoldr.------ Unfold a value into monadic actions and then run the resulting monadic--- actions to generate a stream. Since the step of generating the monadic--- action and running them are decoupled we can run the monadic actions--- cooncurrently. For example, the seed could be a list of monadic actions or a--- pure stream of monadic actions.------ We can have different flavors of this depending on the stream type t. The--- concurrent version could be async or ahead etc. Depending on how we queue--- back the feedback portion b, it could be DFS or BFS style.----unfoldrA :: (b -> Maybe (m a, b)) -> b -> StreamK m a-unfoldrA = undefined--}------------------------------------------------------------------------------------ Special generation----------------------------------------------------------------------------------repeatM :: Monad m => m a -> StreamK m a-repeatM = repeatMWith consM--{-# INLINE replicateM #-}-replicateM :: Monad m => Int -> m a -> StreamK m a-replicateM = replicateMWith consM-{-# INLINE replicate #-}-replicate :: Int -> a -> StreamK m a-replicate n a = go n-    where-    go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)--{-# INLINE fromIndicesM #-}-fromIndicesM :: Monad m => (Int -> m a) -> StreamK m a-fromIndicesM = fromIndicesMWith consM-{-# INLINE fromIndices #-}-fromIndices :: (Int -> a) -> StreamK m a-fromIndices gen = go 0-  where-    go n = gen n `cons` go (n + 1)--{-# INLINE iterate #-}-iterate :: (a -> a) -> a -> StreamK m a-iterate step = go-    where-        go !s = cons s (go (step s))--{-# INLINE iterateM #-}-iterateM :: Monad m => (a -> m a) -> m a -> StreamK m a-iterateM = iterateMWith consM------------------------------------------------------------------------------------ Conversions----------------------------------------------------------------------------------{-# INLINE fromList #-}-fromList :: [a] -> StreamK m a-fromList = fromFoldable------------------------------------------------------------------------------------ Elimination by Folding----------------------------------------------------------------------------------{-# INLINE foldr1 #-}-foldr1 :: Monad m => (a -> a -> a) -> StreamK m a -> m (Maybe a)-foldr1 step m = do-    r <- uncons m-    case r of-        Nothing -> return Nothing-        Just (h, t) -> fmap Just (go h t)-    where-    go p m1 =-        let stp = return p-            single a = return $ step a p-            yieldk a r = fmap (step p) (go a r)-         in foldStream defState yieldk single stp m1---- XXX replace the recursive "go" with explicit continuations.--- | Like 'foldx', but with a monadic step function.-{-# INLINABLE foldlMx' #-}-foldlMx' :: Monad m-    => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b-foldlMx' step begin done = go begin-    where-    go !acc m1 =-        let stop = acc >>= done-            single a = acc >>= \b -> step b a >>= done-            yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r-         in foldStream defState yieldk single stop m1---- | Fold a stream using the supplied left 'Fold' and reducing the resulting--- expression strictly at each step. The behavior is similar to 'foldl''. A--- 'Fold' can terminate early without consuming the full stream. See the--- documentation of individual 'Fold's for termination behavior.------ Definitions:------ >>> fold f = fmap fst . StreamK.foldBreak f--- >>> fold f = StreamK.parseD (Parser.fromFold f)------ Example:------ >>> StreamK.fold Fold.sum $ StreamK.fromStream $ Stream.enumerateFromTo 1 100--- 5050----{-# INLINABLE fold #-}-fold :: Monad m => FL.Fold m a b -> StreamK m a -> m b-fold (FL.Fold step begin done) m = do-    res <- begin-    case res of-        FL.Partial fs -> go fs m-        FL.Done fb -> return fb--    where-    go !acc m1 =-        let stop = done acc-            single a = step acc a-              >>= \case-                        FL.Partial s -> done s-                        FL.Done b1 -> return b1-            yieldk a r = step acc a-              >>= \case-                        FL.Partial s -> go s r-                        FL.Done b1 -> return b1-         in foldStream defState yieldk single stop m1---- | Fold resulting in either breaking the stream or continuation of the fold.--- Instead of supplying the input stream in one go we can run the fold multiple--- times, each time supplying the next segment of the input stream. If the fold--- has not yet finished it returns a fold that can be run again otherwise it--- returns the fold result and the residual stream.------ /Internal/-{-# INLINE foldEither #-}-foldEither :: Monad m =>-    Fold m a b -> StreamK m a -> m (Either (Fold m a b) (b, StreamK m a))-foldEither (FL.Fold step begin done) m = do-    res <- begin-    case res of-        FL.Partial fs -> go fs m-        FL.Done fb -> return $ Right (fb, m)--    where--    go !acc m1 =-        let stop = return $ Left (Fold step (return $ FL.Partial acc) done)-            single a =-                step acc a-                  >>= \case-                    FL.Partial s ->-                        return $ Left (Fold step (return $ FL.Partial s) done)-                    FL.Done b1 -> return $ Right (b1, nil)-            yieldk a r =-                step acc a-                  >>= \case-                    FL.Partial s -> go s r-                    FL.Done b1 -> return $ Right (b1, r)-         in foldStream defState yieldk single stop m1---- | Like 'fold' but also returns the remaining stream. The resulting stream--- would be 'StreamK.nil' if the stream finished before the fold.----{-# INLINE foldBreak #-}-foldBreak :: Monad m => Fold m a b -> StreamK m a -> m (b, StreamK m a)-foldBreak fld strm = do-    r <- foldEither fld strm-    case r of-        Right res -> return res-        Left (Fold _ initial extract) -> do-            res <- initial-            case res of-                FL.Done _ -> error "foldBreak: unreachable state"-                FL.Partial s -> do-                    b <- extract s-                    return (b, nil)---- XXX Array folds can be implemented using this.--- foldContainers? Specialized to foldArrays.---- | Generate streams from individual elements of a stream and fold the--- concatenation of those streams using the supplied fold. Return the result of--- the fold and residual stream.------ For example, this can be used to efficiently fold an Array Word8 stream--- using Word8 folds.------ /Internal/-{-# INLINE foldConcat #-}-foldConcat :: Monad m =>-    Producer m a b -> Fold m b c -> StreamK m a -> m (c, StreamK m a)-foldConcat-    (Producer pstep pinject pextract)-    (Fold fstep begin done)-    stream = do--    res <- begin-    case res of-        FL.Partial fs -> go fs stream-        FL.Done fb -> return (fb, stream)--    where--    go !acc m1 = do-        let stop = do-                r <- done acc-                return (r, nil)-            single a = do-                st <- pinject a-                res <- go1 SPEC acc st-                case res of-                    Left fs -> do-                        r <- done fs-                        return (r, nil)-                    Right (b, s) -> do-                        x <- pextract s-                        return (b, fromPure x)-            yieldk a r = do-                st <- pinject a-                res <- go1 SPEC acc st-                case res of-                    Left fs -> go fs r-                    Right (b, s) -> do-                        x <- pextract s-                        return (b, x `cons` r)-         in foldStream defState yieldk single stop m1--    {-# INLINE go1 #-}-    go1 !_ !fs st = do-        r <- pstep st-        case r of-            Stream.Yield x s -> do-                res <- fstep fs x-                case res of-                    FL.Done b -> return $ Right (b, s)-                    FL.Partial fs1 -> go1 SPEC fs1 s-            Stream.Skip s -> go1 SPEC fs s-            Stream.Stop -> return $ Left fs---- | Like 'foldl'' but with a monadic step function.-{-# INLINE foldlM' #-}-foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b-foldlM' step begin = foldlMx' step begin return----------------------------------------------------------------------------------- Specialized folds---------------------------------------------------------------------------------{-# INLINE head #-}-head :: Monad m => StreamK m a -> m (Maybe a)--- head = foldrM (\x _ -> return $ Just x) (return Nothing)-head m =-    let stop      = return Nothing-        single a  = return (Just a)-        yieldk a _ = return (Just a)-    in foldStream defState yieldk single stop m--{-# INLINE elem #-}-elem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool-elem e = go-    where-    go m1 =-        let stop      = return False-            single a  = return (a == e)-            yieldk a r = if a == e then return True else go r-        in foldStream defState yieldk single stop m1--{-# INLINE notElem #-}-notElem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool-notElem e = go-    where-    go m1 =-        let stop      = return True-            single a  = return (a /= e)-            yieldk a r = if a == e then return False else go r-        in foldStream defState yieldk single stop m1--{-# INLINABLE all #-}-all :: Monad m => (a -> Bool) -> StreamK m a -> m Bool-all p = go-    where-    go m1 =-        let single a   | p a       = return True-                       | otherwise = return False-            yieldk a r | p a       = go r-                       | otherwise = return False-         in foldStream defState yieldk single (return True) m1--{-# INLINABLE any #-}-any :: Monad m => (a -> Bool) -> StreamK m a -> m Bool-any p = go-    where-    go m1 =-        let single a   | p a       = return True-                       | otherwise = return False-            yieldk a r | p a       = return True-                       | otherwise = go r-         in foldStream defState yieldk single (return False) m1---- | Extract the last element of the stream, if any.-{-# INLINE last #-}-last :: Monad m => StreamK m a -> m (Maybe a)-last = foldlx' (\_ y -> Just y) Nothing id--{-# INLINE minimum #-}-minimum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)-minimum = go Nothing-    where-    go Nothing m1 =-        let stop      = return Nothing-            single a  = return (Just a)-            yieldk a r = go (Just a) r-        in foldStream defState yieldk single stop m1--    go (Just res) m1 =-        let stop      = return (Just res)-            single a  =-                if res <= a-                then return (Just res)-                else return (Just a)-            yieldk a r =-                if res <= a-                then go (Just res) r-                else go (Just a) r-        in foldStream defState yieldk single stop m1--{-# INLINE minimumBy #-}-minimumBy-    :: (Monad m)-    => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)-minimumBy cmp = go Nothing-    where-    go Nothing m1 =-        let stop      = return Nothing-            single a  = return (Just a)-            yieldk a r = go (Just a) r-        in foldStream defState yieldk single stop m1--    go (Just res) m1 =-        let stop      = return (Just res)-            single a  = case cmp res a of-                GT -> return (Just a)-                _  -> return (Just res)-            yieldk a r = case cmp res a of-                GT -> go (Just a) r-                _  -> go (Just res) r-        in foldStream defState yieldk single stop m1--{-# INLINE maximum #-}-maximum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)-maximum = go Nothing-    where-    go Nothing m1 =-        let stop      = return Nothing-            single a  = return (Just a)-            yieldk a r = go (Just a) r-        in foldStream defState yieldk single stop m1--    go (Just res) m1 =-        let stop      = return (Just res)-            single a  =-                if res <= a-                then return (Just a)-                else return (Just res)-            yieldk a r =-                if res <= a-                then go (Just a) r-                else go (Just res) r-        in foldStream defState yieldk single stop m1--{-# INLINE maximumBy #-}-maximumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)-maximumBy cmp = go Nothing-    where-    go Nothing m1 =-        let stop      = return Nothing-            single a  = return (Just a)-            yieldk a r = go (Just a) r-        in foldStream defState yieldk single stop m1--    go (Just res) m1 =-        let stop      = return (Just res)-            single a  = case cmp res a of-                GT -> return (Just res)-                _  -> return (Just a)-            yieldk a r = case cmp res a of-                GT -> go (Just res) r-                _  -> go (Just a) r-        in foldStream defState yieldk single stop m1--{-# INLINE (!!) #-}-(!!) :: Monad m => StreamK m a -> Int -> m (Maybe a)-m !! i = go i m-    where-    go n m1 =-      let single a | n == 0 = return $ Just a-                   | otherwise = return Nothing-          yieldk a x | n < 0 = return Nothing-                     | n == 0 = return $ Just a-                     | otherwise = go (n - 1) x-      in foldStream defState yieldk single (return Nothing) m1--{-# INLINE lookup #-}-lookup :: (Monad m, Eq a) => a -> StreamK m (a, b) -> m (Maybe b)-lookup e = go-    where-    go m1 =-        let single (a, b) | a == e = return $ Just b-                          | otherwise = return Nothing-            yieldk (a, b) x | a == e = return $ Just b-                            | otherwise = go x-        in foldStream defState yieldk single (return Nothing) m1--{-# INLINE findM #-}-findM :: Monad m => (a -> m Bool) -> StreamK m a -> m (Maybe a)-findM p = go-    where-    go m1 =-        let single a = do-                b <- p a-                if b then return $ Just a else return Nothing-            yieldk a x = do-                b <- p a-                if b then return $ Just a else go x-        in foldStream defState yieldk single (return Nothing) m1--{-# INLINE find #-}-find :: Monad m => (a -> Bool) -> StreamK m a -> m (Maybe a)-find p = findM (return . p)--{-# INLINE findIndices #-}-findIndices :: (a -> Bool) -> StreamK m a -> StreamK m Int-findIndices p = go 0-    where-    go offset m1 = mkStream $ \st yld sng stp ->-        let single a | p a = sng offset-                     | otherwise = stp-            yieldk a x | p a = yld offset $ go (offset + 1) x-                       | otherwise = foldStream (adaptState st) yld sng stp $-                            go (offset + 1) x-        in foldStream (adaptState st) yieldk single stp m1----------------------------------------------------------------------------------- Map and Fold----------------------------------------------------------------------------------- | Apply a monadic action to each element of the stream and discard the--- output of the action.-{-# INLINE mapM_ #-}-mapM_ :: Monad m => (a -> m b) -> StreamK m a -> m ()-mapM_ f = go-    where-    go m1 =-        let stop = return ()-            single a = void (f a)-            yieldk a r = f a >> go r-         in foldStream defState yieldk single stop m1--{-# INLINE mapM #-}-mapM :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b-mapM = mapMWith consM----------------------------------------------------------------------------------- Converting folds---------------------------------------------------------------------------------{-# INLINABLE toList #-}-toList :: Monad m => StreamK m a -> m [a]-toList = foldr (:) []---- Based on suggestions by David Feuer and Pranay Sashank-{-# INLINE hoist #-}-hoist :: (Monad m, Monad n)-    => (forall x. m x -> n x) -> StreamK m a -> StreamK n a-hoist f str =-    mkStream $ \st yld sng stp ->-            let single = return . sng-                yieldk a s = return $ yld a (hoist f s)-                stop = return stp-                state = adaptState st-             in join . f $ foldStreamShared state yieldk single stop str------------------------------------------------------------------------------------ Transformation by folding (Scans)----------------------------------------------------------------------------------{-# INLINE scanlx' #-}-scanlx' :: (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> StreamK m b-scanlx' step begin done m =-    cons (done begin) $ go m begin-    where-    go m1 !acc = mkStream $ \st yld sng stp ->-        let single a = sng (done $ step acc a)-            yieldk a r =-                let s = step acc a-                in yld (done s) (go r s)-        in foldStream (adaptState st) yieldk single stp m1--{-# INLINE scanl' #-}-scanl' :: (b -> a -> b) -> b -> StreamK m a -> StreamK m b-scanl' step begin = scanlx' step begin id------------------------------------------------------------------------------------ Filtering----------------------------------------------------------------------------------{-# INLINE filter #-}-filter :: (a -> Bool) -> StreamK m a -> StreamK m a-filter p = go-    where-    go m1 = mkStream $ \st yld sng stp ->-        let single a   | p a       = sng a-                       | otherwise = stp-            yieldk a r | p a       = yld a (go r)-                       | otherwise = foldStream st yieldk single stp r-         in foldStream st yieldk single stp m1--{-# INLINE take #-}-take :: Int -> StreamK m a -> StreamK m a-take = go-    where-    go n1 m1 = mkStream $ \st yld sng stp ->-        let yieldk a r = yld a (go (n1 - 1) r)-        in if n1 <= 0-           then stp-           else foldStream st yieldk sng stp m1--{-# INLINE takeWhile #-}-takeWhile :: (a -> Bool) -> StreamK m a -> StreamK m a-takeWhile p = go-    where-    go m1 = mkStream $ \st yld sng stp ->-        let single a   | p a       = sng a-                       | otherwise = stp-            yieldk a r | p a       = yld a (go r)-                       | otherwise = stp-         in foldStream st yieldk single stp m1--{-# INLINE drop #-}-drop :: Int -> StreamK m a -> StreamK m a-drop n m = unShare (go n m)-    where-    go n1 m1 = mkStream $ \st yld sng stp ->-        let single _ = stp-            yieldk _ r = foldStreamShared st yld sng stp $ go (n1 - 1) r-        -- Somehow "<=" check performs better than a ">"-        in if n1 <= 0-           then foldStreamShared st yld sng stp m1-           else foldStreamShared st yieldk single stp m1--{-# INLINE dropWhile #-}-dropWhile :: (a -> Bool) -> StreamK m a -> StreamK m a-dropWhile p = go-    where-    go m1 = mkStream $ \st yld sng stp ->-        let single a   | p a       = stp-                       | otherwise = sng a-            yieldk a r | p a = foldStream st yieldk single stp r-                       | otherwise = yld a r-         in foldStream st yieldk single stp m1------------------------------------------------------------------------------------ Mapping------------------------------------------------------------------------------------ Be careful when modifying this, this uses a consM (|:) deliberately to allow--- other stream types to overload it.-{-# INLINE sequence #-}-sequence :: Monad m => StreamK m (m a) -> StreamK m a-sequence = go-    where-    go m1 = mkStream $ \st yld sng stp ->-        let single ma = ma >>= sng-            yieldk ma r = foldStreamShared st yld sng stp $ ma `consM` go r-         in foldStream (adaptState st) yieldk single stp m1------------------------------------------------------------------------------------ Inserting----------------------------------------------------------------------------------{-# INLINE intersperseM #-}-intersperseM :: Monad m => m a -> StreamK m a -> StreamK m a-intersperseM a = prependingStart-    where-    prependingStart m1 = mkStream $ \st yld sng stp ->-        let yieldk i x =-                foldStreamShared st yld sng stp $ return i `consM` go x-         in foldStream st yieldk sng stp m1-    go m2 = mkStream $ \st yld sng stp ->-        let single i = foldStreamShared st yld sng stp $ a `consM` fromPure i-            yieldk i x =-                foldStreamShared-                    st yld sng stp $ a `consM` return i `consM` go x-         in foldStream st yieldk single stp m2--{-# INLINE intersperse #-}-intersperse :: Monad m => a -> StreamK m a -> StreamK m a-intersperse a = intersperseM (return a)--{-# INLINE insertBy #-}-insertBy :: (a -> a -> Ordering) -> a -> StreamK m a -> StreamK m a-insertBy cmp x = go-  where-    go m1 = mkStream $ \st yld _ _ ->-        let single a = case cmp x a of-                GT -> yld a (fromPure x)-                _  -> yld x (fromPure a)-            stop = yld x nil-            yieldk a r = case cmp x a of-                GT -> yld a (go r)-                _  -> yld x (a `cons` r)-         in foldStream st yieldk single stop m1----------------------------------------------------------------------------------- Deleting---------------------------------------------------------------------------------{-# INLINE deleteBy #-}-deleteBy :: (a -> a -> Bool) -> a -> StreamK m a -> StreamK m a-deleteBy eq x = go-  where-    go m1 = mkStream $ \st yld sng stp ->-        let single a = if eq x a then stp else sng a-            yieldk a r = if eq x a-              then foldStream st yld sng stp r-              else yld a (go r)-         in foldStream st yieldk single stp m1------------------------------------------------------------------------------------ Map and Filter----------------------------------------------------------------------------------{-# INLINE mapMaybe #-}-mapMaybe :: (a -> Maybe b) -> StreamK m a -> StreamK m b-mapMaybe f = go-  where-    go m1 = mkStream $ \st yld sng stp ->-        let single a = maybe stp sng (f a)-            yieldk a r = case f a of-                Just b  -> yld b $ go r-                Nothing -> foldStream (adaptState st) yieldk single stp r-        in foldStream (adaptState st) yieldk single stp m1----------------------------------------------------------------------------------- Serial Zipping----------------------------------------------------------------------------------- | Zip two streams serially using a pure zipping function.----{-# INLINE zipWith #-}-zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c-zipWith f = zipWithM (\a b -> return (f a b))---- | Zip two streams serially using a monadic zipping function.----{-# INLINE zipWithM #-}-zipWithM :: Monad m =>-    (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c-zipWithM f = go--    where--    go mx my = mkStream $ \st yld sng stp -> do-        let merge a ra =-                let single2 b   = f a b >>= sng-                    yield2 b rb = f a b >>= \x -> yld x (go ra rb)-                 in foldStream (adaptState st) yield2 single2 stp my-        let single1 a = merge a nil-            yield1 = merge-        foldStream (adaptState st) yield1 single1 stp mx----------------------------------------------------------------------------------- Merging---------------------------------------------------------------------------------{-# INLINE mergeByM #-}-mergeByM :: Monad m =>-    (a -> a -> m Ordering) -> StreamK m a -> StreamK m a -> StreamK m a-mergeByM cmp = go--    where--    go mx my = mkStream $ \st yld sng stp -> do-        let stop = foldStream st yld sng stp my-            single x = foldStream st yld sng stp (goX0 x my)-            yield x rx = foldStream st yld sng stp (goX x rx my)-        foldStream st yield single stop mx--    goX0 x my = mkStream $ \st yld sng _ -> do-        let stop = sng x-            single y = do-                r <- cmp x y-                case r of-                    GT -> yld y (fromPure x)-                    _  -> yld x (fromPure y)-            yield y ry = do-                r <- cmp x y-                case r of-                    GT -> yld y (goX0 x ry)-                    _  -> yld x (y `cons` ry)-         in foldStream st yield single stop my--    goX x mx my = mkStream $ \st yld _ _ -> do-        let stop = yld x mx-            single y = do-                r <- cmp x y-                case r of-                    GT -> yld y (x `cons` mx)-                    _  -> yld x (goY0 mx y)-            yield y ry = do-                r <- cmp x y-                case r of-                    GT -> yld y (goX x mx ry)-                    _  -> yld x (goY mx y ry)-         in foldStream st yield single stop my--    goY0 mx y = mkStream $ \st yld sng _ -> do-        let stop = sng y-            single x = do-                r <- cmp x y-                case r of-                    GT -> yld y (fromPure x)-                    _  -> yld x (fromPure y)-            yield x rx = do-                r <- cmp x y-                case r of-                    GT -> yld y (x `cons` rx)-                    _  -> yld x (goY0 rx y)-         in foldStream st yield single stop mx--    goY mx y my = mkStream $ \st yld _ _ -> do-        let stop = yld y my-            single x = do-                r <- cmp x y-                case r of-                    GT -> yld y (goX0 x my)-                    _  -> yld x (y `cons` my)-            yield x rx = do-                r <- cmp x y-                case r of-                    GT -> yld y (goX x rx my)-                    _  -> yld x (goY rx y my)-         in foldStream st yield single stop mx--{-# INLINE mergeBy #-}-mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a--- XXX GHC: This has slightly worse performance than replacing "r <- cmp x y"--- with "let r = cmp x y" in the monadic version. The definition below is--- exactly the same as mergeByM except this change.--- mergeBy cmp = mergeByM (\a b -> return $ cmp a b)-mergeBy cmp = go--    where--    go mx my = mkStream $ \st yld sng stp -> do-        let stop = foldStream st yld sng stp my-            single x = foldStream st yld sng stp (goX0 x my)-            yield x rx = foldStream st yld sng stp (goX x rx my)-        foldStream st yield single stop mx--    goX0 x my = mkStream $ \st yld sng _ -> do-        let stop = sng x-            single y = do-                case cmp x y of-                    GT -> yld y (fromPure x)-                    _  -> yld x (fromPure y)-            yield y ry = do-                case cmp x y of-                    GT -> yld y (goX0 x ry)-                    _  -> yld x (y `cons` ry)-         in foldStream st yield single stop my--    goX x mx my = mkStream $ \st yld _ _ -> do-        let stop = yld x mx-            single y = do-                case cmp x y of-                    GT -> yld y (x `cons` mx)-                    _  -> yld x (goY0 mx y)-            yield y ry = do-                case cmp x y of-                    GT -> yld y (goX x mx ry)-                    _  -> yld x (goY mx y ry)-         in foldStream st yield single stop my--    goY0 mx y = mkStream $ \st yld sng _ -> do-        let stop = sng y-            single x = do-                case cmp x y of-                    GT -> yld y (fromPure x)-                    _  -> yld x (fromPure y)-            yield x rx = do-                case cmp x y of-                    GT -> yld y (x `cons` rx)-                    _  -> yld x (goY0 rx y)-         in foldStream st yield single stop mx--    goY mx y my = mkStream $ \st yld _ _ -> do-        let stop = yld y my-            single x = do-                case cmp x y of-                    GT -> yld y (goX0 x my)-                    _  -> yld x (y `cons` my)-            yield x rx = do-                case cmp x y of-                    GT -> yld y (goX x rx my)-                    _  -> yld x (goY rx y my)-         in foldStream st yield single stop mx----------------------------------------------------------------------------------- Transformation comprehensions---------------------------------------------------------------------------------{-# INLINE the #-}-the :: (Eq a, Monad m) => StreamK m a -> m (Maybe a)-the m = do-    r <- uncons m-    case r of-        Nothing -> return Nothing-        Just (h, t) -> go h t-    where-    go h m1 =-        let single a   | h == a    = return $ Just h-                       | otherwise = return Nothing-            yieldk a r | h == a    = go h r-                       | otherwise = return Nothing-         in foldStream defState yieldk single (return $ Just h) m1----------------------------------------------------------------------------------- Alternative & MonadPlus---------------------------------------------------------------------------------_alt :: StreamK m a -> StreamK m a -> StreamK m a-_alt m1 m2 = mkStream $ \st yld sng stp ->-    let stop  = foldStream st yld sng stp m2-    in foldStream st yld sng stop m1----------------------------------------------------------------------------------- MonadError---------------------------------------------------------------------------------{---- XXX handle and test cross thread state transfer-withCatchError-    :: MonadError e m-    => StreamK m a -> (e -> StreamK m a) -> StreamK m a-withCatchError m h =-    mkStream $ \_ stp sng yld ->-        let run x = unStream x Nothing stp sng yieldk-            handle r = r `catchError` \e -> run $ h e-            yieldk a r = yld a (withCatchError r h)-        in handle $ run m--}------------------------------------------------------------------------------------ Parsing------------------------------------------------------------------------------------ Inlined definition.-{-# INLINE splitAt #-}-splitAt :: Int -> [a] -> ([a],[a])-splitAt n ls-  | n <= 0 = ([], ls)-  | otherwise          = splitAt' n ls-    where-        splitAt' :: Int -> [a] -> ([a], [a])-        splitAt' _  []     = ([], [])-        splitAt' 1  (x:xs) = ([x], xs)-        splitAt' m  (x:xs) = (x:xs', xs'')-          where-            (xs', xs'') = splitAt' (m - 1) xs---- | Run a 'Parser' over a stream and return rest of the Stream.-{-# INLINE_NORMAL parseDBreak #-}-parseDBreak-    :: Monad m-    => PR.Parser a m b-    -> StreamK m a-    -> m (Either ParseError b, StreamK m a)-parseDBreak (PR.Parser pstep initial extract) stream = do-    res <- initial-    case res of-        PR.IPartial s -> goStream stream [] s-        PR.IDone b -> return (Right b, stream)-        PR.IError err -> return (Left (ParseError err), stream)--    where--    -- "buf" contains last few items in the stream that we may have to-    -- backtrack to.-    ---    -- XXX currently we are using a dumb list based approach for backtracking-    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.-    -- That will allow us more efficient random back and forth movement.-    goStream st buf !pst =-        let stop = do-                r <- extract pst-                case r of-                    PR.Error err -> return (Left (ParseError err), nil)-                    PR.Done n b -> do-                        assertM(n <= length buf)-                        let src0 = Prelude.take n buf-                            src  = Prelude.reverse src0-                        return (Right b, fromList src)-                    PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"-                    PR.Continue 0 s -> goStream nil buf s-                    PR.Continue n s -> do-                        assertM(n <= length buf)-                        let (src0, buf1) = splitAt n buf-                            src = Prelude.reverse src0-                        goBuf nil buf1 src s-            single x = yieldk x nil-            yieldk x r = do-                res <- pstep pst x-                case res of-                    PR.Partial 0 s -> goStream r [] s-                    PR.Partial n s -> do-                        assertM(n <= length (x:buf))-                        let src0 = Prelude.take n (x:buf)-                            src  = Prelude.reverse src0-                        goBuf r [] src s-                    PR.Continue 0 s -> goStream r (x:buf) s-                    PR.Continue n s -> do-                        assertM(n <= length (x:buf))-                        let (src0, buf1) = splitAt n (x:buf)-                            src = Prelude.reverse src0-                        goBuf r buf1 src s-                    PR.Done 0 b -> return (Right b, r)-                    PR.Done n b -> do-                        assertM(n <= length (x:buf))-                        let src0 = Prelude.take n (x:buf)-                            src  = Prelude.reverse src0-                        return (Right b, append (fromList src) r)-                    PR.Error err -> return (Left (ParseError err), r)-         in foldStream defState yieldk single stop st--    goBuf st buf [] !pst = goStream st buf pst-    goBuf st buf (x:xs) !pst = do-        pRes <- pstep pst x-        case pRes of-            PR.Partial 0 s -> goBuf st [] xs s-            PR.Partial n s -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                goBuf st [] src s-            PR.Continue 0 s -> goBuf st (x:buf) xs s-            PR.Continue n s -> do-                assert (n <= length (x:buf)) (return ())-                let (src0, buf1) = splitAt n (x:buf)-                    src  = Prelude.reverse src0 ++ xs-                goBuf st buf1 src s-            PR.Done n b -> do-                assert (n <= length (x:buf)) (return ())-                let src0 = Prelude.take n (x:buf)-                    src  = Prelude.reverse src0-                return (Right b, append (fromList src) st)-            PR.Error err -> return (Left (ParseError err), nil)---- Using ParserD or ParserK on StreamK may not make much difference. We should--- perhaps use only chunked parsing on StreamK. We can always convert a stream--- to chunks before parsing. Or just have a ParserK element parser for StreamK--- and convert ParserD to ParserK for element parsing using StreamK.-{-# INLINE parseD #-}-parseD :: Monad m =>-    Parser.Parser a m b -> StreamK m a -> m (Either ParseError b)-parseD f = fmap fst . parseDBreak f------------------------------------------------------------------------------------ Chunked parsing using ParserK------------------------------------------------------------------------------------ The backracking buffer consists of arrays in the most-recent-first order. We--- want to take a total of n array elements from this buffer. Note: when we--- have to take an array partially, we must take the last part of the array.-{-# INLINE backTrack #-}-backTrack :: forall m a. Unbox a =>-       Int-    -> [Array a]-    -> StreamK m (Array a)-    -> (StreamK m (Array a), [Array a])-backTrack = go--    where--    go _ [] stream = (stream, [])-    go n xs stream | n <= 0 = (stream, xs)-    go n (x:xs) stream =-        let len = Array.length x-        in if n > len-           then go (n - len) xs (cons x stream)-           else if n == len-           then (cons x stream, xs)-           else let !(Array contents start end) = x-                    !start1 = end - (n * SIZE_OF(a))-                    arr1 = Array contents start1 end-                    arr2 = Array contents start start1-                 in (cons arr1 stream, arr2:xs)---- | A continuation to extract the result when a CPS parser is done.-{-# INLINE parserDone #-}-parserDone :: Applicative m =>-    ParserK.ParseResult b -> Int -> ParserK.Input a -> m (ParserK.Step a m b)-parserDone (ParserK.Success n b) _ _ = pure $ ParserK.Done n b-parserDone (ParserK.Failure n e) _ _ = pure $ ParserK.Error n e---- XXX parseDBreakChunks may be faster than converting parserD to parserK and--- using parseBreakChunks. We can also use parseBreak as an alternative to the--- monad instance of ParserD.---- | Run a 'ParserK' over a chunked 'StreamK' and return the rest of the Stream.-{-# INLINE_NORMAL parseBreakChunks #-}-parseBreakChunks-    :: (Monad m, Unbox a)-    => ParserK a m b-    -> StreamK m (Array a)-    -> m (Either ParseError b, StreamK m (Array a))-parseBreakChunks parser input = do-    let parserk = ParserK.runParser parser parserDone 0 0-     in go [] parserk input--    where--    {-# INLINE goStop #-}-    goStop backBuf parserk = do-        pRes <- parserk ParserK.None-        case pRes of-            -- If we stop in an alternative, it will try calling the next-            -- parser, the next parser may call initial returning Partial and-            -- then immediately we have to call extract on it.-            ParserK.Partial 0 cont1 ->-                 go [] cont1 nil-            ParserK.Partial n cont1 -> do-                let n1 = negate n-                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))-                let (s1, backBuf1) = backTrack n1 backBuf nil-                 in go backBuf1 cont1 s1-            ParserK.Continue 0 cont1 ->-                go backBuf cont1 nil-            ParserK.Continue n cont1 -> do-                let n1 = negate n-                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))-                let (s1, backBuf1) = backTrack n1 backBuf nil-                 in go backBuf1 cont1 s1-            ParserK.Done 0 b ->-                return (Right b, nil)-            ParserK.Done n b -> do-                let n1 = negate n-                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))-                let (s1, _) = backTrack n1 backBuf nil-                 in return (Right b, s1)-            ParserK.Error _ err -> return (Left (ParseError err), nil)--    seekErr n len =-        error $ "parseBreak: Partial: forward seek not implemented n = "-            ++ show n ++ " len = " ++ show len--    yieldk backBuf parserk arr stream = do-        pRes <- parserk (ParserK.Chunk arr)-        let len = Array.length arr-        case pRes of-            ParserK.Partial n cont1 ->-                case compare n len of-                    EQ -> go [] cont1 stream-                    LT -> do-                        if n >= 0-                        then yieldk [] cont1 arr stream-                        else do-                            let n1 = negate n-                                bufLen = sum (Prelude.map Array.length backBuf)-                                s = cons arr stream-                            assertM(n1 >= 0 && n1 <= bufLen)-                            let (s1, _) = backTrack n1 backBuf s-                            go [] cont1 s1-                    GT -> seekErr n len-            ParserK.Continue n cont1 ->-                case compare n len of-                    EQ -> go (arr:backBuf) cont1 stream-                    LT -> do-                        if n >= 0-                        then yieldk backBuf cont1 arr stream-                        else do-                            let n1 = negate n-                                bufLen = sum (Prelude.map Array.length backBuf)-                                s = cons arr stream-                            assertM(n1 >= 0 && n1 <= bufLen)-                            let (s1, backBuf1) = backTrack n1 backBuf s-                            go backBuf1 cont1 s1-                    GT -> seekErr n len-            ParserK.Done n b -> do-                let n1 = len - n-                assertM(n1 <= sum (Prelude.map Array.length (arr:backBuf)))-                let (s1, _) = backTrack n1 (arr:backBuf) stream-                 in return (Right b, s1)-            ParserK.Error _ err -> return (Left (ParseError err), nil)--    go backBuf parserk stream = do-        let stop = goStop backBuf parserk-            single a = yieldk backBuf parserk a nil-         in foldStream-                defState (yieldk backBuf parserk) single stop stream--{-# INLINE parseChunks #-}-parseChunks :: (Monad m, Unbox a) =>-    ParserK a m b -> StreamK m (Array a) -> m (Either ParseError b)-parseChunks f = fmap fst . parseBreakChunks f------------------------------------------------------------------------------------ Sorting------------------------------------------------------------------------------------ | Sort the input stream using a supplied comparison function.------ Sorting can be achieved by simply:------ >>> sortBy cmp = StreamK.mergeMapWith (StreamK.mergeBy cmp) StreamK.fromPure------ However, this combinator uses a parser to first split the input stream into--- down and up sorted segments and then merges them to optimize sorting when--- pre-sorted sequences exist in the input stream.------ /O(n) space/----{-# INLINE sortBy #-}-sortBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> StreamK m a--- sortBy f = Stream.concatPairsWith (Stream.mergeBy f) Stream.fromPure-sortBy cmp =-    let p =-            Parser.groupByRollingEither-                (\x -> (< GT) . cmp x)-                FL.toStreamKRev-                FL.toStreamK-     in   mergeMapWith (mergeBy cmp) id-        . Stream.toStreamK-        . Stream.catRights -- its a non-failing backtracking parser-        . Stream.parseMany (fmap (either id id) p)-        . Stream.fromStreamK
− src/Streamly/Internal/Data/Stream/StreamK/Alt.hs
@@ -1,244 +0,0 @@--- |--- Module      : Streamly.StreamDK.Type--- Copyright   : (c) 2019 Composewell Technologies--- License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ A CPS style stream using a constructor based representation instead of a--- function based representation.------ Streamly internally uses two fundamental stream representations, (1) streams--- with an open or arbitrary control flow (we call it StreamK), (2) streams--- with a structured or closed loop control flow (we call it StreamD). The--- higher level stream types can use any of these representations under the--- hood and can interconvert between the two.------ StreamD:------ StreamD is a non-recursive data type in which the state of the stream and--- the step function are separate. When the step function is called, a stream--- element and the new stream state is yielded. The generated element and the--- state are passed to the next consumer in the loop. The state is threaded--- around in the loop until control returns back to the original step function--- to run the next step. This creates a structured closed loop representation--- (like "for" loops in C) with state of each step being hidden/abstracted or--- existential within that step. This creates a loop representation identical--- to the "for" or "while" loop constructs in imperative languages, the states--- of the steps combined together constitute the state of the loop iteration.------ Internally most combinators use a closed loop representation because it--- provides very high efficiency due to stream fusion. The performance of this--- representation is competitive to the C language implementations.------ Pros and Cons of StreamD:------ 1) stream-fusion: This representation can be optimized very efficiently by--- the compiler because the state is explicitly separated from step functions,--- represented using pure data constructors and visible to the compiler, the--- stream steps can be fused using case-of-case transformations and the state--- can be specialized using spec-constructor optimization, yielding a C like--- tight loop/state machine with no constructors, the state is used unboxed and--- therefore no unnecessary allocation.------ 2) Because of a closed representation consing too many elements in this type--- of stream does not scale, it will have quadratic performance slowdown. Each--- cons creates a layer that needs to return the control back to the caller.--- Another implementation of cons is possible but that will have to box/unbox--- the state and will not fuse. So effectively cons breaks fusion.------ 3) unconsing an item from the stream breaks fusion, we have to "pause" the--- loop, rebox and save the state.------ 3) Exception handling is easy to implement in this model because control--- flow is structured in the loop and cannot be arbitrary. Therefore,--- implementing "bracket" is natural.------ 4) Round-robin scheduling for co-operative multitasking is easy to implement.------ 5) It fuses well with the direct style Fold implementation.------ StreamK/StreamDK:------ StreamDK i.e. the stream defined in this module, like StreamK, is a--- recursive data type which has no explicit state defined using constructors,--- each step yields an element and a computation representing the rest of the--- stream.  Stream state is part of the function representing the rest of the--- stream.  This creates an open computation representation, or essentially a--- continuation passing style computation.  After the stream step is executed,--- the caller is free to consume the produced element and then send the control--- wherever it wants, there is no restriction on the control to return back--- somewhere, the control is free to go anywhere. The caller may decide not to--- consume the rest of the stream. This representation is more like a "goto"--- based implementation in imperative languages.------ Pros and Cons of StreamK:------ 1) The way StreamD can be optimized using stream-fusion, this type can be--- optimized using foldr/build fusion. However, foldr/build has not yet been--- fully implemented for StreamK/StreamDK.------ 2) Using cons is natural in this representation, unlike in StreamD it does--- not have a quadratic slowdown. Currently, we in fact wrap StreamD in StreamK--- to support a better cons operation.------ 3) Similarly, uncons is natural in this representation.------ 4) Exception handling is not easy to implement because of the "goto" nature--- of CPS.------ 5) Composable folds are not implemented/proven, however, intuition says that--- a push style CPS representation should be able to be used along with StreamK--- to efficiently implement composable folds.--module Streamly.Internal.Data.Stream.StreamK.Alt-    (-    -- * Stream Type--      Stream-    , Step (..)--    -- * Construction-    , nil-    , cons-    , consM-    , unfoldr-    , unfoldrM-    , replicateM--    -- * Folding-    , uncons-    , foldrS--    -- * Specific Folds-    , drain-    )-where--#include "inline.hs"---- XXX Use Cons and Nil instead of Yield and Stop?-data Step m a = Yield a (Stream m a) | Stop--newtype Stream m a = Stream (m (Step m a))------------------------------------------------------------------------------------ Construction----------------------------------------------------------------------------------nil :: Monad m => Stream m a-nil = Stream $ return Stop--{-# INLINE_NORMAL cons #-}-cons :: Monad m => a -> Stream m a -> Stream m a-cons x xs = Stream $ return $ Yield x xs--consM :: Monad m => m a -> Stream m a -> Stream m a-consM eff xs = Stream $ eff >>= \x -> return $ Yield x xs--unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a-unfoldrM next state = Stream (step' state)-  where-    step' st = do-        r <- next st-        return $ case r of-            Just (x, s) -> Yield x (Stream (step' s))-            Nothing     -> Stop-{--unfoldrM next s0 = buildM $ \yld stp ->-    let go s = do-            r <- next s-            case r of-                Just (a, b) -> yld a (go b)-                Nothing -> stp-    in go s0--}--{-# INLINE unfoldr #-}-unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a-unfoldr next s0 = build $ \yld stp ->-    let go s =-            case next s of-                Just (a, b) -> yld a (go b)-                Nothing -> stp-    in go s0--replicateM :: Monad m => Int -> a -> Stream m a-replicateM n x = Stream (step n)-    where-    step i = return $-        if i <= 0-        then Stop-        else Yield x (Stream (step (i - 1)))------------------------------------------------------------------------------------ Folding----------------------------------------------------------------------------------uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))-uncons (Stream step) = do-    r <- step-    return $ case r of-        Yield x xs -> Just (x, xs)-        Stop -> Nothing---- | Lazy right associative fold to a stream.-{-# INLINE_NORMAL foldrS #-}-foldrS :: Monad m-       => (a -> Stream m b -> Stream m b)-       -> Stream m b-       -> Stream m a-       -> Stream m b-foldrS f streamb = go-    where-    go (Stream stepa) = Stream $ do-        r <- stepa-        case r of-            Yield x xs -> let Stream step = f x (go xs) in step-            Stop -> let Stream step = streamb in step--{-# INLINE_LATE foldrM #-}-foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b-foldrM fstep acc = go-    where-    go (Stream step) = do-        r <- step-        case r of-            Yield x xs -> fstep x (go xs)-            Stop -> acc--{-# INLINE_NORMAL build #-}-build :: Monad m-    => forall a. (forall b. (a -> b -> b) -> b -> b) -> Stream m a-build g = g cons nil--{-# RULES-"foldrM/build"  forall k z (g :: forall b. (a -> b -> b) -> b -> b).-                foldrM k z (build g) = g k z #-}--{---- To fuse foldrM with unfoldrM we need the type m1 to be polymorphic such that--- it is either Monad m or Stream m.  So that we can use cons/nil as well as--- monadic construction function as its arguments.----{-# INLINE_NORMAL buildM #-}-buildM :: Monad m-    => forall a. (forall b. (a -> m1 b -> m1 b) -> m1 b -> m1 b) -> Stream m a-buildM g = g cons nil--}------------------------------------------------------------------------------------ Specific folds----------------------------------------------------------------------------------{-# INLINE drain #-}-drain :: Monad m => Stream m a -> m ()-drain = foldrM (\_ xs -> xs) (return ())-{--drain (Stream step) = do-    r <- step-    case r of-        Yield _ next -> drain next-        Stop      -> return ()-        -}
− src/Streamly/Internal/Data/Stream/StreamK/Transformer.hs
@@ -1,79 +0,0 @@--- |--- Module      : Streamly.Internal.Data.Stream.StreamK.Transformer--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Stream.StreamK.Transformer-    (-      foldlT-    , foldrT--    , liftInner-    , evalStateT-    )-where--import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Monad.Trans.State.Strict (StateT)-import Streamly.Internal.Data.Stream.StreamK-    (StreamK, nil, cons, uncons, concatEffect)--import qualified Control.Monad.Trans.State.Strict as State---- | Lazy left fold to an arbitrary transformer monad.-{-# INLINE foldlT #-}-foldlT :: (Monad m, Monad (s m), MonadTrans s)-    => (s m b -> a -> s m b) -> s m b -> StreamK m a -> s m b-foldlT step = go-  where-    go acc m1 = do-        res <- lift $ uncons m1-        case res of-            Just (h, t) -> go (step acc h) t-            Nothing -> acc---- | Right associative fold to an arbitrary transformer monad.-{-# INLINE foldrT #-}-foldrT :: (Monad m, Monad (s m), MonadTrans s)-    => (a -> s m b -> s m b) -> s m b -> StreamK m a -> s m b-foldrT step final = go-  where-    go m1 = do-        res <- lift $ uncons m1-        case res of-            Just (h, t) -> step h (go t)-            Nothing -> final----------------------------------------------------------------------------------- Lifting inner monad---------------------------------------------------------------------------------{-# INLINE evalStateT #-}-evalStateT :: Monad m => m s -> StreamK (StateT s m) a -> StreamK m a-evalStateT = go--    where--    go st m1 = concatEffect $ fmap f (st >>= State.runStateT (uncons m1))--    f (res, s1) =-        case res of-            Just (h, t) -> cons h (go (return s1) t)-            Nothing -> nil--{-# INLINE liftInner #-}-liftInner :: (Monad m, MonadTrans t, Monad (t m)) =>-    StreamK m a -> StreamK (t m) a-liftInner = go--    where--    go m1 = concatEffect $ fmap f $ lift $ uncons m1--    f res =-        case res of-            Just (h, t) -> cons h (go t)-            Nothing -> nil
− src/Streamly/Internal/Data/Stream/StreamK/Type.hs
@@ -1,2063 +0,0 @@-{-# LANGUAGE CPP #-}-{-# LANGUAGE UndecidableInstances #-}--- |--- Module      : Streamly.Internal.Data.Stream.StreamK.Type--- Copyright   : (c) 2017 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--------- Continuation passing style (CPS) stream implementation. The symbol 'K' below--- denotes a function as well as a Kontinuation.----module Streamly.Internal.Data.Stream.StreamK.Type-    (-    -- * StreamK type-      Stream-    , StreamK (..)--    -- * CrossStreamK type wrapper-    , CrossStreamK-    , unCross-    , mkCross--    -- * foldr/build Fusion-    , mkStream-    , foldStream-    , foldStreamShared-    , foldrM-    , foldrS-    , foldrSShared-    , foldrSM-    , build-    , buildS-    , buildM-    , buildSM-    , augmentS-    , augmentSM-    , unShare--    -- * Construction-    -- ** Primitives-    , fromStopK-    , fromYieldK-    , consK-    , cons-    , (.:)-    , consM-    , consMBy-    , nil-    , nilM--    -- ** Unfolding-    , unfoldr-    , unfoldrMWith-    , unfoldrM--    -- ** From Values-    , fromEffect-    , fromPure-    , repeat-    , repeatMWith-    , replicateMWith--    -- ** From Indices-    , fromIndicesMWith--    -- ** Iteration-    , iterateMWith--    -- ** From Containers-    , fromFoldable-    , fromFoldableM--    -- ** Cyclic-    , mfix--    -- * Elimination-    -- ** Primitives-    , uncons--    -- ** Strict Left Folds-    , Streamly.Internal.Data.Stream.StreamK.Type.foldl'-    , foldlx'--    -- ** Lazy Right Folds-    , Streamly.Internal.Data.Stream.StreamK.Type.foldr--    -- ** Specific Folds-    , drain-    , null-    , tail-    , init--    -- * Mapping-    , map-    , mapMWith-    , mapMSerial--    -- * Combining Two Streams-    -- ** Appending-    , conjoin-    , append--    -- ** Interleave-    , interleave-    , interleaveFst-    , interleaveMin--    -- ** Cross Product-    , crossApplyWith-    , crossApply-    , crossApplySnd-    , crossApplyFst-    , crossWith-    , cross--    -- * Concat-    , before-    , concatEffect-    , concatMapEffect-    , concatMapWith-    , concatMap-    , bindWith-    , concatIterateWith-    , concatIterateLeftsWith-    , concatIterateScanWith--    -- * Merge-    , mergeMapWith-    , mergeIterateWith--    -- * Buffered Operations-    , foldlS-    , reverse-    )-where--#include "inline.hs"---- import Control.Applicative (liftA2)-import Control.Monad ((>=>))-import Control.Monad.Catch (MonadThrow, throwM)-import Control.Monad.Trans.Class (MonadTrans(lift))-import Control.Applicative (liftA2)-import Control.Monad.IO.Class (MonadIO(..))-import Data.Foldable (Foldable(foldl'), fold, foldr)-import Data.Function (fix)-import Data.Functor.Identity (Identity(..))-import Data.Maybe (fromMaybe)-import Data.Semigroup (Endo(..))-import GHC.Exts (IsList(..), IsString(..), oneShot)-import Streamly.Internal.BaseCompat ((#.))-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)-import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)-import Text.Read-       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec-       , readListPrecDefault)--import qualified Prelude--import Prelude hiding-    (map, mapM, concatMap, foldr, repeat, null, reverse, tail, init)--#include "DocTestDataStreamK.hs"----------------------------------------------------------------------------------- Basic stream type----------------------------------------------------------------------------------- It uses stop, singleton and yield continuations equivalent to the following--- direct style type:------ @--- data StreamK m a = Stop | Singleton a | Yield a (StreamK m a)--- @------ To facilitate parallel composition we maintain a local state in an 'SVar'--- that is shared across and is used for synchronization of the streams being--- composed.------ The singleton case can be expressed in terms of stop and yield but we have--- it as a separate case to optimize composition operations for streams with--- single element.  We build singleton streams in the implementation of 'pure'--- for Applicative and Monad, and in 'lift' for MonadTrans.---- XXX remove the State param.---- | Continuation Passing Style (CPS) version of "Streamly.Data.Stream.Stream".--- Unlike "Streamly.Data.Stream.Stream", 'StreamK' can be composed recursively--- without affecting performance.------ Semigroup instance appends two streams:------ >>> (<>) = Stream.append----{-# DEPRECATED Stream "Please use StreamK instead." #-}-type Stream = StreamK--newtype StreamK m a =-    MkStream (forall r.-               State StreamK m a         -- state-            -> (a -> StreamK m a -> m r) -- yield-            -> (a -> m r)               -- singleton-            -> m r                      -- stop-            -> m r-            )--mkStream-    :: (forall r. State StreamK m a-        -> (a -> StreamK m a -> m r)-        -> (a -> m r)-        -> m r-        -> m r)-    -> StreamK m a-mkStream = MkStream---- | A terminal function that has no continuation to follow.-type StopK m = forall r. m r -> m r---- | A monadic continuation, it is a function that yields a value of type "a"--- and calls the argument (a -> m r) as a continuation with that value. We can--- also think of it as a callback with a handler (a -> m r).  Category--- theorists call it a codensity type, a special type of right kan extension.-type YieldK m a = forall r. (a -> m r) -> m r--_wrapM :: Monad m => m a -> YieldK m a-_wrapM m = (m >>=)---- | Make an empty stream from a stop function.-fromStopK :: StopK m -> StreamK m a-fromStopK k = mkStream $ \_ _ _ stp -> k stp---- | Make a singleton stream from a callback function. The callback function--- calls the one-shot yield continuation to yield an element.-fromYieldK :: YieldK m a -> StreamK m a-fromYieldK k = mkStream $ \_ _ sng _ -> k sng---- | Add a yield function at the head of the stream.-consK :: YieldK m a -> StreamK m a -> StreamK m a-consK k r = mkStream $ \_ yld _ _ -> k (`yld` r)---- XXX Build a stream from a repeating callback function.----------------------------------------------------------------------------------- Construction---------------------------------------------------------------------------------infixr 5 `cons`---- faster than consM because there is no bind.---- | A right associative prepend operation to add a pure value at the head of--- an existing stream::------ >>> s = 1 `StreamK.cons` 2 `StreamK.cons` 3 `StreamK.cons` StreamK.nil--- >>> Stream.fold Fold.toList (StreamK.toStream s)--- [1,2,3]------ It can be used efficiently with 'Prelude.foldr':------ >>> fromFoldable = Prelude.foldr StreamK.cons StreamK.nil------ Same as the following but more efficient:------ >>> cons x xs = return x `StreamK.consM` xs----{-# INLINE_NORMAL cons #-}-cons :: a -> StreamK m a -> StreamK m a-cons a r = mkStream $ \_ yield _ _ -> yield a r--infixr 5 .:---- | Operator equivalent of 'cons'.------ @--- > toList $ 1 .: 2 .: 3 .: nil--- [1,2,3]--- @----{-# INLINE (.:) #-}-(.:) :: a -> StreamK m a -> StreamK m a-(.:) = cons---- | A stream that terminates without producing any output or side effect.------ >>> Stream.fold Fold.toList (StreamK.toStream StreamK.nil)--- []----{-# INLINE_NORMAL nil #-}-nil :: StreamK m a-nil = mkStream $ \_ _ _ stp -> stp---- | A stream that terminates without producing any output, but produces a side--- effect.------ >>> Stream.fold Fold.toList (StreamK.toStream (StreamK.nilM (print "nil")))--- "nil"--- []------ /Pre-release/-{-# INLINE_NORMAL nilM #-}-nilM :: Applicative m => m b -> StreamK m a-nilM m = mkStream $ \_ _ _ stp -> m *> stp---- | Create a singleton stream from a pure value.------ >>> fromPure a = a `StreamK.cons` StreamK.nil--- >>> fromPure = pure--- >>> fromPure = StreamK.fromEffect . pure----{-# INLINE_NORMAL fromPure #-}-fromPure :: a -> StreamK m a-fromPure a = mkStream $ \_ _ single _ -> single a---- | Create a singleton stream from a monadic action.------ >>> fromEffect m = m `StreamK.consM` StreamK.nil------ >>> Stream.fold Fold.drain $ StreamK.toStream $ StreamK.fromEffect (putStrLn "hello")--- hello----{-# INLINE_NORMAL fromEffect #-}-fromEffect :: Monad m => m a -> StreamK m a-fromEffect m = mkStream $ \_ _ single _ -> m >>= single--infixr 5 `consM`---- NOTE: specializing the function outside the instance definition seems to--- improve performance quite a bit at times, even if we have the same--- SPECIALIZE in the instance definition.---- | A right associative prepend operation to add an effectful value at the--- head of an existing stream::------ >>> s = putStrLn "hello" `StreamK.consM` putStrLn "world" `StreamK.consM` StreamK.nil--- >>> Stream.fold Fold.drain (StreamK.toStream s)--- hello--- world------ It can be used efficiently with 'Prelude.foldr':------ >>> fromFoldableM = Prelude.foldr StreamK.consM StreamK.nil------ Same as the following but more efficient:------ >>> consM x xs = StreamK.fromEffect x `StreamK.append` xs----{-# INLINE consM #-}-{-# SPECIALIZE consM :: IO a -> StreamK IO a -> StreamK IO a #-}-consM :: Monad m => m a -> StreamK m a -> StreamK m a-consM m r = MkStream $ \_ yld _ _ -> m >>= (`yld` r)---- XXX specialize to IO?-{-# INLINE consMBy #-}-consMBy :: Monad m =>-    (StreamK m a -> StreamK m a -> StreamK m a) -> m a -> StreamK m a -> StreamK m a-consMBy f m r = fromEffect m `f` r----------------------------------------------------------------------------------- Folding a stream----------------------------------------------------------------------------------- | Fold a stream by providing an SVar, a stop continuation, a singleton--- continuation and a yield continuation. The stream would share the current--- SVar passed via the State.-{-# INLINE_EARLY foldStreamShared #-}-foldStreamShared-    :: State StreamK m a-    -> (a -> StreamK m a -> m r)-    -> (a -> m r)-    -> m r-    -> StreamK m a-    -> m r-foldStreamShared s yield single stop (MkStream k) = k s yield single stop---- | Fold a stream by providing a State, stop continuation, a singleton--- continuation and a yield continuation. The stream will not use the SVar--- passed via State.-{-# INLINE foldStream #-}-foldStream-    :: State StreamK m a-    -> (a -> StreamK m a -> m r)-    -> (a -> m r)-    -> m r-    -> StreamK m a-    -> m r-foldStream s yield single stop (MkStream k) =-    k (adaptState s) yield single stop------------------------------------------------------------------------------------ foldr/build fusion------------------------------------------------------------------------------------ XXX perhaps we can just use foldrSM/buildM everywhere as they are more--- general and cover foldrS/buildS as well.---- | The function 'f' decides how to reconstruct the stream. We could--- reconstruct using a shared state (SVar) or without sharing the state.----{-# INLINE foldrSWith #-}-foldrSWith ::-    (forall r. State StreamK m b-        -> (b -> StreamK m b -> m r)-        -> (b -> m r)-        -> m r-        -> StreamK m b-        -> m r)-    -> (a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrSWith f step final m = go m-    where-    go m1 = mkStream $ \st yld sng stp ->-        let run x = f st yld sng stp x-            stop = run final-            single a = run $ step a final-            yieldk a r = run $ step a (go r)-         -- XXX if type a and b are the same we do not need adaptState, can we-         -- save some perf with that?-         -- XXX since we are using adaptState anyway here we can use-         -- foldStreamShared instead, will that save some perf?-         in foldStream (adaptState st) yieldk single stop m1---- XXX we can use rewrite rules just for foldrSWith, if the function f is the--- same we can rewrite it.---- | Fold sharing the SVar state within the reconstructed stream-{-# INLINE_NORMAL foldrSShared #-}-foldrSShared ::-       (a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrSShared = foldrSWith foldStreamShared---- XXX consM is a typeclass method, therefore rewritten already. Instead maybe--- we can make consM polymorphic using rewrite rules.--- {-# RULES "foldrSShared/id"     foldrSShared consM nil = \x -> x #-}-{-# RULES "foldrSShared/nil"-    forall k z. foldrSShared k z nil = z #-}-{-# RULES "foldrSShared/single"-    forall k z x. foldrSShared k z (fromPure x) = k x z #-}--- {-# RULES "foldrSShared/app" [1]---     forall ys. foldrSShared consM ys = \xs -> xs `conjoin` ys #-}---- | Right fold to a streaming monad.------ > foldrS StreamK.cons StreamK.nil === id------ 'foldrS' can be used to perform stateless stream to stream transformations--- like map and filter in general. It can be coupled with a scan to perform--- stateful transformations. However, note that the custom map and filter--- routines can be much more efficient than this due to better stream fusion.------ >>> input = StreamK.fromStream $ Stream.fromList [1..5]--- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS StreamK.cons StreamK.nil input--- [1,2,3,4,5]------ Find if any element in the stream is 'True':------ >>> step x xs = if odd x then StreamK.fromPure True else xs--- >>> input = StreamK.fromStream (Stream.fromList (2:4:5:undefined)) :: StreamK IO Int--- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step (StreamK.fromPure False) input--- [True]------ Map (+2) on odd elements and filter out the even elements:------ >>> step x xs = if odd x then (x + 2) `StreamK.cons` xs else xs--- >>> input = StreamK.fromStream (Stream.fromList [1..5]) :: StreamK IO Int--- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step StreamK.nil input--- [3,5,7]------ /Pre-release/-{-# INLINE_NORMAL foldrS #-}-foldrS ::-       (a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrS = foldrSWith foldStream--{-# RULES "foldrS/id"     foldrS cons nil = \x -> x #-}-{-# RULES "foldrS/nil"    forall k z.   foldrS k z nil  = z #-}--- See notes in GHC.Base about this rule--- {-# RULES "foldr/cons"---  forall k z x xs. foldrS k z (x `cons` xs) = k x (foldrS k z xs) #-}-{-# RULES "foldrS/single" forall k z x. foldrS k z (fromPure x) = k x z #-}--- {-# RULES "foldrS/app" [1]---  forall ys. foldrS cons ys = \xs -> xs `conjoin` ys #-}------------------------------------------------------------------------------------ foldrS with monadic cons i.e. consM----------------------------------------------------------------------------------{-# INLINE foldrSMWith #-}-foldrSMWith :: Monad m-    => (forall r. State StreamK m b-        -> (b -> StreamK m b -> m r)-        -> (b -> m r)-        -> m r-        -> StreamK m b-        -> m r)-    -> (m a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrSMWith f step final m = go m-    where-    go m1 = mkStream $ \st yld sng stp ->-        let run x = f st yld sng stp x-            stop = run final-            single a = run $ step (return a) final-            yieldk a r = run $ step (return a) (go r)-         in foldStream (adaptState st) yieldk single stop m1--{-# INLINE_NORMAL foldrSM #-}-foldrSM :: Monad m-    => (m a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrSM = foldrSMWith foldStream---- {-# RULES "foldrSM/id"     foldrSM consM nil = \x -> x #-}-{-# RULES "foldrSM/nil"    forall k z.   foldrSM k z nil  = z #-}-{-# RULES "foldrSM/single" forall k z x. foldrSM k z (fromEffect x) = k x z #-}--- {-# RULES "foldrSM/app" [1]---  forall ys. foldrSM consM ys = \xs -> xs `conjoin` ys #-}---- Like foldrSM but sharing the SVar state within the recostructed stream.-{-# INLINE_NORMAL foldrSMShared #-}-foldrSMShared :: Monad m-    => (m a -> StreamK m b -> StreamK m b)-    -> StreamK m b-    -> StreamK m a-    -> StreamK m b-foldrSMShared = foldrSMWith foldStreamShared---- {-# RULES "foldrSM/id"     foldrSM consM nil = \x -> x #-}-{-# RULES "foldrSMShared/nil"-    forall k z. foldrSMShared k z nil = z #-}-{-# RULES "foldrSMShared/single"-    forall k z x. foldrSMShared k z (fromEffect x) = k x z #-}--- {-# RULES "foldrSM/app" [1]---  forall ys. foldrSM consM ys = \xs -> xs `conjoin` ys #-}------------------------------------------------------------------------------------ build----------------------------------------------------------------------------------{-# INLINE_NORMAL build #-}-build :: forall m a. (forall b. (a -> b -> b) -> b -> b) -> StreamK m a-build g = g cons nil--{-# RULES "foldrM/build"-    forall k z (g :: forall b. (a -> b -> b) -> b -> b).-    foldrM k z (build g) = g k z #-}--{-# RULES "foldrS/build"-      forall k z (g :: forall b. (a -> b -> b) -> b -> b).-      foldrS k z (build g) = g k z #-}--{-# RULES "foldrS/cons/build"-      forall k z x (g :: forall b. (a -> b -> b) -> b -> b).-      foldrS k z (x `cons` build g) = k x (g k z) #-}--{-# RULES "foldrSShared/build"-      forall k z (g :: forall b. (a -> b -> b) -> b -> b).-      foldrSShared k z (build g) = g k z #-}--{-# RULES "foldrSShared/cons/build"-      forall k z x (g :: forall b. (a -> b -> b) -> b -> b).-      foldrSShared k z (x `cons` build g) = k x (g k z) #-}---- build a stream by applying cons and nil to a build function-{-# INLINE_NORMAL buildS #-}-buildS ::-       ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-    -> StreamK m a-buildS g = g cons nil--{-# RULES "foldrS/buildS"-      forall k z-        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-      foldrS k z (buildS g) = g k z #-}--{-# RULES "foldrS/cons/buildS"-      forall k z x-        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-      foldrS k z (x `cons` buildS g) = k x (g k z) #-}--{-# RULES "foldrSShared/buildS"-      forall k z-        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-      foldrSShared k z (buildS g) = g k z #-}--{-# RULES "foldrSShared/cons/buildS"-      forall k z x-        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-      foldrSShared k z (x `cons` buildS g) = k x (g k z) #-}---- build a stream by applying consM and nil to a build function-{-# INLINE_NORMAL buildSM #-}-buildSM :: Monad m-    => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-    -> StreamK m a-buildSM g = g consM nil--{-# RULES "foldrSM/buildSM"-     forall k z-        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-     foldrSM k z (buildSM g) = g k z #-}--{-# RULES "foldrSMShared/buildSM"-     forall k z-        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-     foldrSMShared k z (buildSM g) = g k z #-}---- Disabled because this may not fire as consM is a class Op-{--{-# RULES "foldrS/consM/buildSM"-      forall k z x (g :: (m a -> t m a -> t m a) -> t m a -> t m a)-    . foldrSM k z (x `consM` buildSM g)-    = k x (g k z)-#-}--}---- Build using monadic build functions (continuations) instead of--- reconstructing a stream.-{-# INLINE_NORMAL buildM #-}-buildM :: Monad m-    => (forall r. (a -> StreamK m a -> m r)-        -> (a -> m r)-        -> m r-        -> m r-       )-    -> StreamK m a-buildM g = mkStream $ \st yld sng stp ->-    g (\a r -> foldStream st yld sng stp (return a `consM` r)) sng stp---- | Like 'buildM' but shares the SVar state across computations.-{-# INLINE_NORMAL sharedMWith #-}-sharedMWith :: Monad m-    => (m a -> StreamK m a -> StreamK m a)-    -> (forall r. (a -> StreamK m a -> m r)-        -> (a -> m r)-        -> m r-        -> m r-       )-    -> StreamK m a-sharedMWith cns g = mkStream $ \st yld sng stp ->-    g (\a r -> foldStreamShared st yld sng stp (return a `cns` r)) sng stp------------------------------------------------------------------------------------ augment----------------------------------------------------------------------------------{-# INLINE_NORMAL augmentS #-}-augmentS ::-       ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-    -> StreamK m a-    -> StreamK m a-augmentS g xs = g cons xs--{-# RULES "augmentS/nil"-    forall (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    augmentS g nil = buildS g-    #-}--{-# RULES "foldrS/augmentS"-    forall k z xs-        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    foldrS k z (augmentS g xs) = g k (foldrS k z xs)-    #-}--{-# RULES "augmentS/buildS"-    forall (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-           (h :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    augmentS g (buildS h) = buildS (\c n -> g c (h c n))-    #-}--{-# INLINE_NORMAL augmentSM #-}-augmentSM :: Monad m =>-       ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-    -> StreamK m a -> StreamK m a-augmentSM g xs = g consM xs--{-# RULES "augmentSM/nil"-    forall-        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    augmentSM g nil = buildSM g-    #-}--{-# RULES "foldrSM/augmentSM"-    forall k z xs-        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    foldrSM k z (augmentSM g xs) = g k (foldrSM k z xs)-    #-}--{-# RULES "augmentSM/buildSM"-    forall-        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)-        (h :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).-    augmentSM g (buildSM h) = buildSM (\c n -> g c (h c n))-    #-}------------------------------------------------------------------------------------ Experimental foldrM/buildM------------------------------------------------------------------------------------ | Lazy right fold with a monadic step function.-{-# INLINE_NORMAL foldrM #-}-foldrM :: (a -> m b -> m b) -> m b -> StreamK m a -> m b-foldrM step acc m = go m-    where-    go m1 =-        let stop = acc-            single a = step a acc-            yieldk a r = step a (go r)-        in foldStream defState yieldk single stop m1--{-# INLINE_NORMAL foldrMKWith #-}-foldrMKWith-    :: (State StreamK m a-        -> (a -> StreamK m a -> m b)-        -> (a -> m b)-        -> m b-        -> StreamK m a-        -> m b)-    -> (a -> m b -> m b)-    -> m b-    -> ((a -> StreamK m a -> m b) -> (a -> m b) -> m b -> m b)-    -> m b-foldrMKWith f step acc = go-    where-    go k =-        let stop = acc-            single a = step a acc-            yieldk a r = step a (go (\yld sng stp -> f defState yld sng stp r))-        in k yieldk single stop--{--{-# RULES "foldrM/buildS"-      forall k z (g :: (a -> t m a -> t m a) -> t m a -> t m a)-    . foldrM k z (buildS g)-    = g k z-#-}--}--- XXX in which case will foldrM/buildM fusion be useful?-{-# RULES "foldrM/buildM"-    forall step acc (g :: (forall r.-           (a -> StreamK m a -> m r)-        -> (a -> m r)-        -> m r-        -> m r-       )).-    foldrM step acc (buildM g) = foldrMKWith foldStream step acc g-    #-}--{--{-# RULES "foldrM/sharedM"-    forall step acc (g :: (forall r.-           (a -> StreamK m a -> m r)-        -> (a -> m r)-        -> m r-        -> m r-       )).-    foldrM step acc (sharedM g) = foldrMKWith foldStreamShared step acc g-    #-}--}----------------------------------------------------------------------------------- Left fold----------------------------------------------------------------------------------- | Strict left fold with an extraction function. Like the standard strict--- left fold, but applies a user supplied extraction function (the third--- argument) to the folded value at the end. This is designed to work with the--- @foldl@ library. The suffix @x@ is a mnemonic for extraction.------ Note that the accumulator is always evaluated including the initial value.-{-# INLINE foldlx' #-}-foldlx' :: forall m a b x. Monad m-    => (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> m b-foldlx' step begin done m = get $ go m begin-    where-    {-# NOINLINE get #-}-    get :: StreamK m x -> m b-    get m1 =-        -- XXX we are not strictly evaluating the accumulator here. Is this-        -- okay?-        let single = return . done-        -- XXX this is foldSingleton. why foldStreamShared?-         in foldStreamShared undefined undefined single undefined m1--    -- Note, this can be implemented by making a recursive call to "go",-    -- however that is more expensive because of unnecessary recursion-    -- that cannot be tail call optimized. Unfolding recursion explicitly via-    -- continuations is much more efficient.-    go :: StreamK m a -> x -> StreamK m x-    go m1 !acc = mkStream $ \_ yld sng _ ->-        let stop = sng acc-            single a = sng $ step acc a-            -- XXX this is foldNonEmptyStream-            yieldk a r = foldStream defState yld sng undefined $-                go r (step acc a)-        in foldStream defState yieldk single stop m1---- | Strict left associative fold.-{-# INLINE foldl' #-}-foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b-foldl' step begin = foldlx' step begin id----------------------------------------------------------------------------------- Specialized folds----------------------------------------------------------------------------------- XXX use foldrM to implement folds where possible--- XXX This (commented) definition of drain and mapM_ perform much better on--- some benchmarks but worse on others. Need to investigate why, may there is--- an optimization opportunity that we can exploit.--- drain = foldrM (\_ xs -> return () >> xs) (return ())---- |--- > drain = foldl' (\_ _ -> ()) ()--- > drain = mapM_ (\_ -> return ())-{-# INLINE drain #-}-drain :: Monad m => StreamK m a -> m ()-drain = foldrM (\_ xs -> xs) (return ())-{--drain = go-    where-    go m1 =-        let stop = return ()-            single _ = return ()-            yieldk _ r = go r-         in foldStream defState yieldk single stop m1--}--{-# INLINE null #-}-null :: Monad m => StreamK m a -> m Bool--- null = foldrM (\_ _ -> return True) (return False)-null m =-    let stop      = return True-        single _  = return False-        yieldk _ _ = return False-    in foldStream defState yieldk single stop m----------------------------------------------------------------------------------- Semigroup---------------------------------------------------------------------------------infixr 6 `append`---- | Appends two streams sequentially, yielding all elements from the first--- stream, and then all elements from the second stream.------ >>> s1 = StreamK.fromStream $ Stream.fromList [1,2]--- >>> s2 = StreamK.fromStream $ Stream.fromList [3,4]--- >>> Stream.fold Fold.toList $ StreamK.toStream $ s1 `StreamK.append` s2--- [1,2,3,4]------ This has O(n) append performance where @n@ is the number of streams. It can--- be used to efficiently fold an infinite lazy container of streams using--- 'concatMapWith' et. al.----{-# INLINE append #-}-append :: StreamK m a -> StreamK m a -> StreamK m a--- XXX This doubles the time of toNullAp benchmark, may not be fusing properly--- serial xs ys = augmentS (\c n -> foldrS c n xs) ys-append m1 m2 = go m1-    where-    go m = mkStream $ \st yld sng stp ->-               let stop       = foldStream st yld sng stp m2-                   single a   = yld a m2-                   yieldk a r = yld a (go r)-               in foldStream st yieldk single stop m---- join/merge/append streams depending on consM-{-# INLINE conjoin #-}-conjoin :: Monad m => StreamK m a -> StreamK m a -> StreamK m a-conjoin xs = augmentSM (\c n -> foldrSM c n xs)--instance Semigroup (StreamK m a) where-    (<>) = append----------------------------------------------------------------------------------- Monoid---------------------------------------------------------------------------------instance Monoid (StreamK m a) where-    mempty = nil-    mappend = (<>)------------------------------------------------------------------------------------ Functor------------------------------------------------------------------------------------ IMPORTANT: This is eta expanded on purpose. This should not be eta--- reduced. This will cause a lot of regressions, probably because of some--- rewrite rules. Ideally don't run hlint on this file.-{-# INLINE_LATE mapFB #-}-mapFB :: forall b m a.-       (b -> StreamK m b -> StreamK m b)-    -> (a -> b)-    -> a-    -> StreamK m b-    -> StreamK m b-mapFB c f = \x ys -> c (f x) ys--{-# RULES-"mapFB/mapFB" forall c f g. mapFB (mapFB c f) g = mapFB c (f . g)-"mapFB/id"    forall c.     mapFB c (\x -> x)   = c-    #-}--{-# INLINE map #-}-map :: (a -> b) -> StreamK m a -> StreamK m b-map f xs = buildS (\c n -> foldrS (mapFB c f) n xs)---- XXX This definition might potentially be more efficient, but the cost in the--- benchmark is dominated by unfoldrM cost so we cannot correctly determine--- differences in the mapping cost. We should perhaps deduct the cost of--- unfoldrM from the benchmarks and then compare.-{--map f m = go m-    where-        go m1 =-            mkStream $ \st yld sng stp ->-            let single     = sng . f-                yieldk a r = yld (f a) (go r)-            in foldStream (adaptState st) yieldk single stp m1--}--{-# INLINE_LATE mapMFB #-}-mapMFB :: Monad m => (m b -> t m b -> t m b) -> (a -> m b) -> m a -> t m b -> t m b-mapMFB c f x = c (x >>= f)--{-# RULES-    "mapMFB/mapMFB" forall c f g. mapMFB (mapMFB c f) g = mapMFB c (f >=> g)-    #-}--- XXX These rules may never fire because pure/return type class rules will--- fire first.-{--"mapMFB/pure"    forall c.     mapMFB c (\x -> pure x)   = c-"mapMFB/return"  forall c.     mapMFB c (\x -> return x) = c--}---- This is experimental serial version supporting fusion.------ XXX what if we do not want to fuse two concurrent mapMs?--- XXX we can combine two concurrent mapM only if the SVar is of the same type--- So for now we use it only for serial streams.--- XXX fusion would be easier for monomoprhic stream types.--- {-# RULES "mapM serial" mapM = mapMSerial #-}-{-# INLINE mapMSerial #-}-mapMSerial :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b-mapMSerial f xs = buildSM (\c n -> foldrSMShared (mapMFB c f) n xs)--{-# INLINE mapMWith #-}-mapMWith ::-       (m b -> StreamK m b -> StreamK m b)-    -> (a -> m b)-    -> StreamK m a-    -> StreamK m b-mapMWith cns f = foldrSShared (\x xs -> f x `cns` xs) nil--{---- See note under map definition above.-mapMWith cns f = go-    where-    go m1 = mkStream $ \st yld sng stp ->-        let single a  = f a >>= sng-            yieldk a r = foldStreamShared st yld sng stp $ f a `cns` go r-         in foldStream (adaptState st) yieldk single stp m1--}---- XXX in fact use the Stream type everywhere and only use polymorphism in the--- high level modules/prelude.-instance Monad m => Functor (StreamK m) where-    fmap = map----------------------------------------------------------------------------------- Lists----------------------------------------------------------------------------------- Serial streams can act like regular lists using the Identity monad---- XXX Show instance is 10x slower compared to read, we can do much better.--- The list show instance itself is really slow.---- XXX The default definitions of "<" in the Ord instance etc. do not perform--- well, because they do not get inlined. Need to add INLINE in Ord class in--- base?--instance IsList (StreamK Identity a) where-    type (Item (StreamK Identity a)) = a--    {-# INLINE fromList #-}-    fromList = fromFoldable--    {-# INLINE toList #-}-    toList = Data.Foldable.foldr (:) []---- XXX Fix these-{--instance Eq a => Eq (StreamK Identity a) where-    {-# INLINE (==) #-}-    (==) xs ys = runIdentity $ eqBy (==) xs ys--instance Ord a => Ord (StreamK Identity a) where-    {-# INLINE compare #-}-    compare xs ys = runIdentity $ cmpBy compare xs ys--    {-# INLINE (<) #-}-    x < y =-        case compare x y of-            LT -> True-            _ -> False--    {-# INLINE (<=) #-}-    x <= y =-        case compare x y of-            GT -> False-            _ -> True--    {-# INLINE (>) #-}-    x > y =-        case compare x y of-            GT -> True-            _ -> False--    {-# INLINE (>=) #-}-    x >= y =-        case compare x y of-            LT -> False-            _ -> True--    {-# INLINE max #-}-    max x y = if x <= y then y else x--    {-# INLINE min #-}-    min x y = if x <= y then x else y--}--instance Show a => Show (StreamK Identity a) where-    showsPrec p dl = showParen (p > 10) $-        showString "fromList " . shows (toList dl)--instance Read a => Read (StreamK Identity a) where-    readPrec = parens $ prec 10 $ do-        Ident "fromList" <- lexP-        fromList <$> readPrec--    readListPrec = readListPrecDefault--instance (a ~ Char) => IsString (StreamK Identity a) where-    {-# INLINE fromString #-}-    fromString = fromList------------------------------------------------------------------------------------ Foldable------------------------------------------------------------------------------------ | Lazy right associative fold.-{-# INLINE foldr #-}-foldr :: Monad m => (a -> b -> b) -> b -> StreamK m a -> m b-foldr step acc = foldrM (\x xs -> xs >>= \b -> return (step x b)) (return acc)---- The default Foldable instance has several issues:--- 1) several definitions do not have INLINE on them, so we provide---    re-implementations with INLINE pragmas.--- 2) the definitions of sum/product/maximum/minimum are inefficient as they---    use right folds, they cannot run in constant memory. We provide---    implementations using strict left folds here.--instance (Foldable m, Monad m) => Foldable (StreamK m) where--    {-# INLINE foldMap #-}-    foldMap f =-          fold-        . Streamly.Internal.Data.Stream.StreamK.Type.foldr (mappend . f) mempty--    {-# INLINE foldr #-}-    foldr f z t = appEndo (foldMap (Endo #. f) t) z--    {-# INLINE foldl' #-}-    foldl' f z0 xs = Data.Foldable.foldr f' id xs z0-        where f' x k = oneShot $ \z -> k $! f z x--    {-# INLINE length #-}-    length = Data.Foldable.foldl' (\n _ -> n + 1) 0--    {-# INLINE elem #-}-    elem = any . (==)--    {-# INLINE maximum #-}-    maximum =-          fromMaybe (errorWithoutStackTrace "maximum: empty stream")-        . toMaybe-        . Data.Foldable.foldl' getMax Nothing'--        where--        getMax Nothing' x = Just' x-        getMax (Just' mx) x = Just' $! max mx x--    {-# INLINE minimum #-}-    minimum =-          fromMaybe (errorWithoutStackTrace "minimum: empty stream")-        . toMaybe-        . Data.Foldable.foldl' getMin Nothing'--        where--        getMin Nothing' x = Just' x-        getMin (Just' mn) x = Just' $! min mn x--    {-# INLINE sum #-}-    sum = Data.Foldable.foldl' (+) 0--    {-# INLINE product #-}-    product = Data.Foldable.foldl' (*) 1------------------------------------------------------------------------------------ Traversable----------------------------------------------------------------------------------instance Traversable (StreamK Identity) where-    {-# INLINE traverse #-}-    traverse f xs =-        runIdentity-            $ Streamly.Internal.Data.Stream.StreamK.Type.foldr-                consA (pure mempty) xs--        where--        consA x ys = liftA2 cons (f x) ys------------------------------------------------------------------------------------ Nesting------------------------------------------------------------------------------------ | Detach a stream from an SVar-{-# INLINE unShare #-}-unShare :: StreamK m a -> StreamK m a-unShare x = mkStream $ \st yld sng stp ->-    foldStream st yld sng stp x---- XXX the function stream and value stream can run in parallel-{-# INLINE crossApplyWith #-}-crossApplyWith ::-       (StreamK m b -> StreamK m b -> StreamK m b)-    -> StreamK m (a -> b)-    -> StreamK m a-    -> StreamK m b-crossApplyWith par fstream stream = go1 fstream--    where--    go1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single f   = foldShared $ unShare (go2 f stream)-                yieldk f r = foldShared $ unShare (go2 f stream) `par` go1 r-            in foldStream (adaptState st) yieldk single stp m--    go2 f m =-        mkStream $ \st yld sng stp ->-            let single a   = sng (f a)-                yieldk a r = yld (f a) (go2 f r)-            in foldStream (adaptState st) yieldk single stp m---- | Apply a stream of functions to a stream of values and flatten the results.------ Note that the second stream is evaluated multiple times.------ Definition:------ >>> crossApply = StreamK.crossApplyWith StreamK.append--- >>> crossApply = Stream.crossWith id----{-# INLINE crossApply #-}-crossApply ::-       StreamK m (a -> b)-    -> StreamK m a-    -> StreamK m b-crossApply fstream stream = go1 fstream--    where--    go1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single f   = foldShared $ go3 f stream-                yieldk f r = foldShared $ go2 f r stream-            in foldStream (adaptState st) yieldk single stp m--    go2 f r1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                stop = foldShared $ go1 r1-                single a   = yld (f a) (go1 r1)-                yieldk a r = yld (f a) (go2 f r1 r)-            in foldStream (adaptState st) yieldk single stop m--    go3 f m =-        mkStream $ \st yld sng stp ->-            let single a   = sng (f a)-                yieldk a r = yld (f a) (go3 f r)-            in foldStream (adaptState st) yieldk single stp m--{-# INLINE crossApplySnd #-}-crossApplySnd ::-       StreamK m a-    -> StreamK m b-    -> StreamK m b-crossApplySnd fstream stream = go1 fstream--    where--    go1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single _   = foldShared stream-                yieldk _ r = foldShared $ go2 r stream-            in foldStream (adaptState st) yieldk single stp m--    go2 r1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                stop = foldShared $ go1 r1-                single a   = yld a (go1 r1)-                yieldk a r = yld a (go2 r1 r)-            in foldStream st yieldk single stop m--{-# INLINE crossApplyFst #-}-crossApplyFst ::-       StreamK m a-    -> StreamK m b-    -> StreamK m a-crossApplyFst fstream stream = go1 fstream--    where--    go1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single f   = foldShared $ go3 f stream-                yieldk f r = foldShared $ go2 f r stream-            in foldStream st yieldk single stp m--    go2 f r1 m =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                stop = foldShared $ go1 r1-                single _   = yld f (go1 r1)-                yieldk _ r = yld f (go2 f r1 r)-            in foldStream (adaptState st) yieldk single stop m--    go3 f m =-        mkStream $ \st yld sng stp ->-            let single _   = sng f-                yieldk _ r = yld f (go3 f r)-            in foldStream (adaptState st) yieldk single stp m---- |--- Definition:------ >>> crossWith f m1 m2 = fmap f m1 `StreamK.crossApply` m2------ Note that the second stream is evaluated multiple times.----{-# INLINE crossWith #-}-crossWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c-crossWith f m1 m2 = fmap f m1 `crossApply` m2---- | Given a @StreamK m a@ and @StreamK m b@ generate a stream with all possible--- combinations of the tuple @(a, b)@.------ Definition:------ >>> cross = StreamK.crossWith (,)------ The second stream is evaluated multiple times. If that is not desired it can--- be cached in an 'Data.Array.Array' and then generated from the array before--- calling this function. Caching may also improve performance if the stream is--- expensive to evaluate.------ See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused--- alternative.------ Time: O(m x n)------ /Pre-release/-{-# INLINE cross #-}-cross :: Monad m => StreamK m a -> StreamK m b -> StreamK m (a, b)-cross = crossWith (,)---- XXX This is just concatMapWith with arguments flipped. We need to keep this--- instead of using a concatMap style definition because the bind--- implementation in Async and WAsync streams show significant perf degradation--- if the argument order is changed.-{-# INLINE bindWith #-}-bindWith ::-       (StreamK m b -> StreamK m b -> StreamK m b)-    -> StreamK m a-    -> (a -> StreamK m b)-    -> StreamK m b-bindWith par m1 f = go m1-    where-        go m =-            mkStream $ \st yld sng stp ->-                let foldShared = foldStreamShared st yld sng stp-                    single a   = foldShared $ unShare (f a)-                    yieldk a r = foldShared $ unShare (f a) `par` go r-                in foldStream (adaptState st) yieldk single stp m---- XXX express in terms of foldrS?--- XXX can we use a different stream type for the generated stream being--- falttened so that we can combine them differently and keep the resulting--- stream different?--- XXX do we need specialize to IO?--- XXX can we optimize when c and a are same, by removing the forall using--- rewrite rules with type applications?---- | Perform a 'concatMap' using a specified concat strategy. The first--- argument specifies a merge or concat function that is used to merge the--- streams generated by the map function.----{-# INLINE concatMapWith #-}-concatMapWith-    ::-       (StreamK m b -> StreamK m b -> StreamK m b)-    -> (a -> StreamK m b)-    -> StreamK m a-    -> StreamK m b-concatMapWith par f xs = bindWith par xs f--{-# INLINE concatMap #-}-concatMap :: (a -> StreamK m b) -> StreamK m a -> StreamK m b-concatMap = concatMapWith append--{---- Fused version.--- XXX This fuses but when the stream is nil this performs poorly.--- The filterAllOut benchmark degrades. Need to investigate and fix that.-{-# INLINE concatMap #-}-concatMap :: IsStream t => (a -> t m b) -> t m a -> t m b-concatMap f xs = buildS-    (\c n -> foldrS (\x b -> foldrS c b (f x)) n xs)---- Stream polymorphic concatMap implementation--- XXX need to use buildSM/foldrSMShared for parallel behavior--- XXX unShare seems to degrade the fused performance-{-# INLINE_EARLY concatMap_ #-}-concatMap_ :: IsStream t => (a -> t m b) -> t m a -> t m b-concatMap_ f xs = buildS-     (\c n -> foldrSShared (\x b -> foldrSShared c b (unShare $ f x)) n xs)--}---- | Combine streams in pairs using a binary combinator, the resulting streams--- are then combined again in pairs recursively until we get to a single--- combined stream. The composition would thus form a binary tree.------ For example, you can sort a stream using merge sort like this:------ >>> s = StreamK.fromStream $ Stream.fromList [5,1,7,9,2]--- >>> generate = StreamK.fromPure--- >>> combine = StreamK.mergeBy compare--- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.mergeMapWith combine generate s--- [1,2,5,7,9]------ Note that if the stream length is not a power of 2, the binary tree composed--- by mergeMapWith would not be balanced, which may or may not be important--- depending on what you are trying to achieve.------ /Caution: the stream of streams must be finite/------ /Pre-release/----{-# INLINE mergeMapWith #-}-mergeMapWith-    ::-       (StreamK m b -> StreamK m b -> StreamK m b)-    -> (a -> StreamK m b)-    -> StreamK m a-    -> StreamK m b-mergeMapWith combine f str = go (leafPairs str)--    where--    go stream =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single a   = foldShared $ unShare a-                yieldk a r = foldShared $ go1 a r-            in foldStream (adaptState st) yieldk single stp stream--    go1 a1 stream =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                stop = foldShared $ unShare a1-                single a = foldShared $ unShare a1 `combine` a-                yieldk a r =-                    foldShared $ go $ combine a1 a `cons` nonLeafPairs r-            in foldStream (adaptState st) yieldk single stop stream--    -- Exactly the same as "go" except that stop continuation extracts the-    -- stream.-    leafPairs stream =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single a   = sng (f a)-                yieldk a r = foldShared $ leafPairs1 a r-            in foldStream (adaptState st) yieldk single stp stream--    leafPairs1 a1 stream =-        mkStream $ \st yld sng _ ->-            let stop = sng (f a1)-                single a = sng (f a1 `combine` f a)-                yieldk a r = yld (f a1 `combine` f a) $ leafPairs r-            in foldStream (adaptState st) yieldk single stop stream--    -- Exactly the same as "leafPairs" except that it does not map "f"-    nonLeafPairs stream =-        mkStream $ \st yld sng stp ->-            let foldShared = foldStreamShared st yld sng stp-                single a   = sng a-                yieldk a r = foldShared $ nonLeafPairs1 a r-            in foldStream (adaptState st) yieldk single stp stream--    nonLeafPairs1 a1 stream =-        mkStream $ \st yld sng _ ->-            let stop = sng a1-                single a = sng (a1 `combine` a)-                yieldk a r = yld (a1 `combine` a) $ nonLeafPairs r-            in foldStream (adaptState st) yieldk single stop stream--{--instance Monad m => Applicative (StreamK m) where-    {-# INLINE pure #-}-    pure = fromPure--    {-# INLINE (<*>) #-}-    (<*>) = crossApply--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)--    {-# INLINE (*>) #-}-    (*>) = crossApplySnd--    {-# INLINE (<*) #-}-    (<*) = crossApplyFst---- NOTE: even though concatMap for StreamD is 3x faster compared to StreamK,--- the monad instance of StreamD is slower than StreamK after foldr/build--- fusion.-instance Monad m => Monad (StreamK m) where-    {-# INLINE return #-}-    return = pure--    {-# INLINE (>>=) #-}-    (>>=) = flip concatMap--}--{---- Like concatMap but generates stream using an unfold function. Similar to--- unfoldMany but for StreamK.-concatUnfoldr :: IsStream t-    => (b -> t m (Maybe (a, b))) -> t m b -> t m a-concatUnfoldr = undefined--}----------------------------------------------------------------------------------- concatIterate - Map and flatten Trees of Streams----------------------------------------------------------------------------------- | Yield an input element in the output stream, map a stream generator on it--- and repeat the process on the resulting stream. Resulting streams are--- flattened using the 'concatMapWith' combinator. This can be used for a depth--- first style (DFS) traversal of a tree like structure.------ Example, list a directory tree using DFS:------ >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)--- >>> input = StreamK.fromPure (Left ".")--- >>> ls = StreamK.concatIterateWith StreamK.append f input------ Note that 'iterateM' is a special case of 'concatIterateWith':------ >>> iterateM f = StreamK.concatIterateWith StreamK.append (StreamK.fromEffect . f) . StreamK.fromEffect------ /Pre-release/----{-# INLINE concatIterateWith #-}-concatIterateWith ::-       (StreamK m a -> StreamK m a -> StreamK m a)-    -> (a -> StreamK m a)-    -> StreamK m a-    -> StreamK m a-concatIterateWith combine f = iterateStream--    where--    iterateStream = concatMapWith combine generate--    generate x = x `cons` iterateStream (f x)---- | Like 'concatIterateWith' but uses the pairwise flattening combinator--- 'mergeMapWith' for flattening the resulting streams. This can be used for a--- balanced traversal of a tree like structure.------ Example, list a directory tree using balanced traversal:------ >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)--- >>> input = StreamK.fromPure (Left ".")--- >>> ls = StreamK.mergeIterateWith StreamK.interleave f input------ /Pre-release/----{-# INLINE mergeIterateWith #-}-mergeIterateWith ::-       (StreamK m a -> StreamK m a -> StreamK m a)-    -> (a -> StreamK m a)-    -> StreamK m a-    -> StreamK m a-mergeIterateWith combine f = iterateStream--    where--    iterateStream = mergeMapWith combine generate--    generate x = x `cons` iterateStream (f x)----------------------------------------------------------------------------------- Flattening Graphs----------------------------------------------------------------------------------- To traverse graphs we need a state to be carried around in the traversal.--- For example, we can use a hashmap to store the visited status of nodes.---- | Like 'iterateMap' but carries a state in the stream generation function.--- This can be used to traverse graph like structures, we can remember the--- visited nodes in the state to avoid cycles.------ Note that a combination of 'iterateMap' and 'usingState' can also be used to--- traverse graphs. However, this function provides a more localized state--- instead of using a global state.------ See also: 'mfix'------ /Pre-release/----{-# INLINE concatIterateScanWith #-}-concatIterateScanWith-    :: Monad m-    => (StreamK m a -> StreamK m a -> StreamK m a)-    -> (b -> a -> m (b, StreamK m a))-    -> m b-    -> StreamK m a-    -> StreamK m a-concatIterateScanWith combine f initial stream =-    concatEffect $ do-        b <- initial-        iterateStream (b, stream)--    where--    iterateStream (b, s) = pure $ concatMapWith combine (generate b) s--    generate b a = a `cons` feedback b a--    feedback b a = concatEffect $ f b a >>= iterateStream----------------------------------------------------------------------------------- Either streams----------------------------------------------------------------------------------- Keep concating either streams as long as rights are generated, stop as soon--- as a left is generated and concat the left stream.------ See also: 'handle'------ /Unimplemented/----{--concatMapEitherWith-    :: (forall x. t m x -> t m x -> t m x)-    -> (a -> t m (Either (StreamK m b) b))-    -> StreamK m a-    -> StreamK m b-concatMapEitherWith = undefined--}---- XXX We should prefer using the Maybe stream returning signatures over this.--- This API should perhaps be removed in favor of those.---- | In an 'Either' stream iterate on 'Left's.  This is a special case of--- 'concatIterateWith':------ >>> concatIterateLeftsWith combine f = StreamK.concatIterateWith combine (either f (const StreamK.nil))------ To traverse a directory tree:------ >>> input = StreamK.fromPure (Left ".")--- >>> ls = StreamK.concatIterateLeftsWith StreamK.append (StreamK.fromStream . Dir.readEither) input------ /Pre-release/----{-# INLINE concatIterateLeftsWith #-}-concatIterateLeftsWith-    :: (b ~ Either a c)-    => (StreamK m b -> StreamK m b -> StreamK m b)-    -> (a -> StreamK m b)-    -> StreamK m b-    -> StreamK m b-concatIterateLeftsWith combine f =-    concatIterateWith combine (either f (const nil))----------------------------------------------------------------------------------- Interleaving---------------------------------------------------------------------------------infixr 6 `interleave`---- Additionally we can have m elements yield from the first stream and n--- elements yielding from the second stream. We can also have time slicing--- variants of positional interleaving, e.g. run first stream for m seconds and--- run the second stream for n seconds.---- | Interleaves two streams, yielding one element from each stream--- alternately.  When one stream stops the rest of the other stream is used in--- the output stream.------ When joining many streams in a left associative manner earlier streams will--- get exponential priority than the ones joining later. Because of exponential--- weighting it can be used with 'concatMapWith' even on a large number of--- streams.----{-# INLINE interleave #-}-interleave :: StreamK m a -> StreamK m a -> StreamK m a-interleave m1 m2 = mkStream $ \st yld sng stp -> do-    let stop       = foldStream st yld sng stp m2-        single a   = yld a m2-        yieldk a r = yld a (interleave m2 r)-    foldStream st yieldk single stop m1--infixr 6 `interleaveFst`---- | Like `interleave` but stops interleaving as soon as the first stream stops.----{-# INLINE interleaveFst #-}-interleaveFst :: StreamK m a -> StreamK m a -> StreamK m a-interleaveFst m1 m2 = mkStream $ \st yld sng stp -> do-    let yieldFirst a r = yld a (yieldSecond r m2)-     in foldStream st yieldFirst sng stp m1--    where--    yieldSecond s1 s2 = mkStream $ \st yld sng stp -> do-            let stop       = foldStream st yld sng stp s1-                single a   = yld a s1-                yieldk a r = yld a (interleave s1 r)-             in foldStream st yieldk single stop s2--infixr 6 `interleaveMin`---- | Like `interleave` but stops interleaving as soon as any of the two streams--- stops.----{-# INLINE interleaveMin #-}-interleaveMin :: StreamK m a -> StreamK m a -> StreamK m a-interleaveMin m1 m2 = mkStream $ \st yld _ stp -> do-    let stop       = stp-        -- "single a" is defined as "yld a (interleaveMin m2 nil)" instead of-        -- "sng a" to keep the behaviour consistent with the yield-        -- continuation.-        single a   = yld a (interleaveMin m2 nil)-        yieldk a r = yld a (interleaveMin m2 r)-    foldStream st yieldk single stop m1------------------------------------------------------------------------------------ Generation----------------------------------------------------------------------------------{-# INLINE unfoldr #-}-unfoldr :: (b -> Maybe (a, b)) -> b -> StreamK m a-unfoldr next s0 = build $ \yld stp ->-    let go s =-            case next s of-                Just (a, b) -> yld a (go b)-                Nothing -> stp-    in go s0--{-# INLINE unfoldrMWith #-}-unfoldrMWith :: Monad m =>-       (m a -> StreamK m a -> StreamK m a)-    -> (b -> m (Maybe (a, b)))-    -> b-    -> StreamK m a-unfoldrMWith cns step = go--    where--    go s = sharedMWith cns $ \yld _ stp -> do-                r <- step s-                case r of-                    Just (a, b) -> yld a (go b)-                    Nothing -> stp--{-# INLINE unfoldrM #-}-unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> StreamK m a-unfoldrM = unfoldrMWith consM---- | Generate an infinite stream by repeating a pure value.------ /Pre-release/-{-# INLINE repeat #-}-repeat :: a -> StreamK m a-repeat a = let x = cons a x in x---- | Like 'repeatM' but takes a stream 'cons' operation to combine the actions--- in a stream specific manner. A serial cons would repeat the values serially--- while an async cons would repeat concurrently.------ /Pre-release/-repeatMWith :: (m a -> t m a -> t m a) -> m a -> t m a-repeatMWith cns = go--    where--    go m = m `cns` go m--{-# INLINE replicateMWith #-}-replicateMWith :: (m a -> StreamK m a -> StreamK m a) -> Int -> m a -> StreamK m a-replicateMWith cns n m = go n--    where--    go cnt = if cnt <= 0 then nil else m `cns` go (cnt - 1)--{-# INLINE fromIndicesMWith #-}-fromIndicesMWith ::-    (m a -> StreamK m a -> StreamK m a) -> (Int -> m a) -> StreamK m a-fromIndicesMWith cns gen = go 0--    where--    go i = mkStream $ \st stp sng yld -> do-        foldStreamShared st stp sng yld (gen i `cns` go (i + 1))--{-# INLINE iterateMWith #-}-iterateMWith :: Monad m =>-    (m a -> StreamK m a -> StreamK m a) -> (a -> m a) -> m a -> StreamK m a-iterateMWith cns step = go--    where--    go s = mkStream $ \st stp sng yld -> do-        !next <- s-        foldStreamShared st stp sng yld (return next `cns` go (step next))--{-# INLINE headPartial #-}-headPartial :: Monad m => StreamK m a -> m a-headPartial = foldrM (\x _ -> return x) (error "head of nil")--{-# INLINE tailPartial #-}-tailPartial :: StreamK m a -> StreamK m a-tailPartial m = mkStream $ \st yld sng stp ->-    let stop      = error "tail of nil"-        single _  = stp-        yieldk _ r = foldStream st yld sng stp r-    in foldStream st yieldk single stop m---- | We can define cyclic structures using @let@:------ >>> let (a, b) = ([1, b], head a) in (a, b)--- ([1,1],1)------ The function @fix@ defined as:------ >>> fix f = let x = f x in x------ ensures that the argument of a function and its output refer to the same--- lazy value @x@ i.e.  the same location in memory.  Thus @x@ can be defined--- in terms of itself, creating structures with cyclic references.------ >>> f ~(a, b) = ([1, b], head a)--- >>> fix f--- ([1,1],1)------ 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic--- values.------ Using 'mfix' for streams we can construct a stream in which each element of--- the stream is defined in a cyclic fashion. The argument of the function--- being fixed represents the current element of the stream which is being--- returned by the stream monad. Thus, we can use the argument to construct--- itself.------ In the following example, the argument @action@ of the function @f@--- represents the tuple @(x,y)@ returned by it in a given iteration. We define--- the first element of the tuple in terms of the second.------ >>> import System.IO.Unsafe (unsafeInterleaveIO)------ >>> :{--- main = Stream.fold (Fold.drainMapM print) $ StreamK.toStream $ StreamK.mfix f---     where---     f action = StreamK.unCross $ do---         let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act---         x <- StreamK.mkCross $ StreamK.fromStream $ Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action]---         y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [4,5]---         return (x, y)--- :}------ Note: you cannot achieve this by just changing the order of the monad--- statements because that would change the order in which the stream elements--- are generated.------ Note that the function @f@ must be lazy in its argument, that's why we use--- 'unsafeInterleaveIO' on @action@ because IO monad is strict.------ /Pre-release/-{-# INLINE mfix #-}-mfix :: Monad m => (m a -> StreamK m a) -> StreamK m a-mfix f = mkStream $ \st yld sng stp ->-    let single a  = foldStream st yld sng stp $ a `cons` ys-        yieldk a _ = foldStream st yld sng stp $ a `cons` ys-    in foldStream st yieldk single stp xs--    where--    -- fix the head element of the stream-    xs = fix  (f . headPartial)--    -- now fix the tail recursively-    ys = mfix (tailPartial . f)------------------------------------------------------------------------------------ Conversions------------------------------------------------------------------------------------ |--- >>> fromFoldable = Prelude.foldr StreamK.cons StreamK.nil------ Construct a stream from a 'Foldable' containing pure values:----{-# INLINE fromFoldable #-}-fromFoldable :: Foldable f => f a -> StreamK m a-fromFoldable = Prelude.foldr cons nil--{-# INLINE fromFoldableM #-}-fromFoldableM :: (Foldable f, Monad m) => f (m a) -> StreamK m a-fromFoldableM = Prelude.foldr consM nil------------------------------------------------------------------------------------ Deconstruction----------------------------------------------------------------------------------{-# INLINE uncons #-}-uncons :: Applicative m => StreamK m a -> m (Maybe (a, StreamK m a))-uncons m =-    let stop = pure Nothing-        single a = pure (Just (a, nil))-        yieldk a r = pure (Just (a, r))-    in foldStream defState yieldk single stop m--{-# INLINE tail #-}-tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))-tail =-    let stop      = pure Nothing-        single _  = pure $ Just nil-        yieldk _ r = pure $ Just r-    in foldStream defState yieldk single stop---- | Extract all but the last element of the stream, if any.------ Note: This will end up buffering the entire stream.------ /Pre-release/-{-# INLINE init #-}-init :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))-init = go1-    where-    go1 m1 = do-        (\case-            Nothing -> Nothing-            Just (h, t) -> Just $ go h t) <$> uncons m1-    go p m1 = mkStream $ \_ yld sng stp ->-        let single _ = sng p-            yieldk a x = yld p $ go a x-         in foldStream defState yieldk single stp m1----------------------------------------------------------------------------------- Reordering----------------------------------------------------------------------------------- | Lazy left fold to a stream.-{-# INLINE foldlS #-}-foldlS ::-    (StreamK m b -> a -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b-foldlS step = go-    where-    go acc rest = mkStream $ \st yld sng stp ->-        let run x = foldStream st yld sng stp x-            stop = run acc-            single a = run $ step acc a-            yieldk a r = run $ go (step acc a) r-         in foldStream (adaptState st) yieldk single stop rest--{-# INLINE reverse #-}-reverse :: StreamK m a -> StreamK m a-reverse = foldlS (flip cons) nil----------------------------------------------------------------------------------- Running effects----------------------------------------------------------------------------------- | Run an action before evaluating the stream.-{-# INLINE before #-}-before :: Monad m => m b -> StreamK m a -> StreamK m a-before action stream =-    mkStream $ \st yld sng stp ->-        action >> foldStreamShared st yld sng stp stream---- | concat . fromEffect-{-# INLINE concatEffect #-}-concatEffect :: Monad m => m (StreamK m a) -> StreamK m a-concatEffect action =-    mkStream $ \st yld sng stp ->-        action >>= foldStreamShared st yld sng stp--{-# INLINE concatMapEffect #-}-concatMapEffect :: Monad m => (b -> StreamK m a) -> m b -> StreamK m a-concatMapEffect f action =-    mkStream $ \st yld sng stp ->-        action >>= foldStreamShared st yld sng stp . f----------------------------------------------------------------------------------- Stream with a cross product style monad instance----------------------------------------------------------------------------------- | A newtype wrapper for the 'StreamK' type adding a cross product style--- monad instance.------ A 'Monad' bind behaves like a @for@ loop:------ >>> :{--- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do---     x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]---     -- Perform the following actions for each x in the stream---     return x--- :}--- [1,2]------ Nested monad binds behave like nested @for@ loops:------ >>> :{--- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do---     x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]---     y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [3,4]---     -- Perform the following actions for each x, for each y---     return (x, y)--- :}--- [(1,3),(1,4),(2,3),(2,4)]----newtype CrossStreamK m a = CrossStreamK {unCrossStreamK :: StreamK m a}-        deriving (Functor, Semigroup, Monoid, Foldable)---- | Wrap the 'StreamK' type in a 'CrossStreamK' newtype to enable cross--- product style applicative and monad instances.------ This is a type level operation with no runtime overhead.-{-# INLINE mkCross #-}-mkCross :: StreamK m a -> CrossStreamK m a-mkCross = CrossStreamK---- | Unwrap the 'StreamK' type from 'CrossStreamK' newtype.------ This is a type level operation with no runtime overhead.-{-# INLINE unCross #-}-unCross :: CrossStreamK m a -> StreamK m a-unCross = unCrossStreamK---- Pure (Identity monad) stream instances-deriving instance Traversable (CrossStreamK Identity)-deriving instance IsList (CrossStreamK Identity a)-deriving instance (a ~ Char) => IsString (CrossStreamK Identity a)--- deriving instance Eq a => Eq (CrossStreamK Identity a)--- deriving instance Ord a => Ord (CrossStreamK Identity a)---- Do not use automatic derivation for this to show as "fromList" rather than--- "fromList Identity".-instance Show a => Show (CrossStreamK Identity a) where-    {-# INLINE show #-}-    show (CrossStreamK xs) = show xs--instance Read a => Read (CrossStreamK Identity a) where-    {-# INLINE readPrec #-}-    readPrec = fmap CrossStreamK readPrec----------------------------------------------------------------------------------- Applicative----------------------------------------------------------------------------------- Note: we need to define all the typeclass operations because we want to--- INLINE them.-instance Monad m => Applicative (CrossStreamK m) where-    {-# INLINE pure #-}-    pure x = CrossStreamK (fromPure x)--    {-# INLINE (<*>) #-}-    (CrossStreamK s1) <*> (CrossStreamK s2) =-        CrossStreamK (crossApply s1 s2)--    {-# INLINE liftA2 #-}-    liftA2 f x = (<*>) (fmap f x)--    {-# INLINE (*>) #-}-    (CrossStreamK s1) *> (CrossStreamK s2) =-        CrossStreamK (crossApplySnd s1 s2)--    {-# INLINE (<*) #-}-    (CrossStreamK s1) <* (CrossStreamK s2) =-        CrossStreamK (crossApplyFst s1 s2)----------------------------------------------------------------------------------- Monad---------------------------------------------------------------------------------instance Monad m => Monad (CrossStreamK m) where-    return = pure--    -- Benchmarks better with CPS bind and pure:-    -- Prime sieve (25x)-    -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)-    ---    {-# INLINE (>>=) #-}-    (>>=) (CrossStreamK m) f =-        CrossStreamK (bindWith append m (unCrossStreamK . f))--    {-# INLINE (>>) #-}-    (>>) = (*>)----------------------------------------------------------------------------------- Transformers---------------------------------------------------------------------------------instance (MonadIO m) => MonadIO (CrossStreamK m) where-    liftIO x = CrossStreamK (fromEffect $ liftIO x)--instance MonadTrans CrossStreamK where-    {-# INLINE lift #-}-    lift x = CrossStreamK (fromEffect x)--instance (MonadThrow m) => MonadThrow (CrossStreamK m) where-    throwM = lift . throwM
+ src/Streamly/Internal/Data/Stream/Top.hs view
@@ -0,0 +1,352 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Stream.Top+-- Copyright   : (c) 2020 Composewell Technologies+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- Top level module that can depend on all other lower level Stream modules.++module Streamly.Internal.Data.Stream.Top+    (+    -- * Transformation+    -- ** Sampling+    -- | Value agnostic filtering.+      strideFromThen++    -- * Nesting+    -- ** Set like operations+    -- | These are not exactly set operations because streams are not+    -- necessarily sets, they may have duplicated elements. These operations+    -- are generic i.e. they work on streams of unconstrained types, therefore,+    -- they have quadratic performance characterstics. For better performance+    -- using Set structures see the Streamly.Internal.Data.Stream.Container+    -- module.+    , filterInStreamGenericBy+    , deleteInStreamGenericBy+    , unionWithStreamGenericBy++    -- ** Set like operations on sorted streams+    , filterInStreamAscBy+    , deleteInStreamAscBy+    , unionWithStreamAscBy++    -- ** Join operations+    , joinInnerGeneric++    -- * Joins on sorted stream+    , joinInnerAscBy+    , joinLeftAscBy+    , joinOuterAscBy+    )+where++#include "inline.hs"++import Control.Monad.IO.Class (MonadIO(..))+import Data.IORef (newIORef, readIORef, modifyIORef')+import Streamly.Internal.Data.Fold.Type (Fold)+import Streamly.Internal.Data.Stream.Type (Stream, cross)++import qualified Data.List as List+import qualified Streamly.Internal.Data.Fold as Fold+import qualified Streamly.Internal.Data.Stream.Type as Stream+import qualified Streamly.Internal.Data.Stream.Nesting as Stream+import qualified Streamly.Internal.Data.Stream.Transform as Stream++import Prelude hiding (filter, zipWith, concatMap, concat)++#include "DocTestDataStream.hs"++------------------------------------------------------------------------------+-- Sampling+------------------------------------------------------------------------------++-- XXX We can implement this using addition instead of "mod" to make it more+-- efficient.++-- | @strideFromthen offset stride@ takes the element at @offset@ index and+-- then every element at strides of @stride@.+--+-- >>> Stream.fold Fold.toList $ Stream.strideFromThen 2 3 $ Stream.enumerateFromTo 0 10+-- [2,5,8]+--+{-# INLINE strideFromThen #-}+strideFromThen :: Monad m => Int -> Int -> Stream m a -> Stream m a+strideFromThen offset stride =+    Stream.with Stream.indexed Stream.filter+        (\(i, _) -> i >= offset && (i - offset) `mod` stride == 0)++------------------------------------------------------------------------------+-- SQL Joins+------------------------------------------------------------------------------+--+-- Some references:+-- * https://en.wikipedia.org/wiki/Relational_algebra+-- * https://en.wikipedia.org/wiki/Join_(SQL)++-- TODO: OrdSet/IntSet/hashmap based versions of these. With Eq only+-- constraint, the best would be to use an Array with linear search. If the+-- second stream is sorted we can also use a binary search, using Ord+-- constraint or an ordering function.+--+-- For Storables we can cache the second stream into an unboxed array for+-- possibly faster access/compact representation?+--+-- If we do not want to keep the stream in memory but always read it from the+-- source (disk/network) every time we iterate through it then we can do that+-- too by reading the stream every time, the stream must have immutable state+-- in that case and the user is responsible for the behavior if the stream+-- source changes during iterations. We can also use an Unfold instead of+-- stream. We probably need a way to distinguish streams that can be read+-- mutliple times without any interference (e.g. unfolding a stream using an+-- immutable handle would work i.e. using pread/pwrite instead of maintaining+-- an offset in the handle).++-- XXX We can do this concurrently.+-- XXX If the second stream is sorted and passed as an Array we could use+-- binary search if we have an Ord instance or Ordering returning function. The+-- time complexity would then become (m x log n).++-- | Like 'cross' but emits only those tuples where @a == b@ using the+-- supplied equality predicate.+--+-- Definition:+--+-- >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2+--+-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if+-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more+-- space for caching the second stream.+--+-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused+-- alternative.+--+-- Time: O(m x n)+--+-- /Pre-release/+{-# INLINE joinInnerGeneric #-}+joinInnerGeneric :: Monad m =>+    (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)+joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2+{-+joinInnerGeneric eq s1 s2 = do+    -- ConcatMap works faster than bind+    Stream.concatMap (\a ->+        Stream.concatMap (\b ->+            if a `eq` b+            then Stream.fromPure (a, b)+            else Stream.nil+            ) s2+        ) s1+-}++-- | A more efficient 'joinInner' for sorted streams.+--+-- Space: O(1)+--+-- Time: O(m + n)+--+-- /Unimplemented/+{-# INLINE joinInnerAscBy #-}+joinInnerAscBy ::+    (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)+joinInnerAscBy = undefined++-- | A more efficient 'joinLeft' for sorted streams.+--+-- Space: O(1)+--+-- Time: O(m + n)+--+-- /Unimplemented/+{-# INLINE joinLeftAscBy #-}+joinLeftAscBy :: -- Monad m =>+    (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)+joinLeftAscBy _eq _s1 _s2 = undefined++-- | A more efficient 'joinOuter' for sorted streams.+--+-- Space: O(1)+--+-- Time: O(m + n)+--+-- /Unimplemented/+{-# INLINE joinOuterAscBy #-}+joinOuterAscBy :: -- Monad m =>+       (a -> b -> Ordering)+    -> Stream m a+    -> Stream m b+    -> Stream m (Maybe a, Maybe b)+joinOuterAscBy _eq _s1 _s2 = undefined++------------------------------------------------------------------------------+-- Set operations (special joins)+------------------------------------------------------------------------------+--+-- TODO: OrdSet/IntSet/hashmap based versions of these. With Eq only constraint+-- the best would be to use an Array with linear search. If the second stream+-- is sorted we can also use a binary search, using Ord constraint.++-- | Keep only those elements in the second stream that are present in the+-- first stream too. The first stream is folded to a container using the+-- supplied fold and then the elements in the container are looked up using the+-- supplied lookup function.+--+-- The first stream must be finite and must not block.+{-# INLINE filterStreamWith #-}+filterStreamWith :: Monad m =>+       Fold m a (f a)+    -> (a -> f a -> Bool)+    -> Stream m a+    -> Stream m a+    -> Stream m a+filterStreamWith fld member s1 s2 =+    Stream.concatEffect+        $ do+            xs <- Stream.fold fld s1+            return $ Stream.filter (`member` xs) s2++-- | 'filterInStreamGenericBy' retains only those elements in the second stream that+-- are present in the first stream.+--+-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])+-- [2,1,1]+--+-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])+-- [1,2,2]+--+-- Similar to the list intersectBy operation but with the stream argument order+-- flipped.+--+-- The first stream must be finite and must not block. Second stream is+-- processed only after the first stream is fully realized.+--+-- Space: O(n) where @n@ is the number of elements in the second stream.+--+-- Time: O(m x n) where @m@ is the number of elements in the first stream and+-- @n@ is the number of elements in the second stream.+--+-- /Pre-release/+{-# INLINE filterInStreamGenericBy #-}+filterInStreamGenericBy :: Monad m =>+    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+filterInStreamGenericBy eq =+    -- XXX Use an (unboxed) array instead.+    filterStreamWith+        (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)+        (List.any . eq)++-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in+-- ascending order. To use it on streams sorted in descending order pass an+-- inverted comparison function returning GT for less than and LT for greater+-- than.+--+-- Space: O(1)+--+-- Time: O(m+n)+--+-- /Pre-release/+{-# INLINE filterInStreamAscBy #-}+filterInStreamAscBy :: Monad m =>+    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1++-- | Delete all elements of the first stream from the seconds stream. If an+-- element occurs multiple times in the first stream as many occurrences of it+-- are deleted from the second stream.+--+-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])+-- [2]+--+-- The following laws hold:+--+-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2+-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2+--+-- Same as the list 'Data.List.//' operation but with argument order flipped.+--+-- The first stream must be finite and must not block. Second stream is+-- processed only after the first stream is fully realized.+--+-- Space: O(m) where @m@ is the number of elements in the first stream.+--+-- Time: O(m x n) where @m@ is the number of elements in the first stream and+-- @n@ is the number of elements in the second stream.+--+-- /Pre-release/+{-# INLINE deleteInStreamGenericBy #-}+deleteInStreamGenericBy :: Monad m =>+    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+deleteInStreamGenericBy eq s1 s2 =+    Stream.concatEffect+        $ do+            -- This may work well if s1 is small+            -- If s1 is big we can go through s1, deleting elements from s2 and+            -- not emitting an element if it was successfully deleted from s2.+            -- we will need a deleteBy that can return whether the element was+            -- deleted or not.+            xs <- Stream.fold Fold.toList s2+            let f = Fold.foldl' (flip (List.deleteBy eq)) xs+            fmap Stream.fromList $ Stream.fold f s1++-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.+--+-- Space: O(1)+--+-- /Unimplemented/+{-# INLINE deleteInStreamAscBy #-}+deleteInStreamAscBy :: -- (Monad m) =>+    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+deleteInStreamAscBy _eq _s1 _s2 = undefined++-- XXX Remove the MonadIO constraint. We can just cache one stream and then+-- implement using differenceEqBy.++-- | This essentially appends to the second stream all the occurrences of+-- elements in the first stream that are not already present in the second+-- stream.+--+-- Equivalent to the following except that @s2@ is evaluated only once:+--+-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)+--+-- Example:+--+-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])+-- [1,2,2,4,3]+--+-- Space: O(n)+--+-- Time: O(m x n)+--+-- /Pre-release/+{-# INLINE unionWithStreamGenericBy #-}+unionWithStreamGenericBy :: MonadIO m =>+    (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a+unionWithStreamGenericBy eq s1 s2 =+    Stream.concatEffect+        $ do+            xs <- Stream.fold Fold.toList  s1+            -- XXX we can use postscanlMAfter' instead of IORef+            ref <- liftIO $ newIORef $! List.nubBy eq xs+            let f x = do+                    liftIO $ modifyIORef' ref (List.deleteBy eq x)+                    return x+                s3 = Stream.concatEffect+                        $ do+                            xs1 <- liftIO $ readIORef ref+                            return $ Stream.fromList xs1+            return $ Stream.mapM f s2 `Stream.append` s3++-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.+--+-- Space: O(1)+--+-- /Unimplemented/+{-# INLINE unionWithStreamAscBy #-}+unionWithStreamAscBy :: -- (Monad m) =>+    (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a+unionWithStreamAscBy _eq _s1 _s2 = undefined
src/Streamly/Internal/Data/Stream/Transform.hs view
@@ -1,1056 +1,1945 @@--- |--- Module      : Streamly.Internal.Data.Stream.Transform--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC--module Streamly.Internal.Data.Stream.Transform-    (-    -- * Piping-    -- | Pass through a 'Pipe'.-      transform--    -- * Folding-    , foldrS--    -- * Mapping-    -- | Stateless one-to-one maps.-    , sequence-    , mapM--    -- * Mapping Side Effects (Observation)-    -- | See also the intersperse*_ combinators.-    , trace-    , trace_-    , tap--    -- * Scanning-    , scan-    , scanMany-    , postscan-    , smapM-    , scanlMAfter'--    -- * Filtering-    -- | Produce a subset of the stream using criteria based on the values of-    -- the elements. We can use a concatMap and scan for filtering but these-    -- combinators are more efficient and convenient.--    -- mapMaybeM is a general filtering combinator as we can map the stream to-    -- Just/Nothing using any stateful fold and then use this to filter out.-    , mapMaybeM-    , mapMaybe-    , catMaybes-    , scanMaybe--    , with-    , deleteBy-    , filter-    , filterM--    -- Stateful/scanning filters-    , uniq-    , uniqBy-    , prune-    , repeated--    -- * Trimming-    -- | Produce a subset of the stream trimmed at ends.--    , take-    , takeWhile-    , takeWhileM-    , takeWhileLast-    , takeWhileAround-    , drop-    , dropLast-    , dropWhile-    , dropWhileM-    , dropWhileLast-    , dropWhileAround--    -- * Position Indexing-    , indexed-    , indexedR--      -- * Time Indexing-    , timestamped-    , timestampWith-    , timeIndexed-    , timeIndexWith--    -- * Searching-    , findIndices -- XXX indicesBy-    , elemIndices -- XXX indicesOf--    -- * Rolling map-    -- | Map using the previous element.-    , rollingMapM-    , rollingMap-    , rollingMap2--    -- Merge--    -- * Inserting Elements-    -- | Produce a superset of the stream. This is the opposite of-    -- filtering/sampling.  We can always use concatMap and scan for inserting-    -- but these combinators are more efficient and convenient.--    -- Element agnostic (Opposite of sampling)-    , intersperse-    , intersperseM -- XXX naming-    , intersperseMWith--    , intersperseMSuffix-    , intersperseMSuffixWith--    -- , interspersePrefix-    -- , interspersePrefixBySpan--    -- * Inserting Side Effects/Time-    , intersperseM_ -- XXX naming-    , delay-    , intersperseMSuffix_-    , delayPost-    , intersperseMPrefix_-    , delayPre--    -- * Element Aware Insertion-    -- | Opposite of filtering-    , insertBy-    -- , intersperseByBefore-    -- , intersperseByAfter--    -- Fold and Unfold, Buffering--    -- * Reordering-    , reverse-    , reverse'-    , reassembleBy--    -- * Either Streams-    -- Move these to Streamly.Data.Either.Stream?-    , catLefts-    , catRights-    , catEithers-    )-where--#include "inline.hs"--import Control.Concurrent (threadDelay)-import Control.Monad (void)-import Control.Monad.IO.Class (MonadIO (liftIO))-import Data.Either (fromLeft, isLeft, isRight, fromRight)-import Data.Maybe (isJust, fromJust)--import Streamly.Internal.Data.Fold.Type (Fold)-import Streamly.Internal.Data.Pipe (Pipe)-import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64)--import qualified Streamly.Internal.Data.Fold as FL--- import qualified Streamly.Internal.Data.Fold.Window as Window-import qualified Streamly.Internal.Data.Stream.StreamD.Transform as D-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K---import Streamly.Internal.Data.Stream.Bottom-import Streamly.Internal.Data.Stream.Type--import Prelude hiding-       ( filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence-       , reverse, foldr1 , repeat, scanl, scanl1, zipWith)------- $setup--- >>> :m--- >>> import Control.Concurrent (threadDelay)--- >>> import Control.Monad (void)--- >>> import Control.Monad.IO.Class (MonadIO (liftIO))--- >>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)--- >>> import Data.Function ((&))--- >>> import Data.Maybe (fromJust, isJust)--- >>> import Prelude hiding (filter, drop, dropWhile, take, takeWhile, foldr, map, mapM, sequence, reverse, foldr1 , scanl, scanl1)--- >>> import Streamly.Internal.Data.Stream (Stream)--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Fold as Fold (filtering)--- >>> import qualified Streamly.Internal.Data.Fold.Window as Window--- >>> import qualified Streamly.Internal.Data.Stream as Stream--- >>> import System.IO (stdout, hSetBuffering, BufferMode(LineBuffering))------ >>> hSetBuffering stdout LineBuffering---- XXX because of the use of D.cons for appending, folds and scans have--- quadratic complexity when iterated over a stream. We should use StreamK for--- linear performance on iteration.----------------------------------------------------------------------------------- Piping----------------------------------------------------------------------------------- | Use a 'Pipe' to transform a stream.------ /Pre-release/----{-# INLINE transform #-}-transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b-transform pipe xs = fromStreamD $ D.transform pipe (toStreamD xs)----------------------------------------------------------------------------------- Transformation Folds----------------------------------------------------------------------------------- | Right fold to a streaming monad.------ > foldrS Stream.cons Stream.nil === id------ 'foldrS' can be used to perform stateless stream to stream transformations--- like map and filter in general. It can be coupled with a scan to perform--- stateful transformations. However, note that the custom map and filter--- routines can be much more efficient than this due to better stream fusion.------ >>> input = Stream.fromList [1..5]--- >>> Stream.fold Fold.toList $ Stream.foldrS Stream.cons Stream.nil input--- [1,2,3,4,5]------ Find if any element in the stream is 'True':------ >>> step x xs = if odd x then Stream.fromPure True else xs--- >>> input = Stream.fromList (2:4:5:undefined) :: Stream IO Int--- >>> Stream.fold Fold.toList $ Stream.foldrS step (Stream.fromPure False) input--- [True]------ Map (+2) on odd elements and filter out the even elements:------ >>> step x xs = if odd x then (x + 2) `Stream.cons` xs else xs--- >>> input = Stream.fromList [1..5] :: Stream IO Int--- >>> Stream.fold Fold.toList $ Stream.foldrS step Stream.nil input--- [3,5,7]------ /Pre-release/-{-# INLINE foldrS #-}-foldrS ::-     (a -> Stream m b -> Stream m b)-  -> Stream m b-  -> Stream m a-  -> Stream m b-foldrS f z xs =-    fromStreamK-        $ K.foldrS-            (\y ys -> toStreamK $ f y (fromStreamK ys))-            (toStreamK z)-            (toStreamK xs)----------------------------------------------------------------------------------- Transformation by Mapping----------------------------------------------------------------------------------- |--- >>> mapM f = Stream.sequence . fmap f------ Apply a monadic function to each element of the stream and replace it with--- the output of the resulting action.------ >>> s = Stream.fromList ["a", "b", "c"]--- >>> Stream.fold Fold.drain $ Stream.mapM putStr s--- abc----{-# INLINE mapM #-}-mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b-mapM f m = fromStreamK $ D.toStreamK $ D.mapM f $ toStreamD m---- |--- >>> sequence = Stream.mapM id------ Replace the elements of a stream of monadic actions with the outputs of--- those actions.------ >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]--- >>> Stream.fold Fold.drain $ Stream.sequence s--- abc----{-# INLINE sequence #-}-sequence :: Monad m => Stream m (m a) -> Stream m a-sequence = mapM id----------------------------------------------------------------------------------- Mapping side effects----------------------------------------------------------------------------------- | Tap the data flowing through a stream into a 'Fold'. For example, you may--- add a tap to log the contents flowing through the stream. The fold is used--- only for effects, its result is discarded.------ @---                   Fold m a b---                       |--- -----stream m a ---------------stream m a----------- @------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainMapM print) s--- 1--- 2------ Compare with 'trace'.----{-# INLINE tap #-}-tap :: Monad m => FL.Fold m a b -> Stream m a -> Stream m a-tap f xs = fromStreamD $ D.tap f (toStreamD xs)---- | Apply a monadic function to each element flowing through the stream and--- discard the results.------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.trace print s--- 1--- 2------ Compare with 'tap'.----{-# INLINE trace #-}-trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a-trace f = mapM (\x -> void (f x) >> return x)---- | Perform a side effect before yielding each element of the stream and--- discard the results.------ >>> s = Stream.enumerateFromTo 1 2--- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s--- "got here"--- "got here"------ Same as 'intersperseMPrefix_' but always serial.------ See also: 'trace'------ /Pre-release/-{-# INLINE trace_ #-}-trace_ :: Monad m => m b -> Stream m a -> Stream m a-trace_ eff = fromStreamD . D.mapM (\x -> eff >> return x) . toStreamD------------------------------------------------------------------------------------ Scanning------------------------------------------------------------------------------------ | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except--- that it provides an additional @done@ function to be applied on the--- accumulator when the stream stops. The result of @done@ is also emitted in--- the stream.------ This function can be used to allocate a resource in the beginning of the--- scan and release it when the stream ends or to flush the internal state of--- the scan at the end.------ /Pre-release/----{-# INLINE scanlMAfter' #-}-scanlMAfter' ::-       Monad m-    => (b -> a -> m b)-    -> m b-    -> (b -> m b)-    -> Stream m a-    -> Stream m b-scanlMAfter' step initial done stream =-    fromStreamD $ D.scanlMAfter' step initial done $ toStreamD stream----------------------------------------------------------------------------------- Scanning with a Fold----------------------------------------------------------------------------------- XXX It may be useful to have a version of scan where we can keep the--- accumulator independent of the value emitted. So that we do not necessarily--- have to keep a value in the accumulator which we are not using. We can pass--- an extraction function that will take the accumulator and the current value--- of the element and emit the next value in the stream. That will also make it--- possible to modify the accumulator after using it. In fact, the step function--- can return new accumulator and the value to be emitted. The signature would--- be more like mapAccumL.---- | Strict left scan. Scan a stream using the given monadic fold.------ >>> s = Stream.fromList [1..10]--- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s--- [0,1,3,6]------ See also: 'usingStateT'------- EXPLANATION:--- >>> scanl' step z = Stream.scan (Fold.foldl' step z)------ Like 'map', 'scanl'' too is a one to one transformation,--- however it adds an extra element.------ >>> s = Stream.fromList [1,2,3,4]--- >>> Stream.fold Fold.toList $ scanl' (+) 0 s--- [0,1,3,6,10]------ >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s--- [[],[1],[2,1],[3,2,1],[4,3,2,1]]------ The output of 'scanl'' is the initial value of the accumulator followed by--- all the intermediate steps and the final result of 'foldl''.------ By streaming the accumulated state after each fold step, we can share the--- state across multiple stages of stream composition. Each stage can modify or--- extend the state, do some processing with it and emit it for the next stage,--- thus modularizing the stream processing. This can be useful in--- stateful or event-driven programming.------ Consider the following monolithic example, computing the sum and the product--- of the elements in a stream in one go using a @foldl'@:------ >>> foldl' step z = Stream.fold (Fold.foldl' step z)--- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s--- (10,24)------ Using @scanl'@ we can make it modular by computing the sum in the first--- stage and passing it down to the next stage for computing the product:------ >>> :{---   foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)---   $ scanl' (\(s, _) x -> (s + x, x)) (0,1)---   $ Stream.fromList [1,2,3,4]--- :}--- (10,24)------ IMPORTANT: 'scanl'' evaluates the accumulator to WHNF.  To avoid building--- lazy expressions inside the accumulator, it is recommended that a strict--- data structure is used for accumulator.----{-# INLINE scan #-}-scan :: Monad m => Fold m a b -> Stream m a -> Stream m b-scan fld m = fromStreamD $ D.scan fld $ toStreamD m---- | Like 'scan' but restarts scanning afresh when the scanning fold--- terminates.----{-# INLINE scanMany #-}-scanMany :: Monad m => Fold m a b -> Stream m a -> Stream m b-scanMany fld m = fromStreamD $ D.scanMany fld $ toStreamD m----------------------------------------------------------------------------------- Filtering----------------------------------------------------------------------------------- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a--- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a--- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with--- index or time.------ >>> filterWithIndex = Stream.with Stream.indexed Stream.filter------ /Pre-release/-{-# INLINE with #-}-with :: Monad m =>-       (Stream m a -> Stream m (s, a))-    -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))-    -> (((s, a) -> b) -> Stream m a -> Stream m a)-with f comb g = fmap snd . comb g . f---- | Include only those elements that pass a predicate.------ >>> filter p = Stream.filterM (return . p)--- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)--- >>> filter p = Stream.scanMaybe (Fold.filtering p)----{-# INLINE filter #-}-filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a--- filter p = scanMaybe (FL.filtering p)-filter p m = fromStreamD $ D.filter p $ toStreamD m---- | Same as 'filter' but with a monadic predicate.------ >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing--- >>> filterM p = Stream.mapMaybeM (f p)----{-# INLINE filterM #-}-filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a-filterM p m = fromStreamD $ D.filterM p $ toStreamD m---- | Drop repeated elements that are adjacent to each other using the supplied--- comparison function.------ >>> uniq = Stream.uniqBy (==)------ To strip duplicate path separators:------ >>> input = Stream.fromList "//a//b"--- >>> f x y = x == '/' && y == '/'--- >>> Stream.fold Fold.toList $ Stream.uniqBy f input--- "/a/b"------ Space: @O(1)@------ /Pre-release/----{-# INLINE uniqBy #-}-uniqBy :: Monad m =>-    (a -> a -> Bool) -> Stream m a -> Stream m a--- uniqBy eq = scanMaybe (FL.uniqBy eq)-uniqBy eq = catMaybes . rollingMap f--    where--    f pre curr =-        case pre of-            Nothing -> Just curr-            Just x -> if x `eq` curr then Nothing else Just curr---- | Drop repeated elements that are adjacent to each other.------ >>> uniq = Stream.uniqBy (==)----{-# INLINE uniq #-}-uniq :: (Eq a, Monad m) => Stream m a -> Stream m a--- uniq = scanMaybe FL.uniq-uniq = fromStreamD . D.uniq . toStreamD---- | Strip all leading and trailing occurrences of an element passing a--- predicate and make all other consecutive occurrences uniq.------ >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)------ @--- > Stream.prune isSpace (Stream.fromList "  hello      world!   ")--- "hello world!"------ @------ Space: @O(1)@------ /Unimplemented/-{-# INLINE prune #-}-prune ::-    -- (Monad m, Eq a) =>-    (a -> Bool) -> Stream m a -> Stream m a-prune = error "Not implemented yet!"---- Possible implementation:--- @repeated =---      Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@------ 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a--- single element.---- | Emit only repeated elements, once.------ /Unimplemented/-repeated :: -- (Monad m, Eq a) =>-    Stream m a -> Stream m a-repeated = undefined---- | Deletes the first occurrence of the element in the stream that satisfies--- the given equality predicate.------ >>> input = Stream.fromList [1,3,3,5]--- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input--- [1,3,5]----{-# INLINE deleteBy #-}-deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a--- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)-deleteBy cmp x m = fromStreamD $ D.deleteBy cmp x (toStreamD m)----------------------------------------------------------------------------------- Trimming----------------------------------------------------------------------------------- | Same as 'takeWhile' but with a monadic predicate.----{-# INLINE takeWhileM #-}-takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a--- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))-takeWhileM p m = fromStreamD $ D.takeWhileM p $ toStreamD m---- | Take all consecutive elements at the end of the stream for which the--- predicate is true.------ O(n) space, where n is the number elements taken.------ /Unimplemented/-{-# INLINE takeWhileLast #-}-takeWhileLast :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m---- | Like 'takeWhile' and 'takeWhileLast' combined.------ O(n) space, where n is the number elements taken from the end.------ /Unimplemented/-{-# INLINE takeWhileAround #-}-takeWhileAround :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m---- | Drop elements in the stream as long as the predicate succeeds and then--- take the rest of the stream.----{-# INLINE dropWhile #-}-dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a--- dropWhile p = scanMaybe (FL.droppingWhile p)-dropWhile p m = fromStreamD $ D.dropWhile p $ toStreamD m---- | Same as 'dropWhile' but with a monadic predicate.----{-# INLINE dropWhileM #-}-dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a--- dropWhileM p = scanMaybe (FL.droppingWhileM p)-dropWhileM p m = fromStreamD $ D.dropWhileM p $ toStreamD m---- | Drop @n@ elements at the end of the stream.------ O(n) space, where n is the number elements dropped.------ /Unimplemented/-{-# INLINE dropLast #-}-dropLast :: -- Monad m =>-    Int -> Stream m a -> Stream m a-dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m---- | Drop all consecutive elements at the end of the stream for which the--- predicate is true.------ O(n) space, where n is the number elements dropped.------ /Unimplemented/-{-# INLINE dropWhileLast #-}-dropWhileLast :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m---- | Like 'dropWhile' and 'dropWhileLast' combined.------ O(n) space, where n is the number elements dropped from the end.------ /Unimplemented/-{-# INLINE dropWhileAround #-}-dropWhileAround :: -- Monad m =>-    (a -> Bool) -> Stream m a -> Stream m a-dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m----------------------------------------------------------------------------------- Inserting Elements----------------------------------------------------------------------------------- | @insertBy cmp elem stream@ inserts @elem@ before the first element in--- @stream@ that is less than @elem@ when compared using @cmp@.------ >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)------ >>> input = Stream.fromList [1,3,5]--- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input--- [1,2,3,5]----{-# INLINE insertBy #-}-insertBy ::Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a-insertBy cmp x m = fromStreamD $ D.insertBy cmp x (toStreamD m)---- | Insert a pure value between successive elements of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input--- "h,e,l,l,o"----{-# INLINE intersperse #-}-intersperse :: Monad m => a -> Stream m a -> Stream m a-intersperse a = fromStreamD . D.intersperse a . toStreamD---- | Insert a side effect before consuming an element of a stream except the--- first one.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input--- h.e.l.l.o------ /Pre-release/-{-# INLINE intersperseM_ #-}-intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseM_ m = fromStreamD . D.intersperseM_ m . toStreamD---- | Intersperse a monadic action into the input stream after every @n@--- elements.------ >> input = Stream.fromList "hello"--- >> Stream.fold Fold.toList $ Stream.intersperseMWith 2 (return ',') input--- "he,ll,o"------ /Unimplemented/-{-# INLINE intersperseMWith #-}-intersperseMWith :: -- Monad m =>-    Int -> m a -> Stream m a -> Stream m a-intersperseMWith _n _f _xs = undefined---- | Insert an effect and its output after consuming an element of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMSuffix (putChar '.' >> return ',') input--- h.,e.,l.,l.,o.,"h,e,l,l,o,"------ /Pre-release/-{-# INLINE intersperseMSuffix #-}-intersperseMSuffix :: Monad m => m a -> Stream m a -> Stream m a-intersperseMSuffix m = fromStreamD . D.intersperseMSuffix m . toStreamD---- | Insert a side effect after consuming an element of a stream.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffix_ (threadDelay 1000000) input--- "hello"------ /Pre-release/----{-# INLINE intersperseMSuffix_ #-}-intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseMSuffix_ m = fromStreamD . D.intersperseMSuffix_ m . toStreamD---- XXX Use an offset argument, like tapOffsetEvery---- | Like 'intersperseMSuffix' but intersperses an effectful action into the--- input stream after every @n@ elements and after the last element.------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffixWith 2 (return ',') input--- "he,ll,o,"------ /Pre-release/----{-# INLINE intersperseMSuffixWith #-}-intersperseMSuffixWith :: Monad m-    => Int -> m a -> Stream m a -> Stream m a-intersperseMSuffixWith n eff =-    fromStreamD . D.intersperseMSuffixWith n eff . toStreamD---- | Insert a side effect before consuming an element of a stream.------ Definition:------ >>> intersperseMPrefix_ m = Stream.mapM (\x -> void m >> return x)------ >>> input = Stream.fromList "hello"--- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMPrefix_ (putChar '.' >> return ',') input--- .h.e.l.l.o"hello"------ Same as 'trace_'.------ /Pre-release/----{-# INLINE intersperseMPrefix_ #-}-intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a-intersperseMPrefix_ m = mapM (\x -> void m >> return x)----------------------------------------------------------------------------------- Inserting Time----------------------------------------------------------------------------------- XXX This should be in Prelude, should we export this as a helper function?---- | Block the current thread for specified number of seconds.-{-# INLINE sleep #-}-sleep :: MonadIO m => Double -> m ()-sleep n = liftIO $ threadDelay $ round $ n * 1000000---- | Introduce a delay of specified seconds between elements of the stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delay = Stream.intersperseM_ . sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delay 1 input--- 1--- 2--- 3----{-# INLINE delay #-}-delay :: MonadIO m => Double -> Stream m a -> Stream m a-delay = intersperseM_ . sleep---- | Introduce a delay of specified seconds after consuming an element of a--- stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delayPost = Stream.intersperseMSuffix_ . sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPost 1 input--- 1--- 2--- 3------ /Pre-release/----{-# INLINE delayPost #-}-delayPost :: MonadIO m => Double -> Stream m a -> Stream m a-delayPost n = intersperseMSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000---- | Introduce a delay of specified seconds before consuming an element of a--- stream.------ Definition:------ >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000--- >>> delayPre = Stream.intersperseMPrefix_. sleep------ Example:------ >>> input = Stream.enumerateFromTo 1 3--- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPre 1 input--- 1--- 2--- 3------ /Pre-release/----{-# INLINE delayPre #-}-delayPre :: MonadIO m => Double -> Stream m a -> Stream m a-delayPre = intersperseMPrefix_. sleep----------------------------------------------------------------------------------- Reorder in sequence----------------------------------------------------------------------------------- | Buffer until the next element in sequence arrives. The function argument--- determines the difference in sequence numbers. This could be useful in--- implementing sequenced streams, for example, TCP reassembly.------ /Unimplemented/----{-# INLINE reassembleBy #-}-reassembleBy-    :: -- Monad m =>-       Fold m a b-    -> (a -> a -> Int)-    -> Stream m a-    -> Stream m b-reassembleBy = undefined----------------------------------------------------------------------------------- Position Indexing----------------------------------------------------------------------------------- |--- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)--- >>> indexed = Stream.postscan f--- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)--- >>> indexedR n = fmap (\(i, a) -> (n - i, a)) . indexed------ Pair each element in a stream with its index, starting from index 0.------ >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"--- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]----{-# INLINE indexed #-}-indexed :: Monad m => Stream m a -> Stream m (Int, a)--- indexed = scanMaybe FL.indexing-indexed = fromStreamD . D.indexed . toStreamD---- |--- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)--- >>> indexedR n = Stream.postscan (f n)------ >>> s n = Stream.enumerateFromThen n (n - 1)--- >>> indexedR n = Stream.zipWith (,) (s n)------ Pair each element in a stream with its index, starting from the--- given index @n@ and counting down.------ >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"--- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]----{-# INLINE indexedR #-}-indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)--- indexedR n = scanMaybe (FL.indexingRev n)-indexedR n = fromStreamD . D.indexedR n . toStreamD------------------------------------------------------------------------------------ Time Indexing------------------------------------------------------------------------------------ Note: The timestamp stream must be the second stream in the zip so that the--- timestamp is generated after generating the stream element and not before.--- If we do not do that then the following example will generate the same--- timestamp for first two elements:------ Stream.fold Fold.toList $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3------ | Pair each element in a stream with an absolute timestamp, using a clock of--- specified granularity.  The timestamp is generated just before the element--- is consumed.------ >>> Stream.fold Fold.toList $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(AbsTime (TimeSpec {sec = ..., nsec = ...}),1),(AbsTime (TimeSpec {sec = ..., nsec = ...}),2),(AbsTime (TimeSpec {sec = ..., nsec = ...}),3)]------ /Pre-release/----{-# INLINE timestampWith #-}-timestampWith :: (MonadIO m)-    => Double -> Stream m a -> Stream m (AbsTime, a)-timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)---- TBD: check performance vs a custom implementation without using zipWith.------ /Pre-release/----{-# INLINE timestamped #-}-timestamped :: (MonadIO m)-    => Stream m a -> Stream m (AbsTime, a)-timestamped = timestampWith 0.01---- | Pair each element in a stream with relative times starting from 0, using a--- clock with the specified granularity. The time is measured just before the--- element is consumed.------ >>> Stream.fold Fold.toList $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]------ /Pre-release/Monad----{-# INLINE timeIndexWith #-}-timeIndexWith :: (MonadIO m)-    => Double -> Stream m a -> Stream m (RelTime64, a)-timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)---- | Pair each element in a stream with relative times starting from 0, using a--- 10 ms granularity clock. The time is measured just before the element is--- consumed.------ >>> Stream.fold Fold.toList $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3--- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]------ /Pre-release/----{-# INLINE timeIndexed #-}-timeIndexed :: (MonadIO m)-    => Stream m a -> Stream m (RelTime64, a)-timeIndexed = timeIndexWith 0.01----------------------------------------------------------------------------------- Searching----------------------------------------------------------------------------------- | Find all the indices where the value of the element in the stream is equal--- to the given value.------ >>> elemIndices a = Stream.findIndices (== a)----{-# INLINE elemIndices #-}-elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int-elemIndices a = findIndices (== a)----------------------------------------------------------------------------------- Rolling map----------------------------------------------------------------------------------- XXX this is not a one-to-one map so calling it map may not be right.--- We can perhaps call it zipWithTail or rollWith.---- | Apply a function on every two successive elements of a stream. The first--- argument of the map function is the previous element and the second argument--- is the current element. When the current element is the first element, the--- previous element is 'Nothing'.------ /Pre-release/----{-# INLINE rollingMap #-}-rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b--- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)-rollingMap f m = fromStreamD $ D.rollingMap f $ toStreamD m---- | Like 'rollingMap' but with an effectful map function.------ /Pre-release/----{-# INLINE rollingMapM #-}-rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b--- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)-rollingMapM f m = fromStreamD $ D.rollingMapM f $ toStreamD m---- | Like 'rollingMap' but requires at least two elements in the stream,--- returns an empty stream otherwise.------ This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.------ /Pre-release/----{-# INLINE rollingMap2 #-}-rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b-rollingMap2 f m = fromStreamD $ D.rollingMap2 f $ toStreamD m----------------------------------------------------------------------------------- Maybe Streams----------------------------------------------------------------------------------- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'--- elements, and return a stream of values extracted from 'Just'.------ Equivalent to:------ >>> mapMaybe f = Stream.catMaybes . fmap f----{-# INLINE mapMaybe #-}-mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b-mapMaybe f m = fromStreamD $ D.mapMaybe f $ toStreamD m---- | Like 'mapMaybe' but maps a monadic function.------ Equivalent to:------ >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f------ >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)----{-# INLINE_EARLY mapMaybeM #-}-mapMaybeM :: Monad m-          => (a -> m (Maybe b)) -> Stream m a -> Stream m b-mapMaybeM f = fmap fromJust . filter isJust . mapM f----------------------------------------------------------------------------------- Either streams----------------------------------------------------------------------------------- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.------ >>> catLefts = fmap (fromLeft undefined) . Stream.filter isLeft------ /Pre-release/----{-# INLINE catLefts #-}-catLefts :: Monad m => Stream m (Either a b) -> Stream m a-catLefts = fmap (fromLeft undefined) . filter isLeft---- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.------ >>> catRights = fmap (fromRight undefined) . Stream.filter isRight------ /Pre-release/----{-# INLINE catRights #-}-catRights :: Monad m => Stream m (Either a b) -> Stream m b-catRights = fmap (fromRight undefined) . filter isRight---- | Remove the either wrapper and flatten both lefts and as well as rights in--- the output stream.------ >>> catEithers = fmap (either id id)------ /Pre-release/----{-# INLINE catEithers #-}-catEithers :: Monad m => Stream m (Either a a) -> Stream m a-catEithers = fmap (either id id)+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.Stream.Transform+-- Copyright   : (c) 2018 Composewell Technologies+--               (c) Roman Leshchinskiy 2008-2010+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- "Streamly.Internal.Data.Pipe" might ultimately replace this module.++-- A few functions in this module have been adapted from the vector package+-- (c) Roman Leshchinskiy. See the notes in specific combinators.++module Streamly.Internal.Data.Stream.Transform+    (+    -- * Piping+    -- | Pass through a 'Pipe'.+      transform++    -- * Mapping+    -- | Stateless one-to-one maps.+    , map+    , mapM+    , sequence++    -- * Mapping Effects+    , tap+    , tapOffsetEvery+    , trace+    , trace_++    -- * Folding+    , foldrS+    , foldlS++    -- * Scanning By 'Fold'+    , postscan+    , scan+    , scanMany++    -- * Splitting+    , splitOn++    -- * Scanning+    -- | Left scans. Stateful, mostly one-to-one maps.+    , scanlM'+    , scanlMAfter'+    , scanl'+    , scanlM+    , scanl+    , scanl1M'+    , scanl1'+    , scanl1M+    , scanl1++    , prescanl'+    , prescanlM'++    , postscanl+    , postscanlM+    , postscanl'+    , postscanlM'+    , postscanlMAfter'++    , postscanlx'+    , postscanlMx'+    , scanlMx'+    , scanlx'++    -- * Filtering+    -- | Produce a subset of the stream.+    , with+    , scanMaybe+    , filter+    , filterM+    , deleteBy+    , uniqBy+    , uniq+    , prune+    , repeated++    -- * Trimming+    -- | Produce a subset of the stream trimmed at ends.+    , take+    , takeWhile+    , takeWhileM+    , takeWhileLast+    , takeWhileAround+    , drop+    , dropWhile+    , dropWhileM+    , dropLast+    , dropWhileLast+    , dropWhileAround++    -- * Inserting Elements+    -- | Produce a superset of the stream.+    , insertBy+    , intersperse+    , intersperseM+    , intersperseMWith+    , intersperseMSuffix+    , intersperseMSuffixWith++    -- * Inserting Side Effects+    , intersperseM_+    , intersperseMSuffix_+    , intersperseMPrefix_++    , delay+    , delayPre+    , delayPost++    -- * Reordering+    -- | Produce strictly the same set but reordered.+    , reverse+    , reverseUnbox+    , reassembleBy++    -- * Position Indexing+    , indexed+    , indexedR++    -- * Time Indexing+    , timestampWith+    , timestamped+    , timeIndexWith+    , timeIndexed++    -- * Searching+    , findIndices+    , elemIndices+    , slicesBy++    -- * Rolling map+    -- | Map using the previous element.+    , rollingMap+    , rollingMapM+    , rollingMap2++    -- * Maybe Streams+    , mapMaybe+    , mapMaybeM+    , catMaybes++    -- * Either Streams+    , catLefts+    , catRights+    , catEithers+    )+where++#include "inline.hs"++import Control.Concurrent (threadDelay)+import Control.Monad (void)+import Control.Monad.IO.Class (MonadIO (liftIO))+import Data.Either (fromLeft, isLeft, isRight, fromRight)+import Data.Functor ((<&>))+import Data.Maybe (fromJust, isJust)+import Fusion.Plugin.Types (Fuse(..))++import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Pipe.Type (Pipe(..), PipeState(..))+import Streamly.Internal.Data.SVar.Type (adaptState)+import Streamly.Internal.Data.Time.Units (AbsTime, RelTime64)+import Streamly.Internal.Data.Unbox (Unbox)+import Streamly.Internal.System.IO (defaultChunkSize)++-- import qualified Data.List as List+import qualified Streamly.Internal.Data.Array.Type as A+import qualified Streamly.Internal.Data.Fold as FL+import qualified Streamly.Internal.Data.Pipe.Type as Pipe+import qualified Streamly.Internal.Data.StreamK.Type as K++import Prelude hiding+       ( drop, dropWhile, filter, map, mapM, reverse+       , scanl, scanl1, sequence, take, takeWhile, zipWith)++import Streamly.Internal.Data.Stream.Generate+    (absTimesWith, relTimesWith)+import Streamly.Internal.Data.Stream.Type++#include "DocTestDataStream.hs"++------------------------------------------------------------------------------+-- Piping+------------------------------------------------------------------------------++-- | Use a 'Pipe' to transform a stream.+--+-- /Pre-release/+--+{-# INLINE_NORMAL transform #-}+transform :: Monad m => Pipe m a b -> Stream m a -> Stream m b+transform (Pipe pstep1 pstep2 pstate) (Stream step state) =+    Stream step' (Consume pstate, state)++  where++    {-# INLINE_LATE step' #-}++    step' gst (Consume pst, st) = pst `seq` do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                res <- pstep1 pst x+                case res of+                    Pipe.Yield b pst' -> return $ Yield b (pst', s)+                    Pipe.Continue pst' -> return $ Skip (pst', s)+            Skip s -> return $ Skip (Consume pst, s)+            Stop   -> return Stop++    step' _ (Produce pst, st) = pst `seq` do+        res <- pstep2 pst+        case res of+            Pipe.Yield b pst' -> return $ Yield b (pst', st)+            Pipe.Continue pst' -> return $ Skip (pst', st)++------------------------------------------------------------------------------+-- Transformation Folds+------------------------------------------------------------------------------++-- Note, this is going to have horrible performance, because of the nature of+-- the stream type (i.e. direct stream vs CPS). Its only for reference, it is+-- likely be practically unusable.+{-# INLINE_NORMAL foldlS #-}+foldlS :: Monad m+    => (Stream m b -> a -> Stream m b) -> Stream m b -> Stream m a -> Stream m b+foldlS fstep begin (Stream step state) = Stream step' (Left (state, begin))+  where+    step' gst (Left (st, acc)) = do+        r <- step (adaptState gst) st+        return $ case r of+            Yield x s -> Skip (Left (s, fstep acc x))+            Skip s -> Skip (Left (s, acc))+            Stop   -> Skip (Right acc)++    step' gst (Right (Stream stp stt)) = do+        r <- stp (adaptState gst) stt+        return $ case r of+            Yield x s -> Yield x (Right (Stream stp s))+            Skip s -> Skip (Right (Stream stp s))+            Stop   -> Stop++------------------------------------------------------------------------------+-- Transformation by Mapping+------------------------------------------------------------------------------++-- |+-- >>> sequence = Stream.mapM id+--+-- Replace the elements of a stream of monadic actions with the outputs of+-- those actions.+--+-- >>> s = Stream.fromList [putStr "a", putStr "b", putStrLn "c"]+-- >>> Stream.fold Fold.drain $ Stream.sequence s+-- abc+--+{-# INLINE_NORMAL sequence #-}+sequence :: Monad m => Stream m (m a) -> Stream m a+sequence (Stream step state) = Stream step' state+  where+    {-# INLINE_LATE step' #-}+    step' gst st = do+         r <- step (adaptState gst) st+         case r of+             Yield x s -> x >>= \a -> return (Yield a s)+             Skip s    -> return $ Skip s+             Stop      -> return Stop++------------------------------------------------------------------------------+-- Mapping side effects+------------------------------------------------------------------------------++data TapState fs st a+    = TapInit | Tapping !fs st | TapDone st++-- XXX Multiple yield points++-- | Tap the data flowing through a stream into a 'Fold'. For example, you may+-- add a tap to log the contents flowing through the stream. The fold is used+-- only for effects, its result is discarded.+--+-- @+--                   Fold m a b+--                       |+-- -----stream m a ---------------stream m a-----+--+-- @+--+-- >>> s = Stream.enumerateFromTo 1 2+-- >>> Stream.fold Fold.drain $ Stream.tap (Fold.drainMapM print) s+-- 1+-- 2+--+-- Compare with 'trace'.+--+{-# INLINE tap #-}+tap :: Monad m => Fold m a b -> Stream m a -> Stream m a+tap (Fold fstep initial _ final) (Stream step state) = Stream step' TapInit++    where++    step' _ TapInit = do+        res <- initial+        return+            $ Skip+            $ case res of+                  FL.Partial s -> Tapping s state+                  FL.Done _ -> TapDone state+    step' gst (Tapping acc st) = do+        r <- step gst st+        case r of+            Yield x s -> do+                res <- fstep acc x+                return+                    $ Yield x+                    $ case res of+                          FL.Partial fs -> Tapping fs s+                          FL.Done _ -> TapDone s+            Skip s -> return $ Skip (Tapping acc s)+            Stop -> do+                void $ final acc+                return Stop+    step' gst (TapDone st) = do+        r <- step gst st+        return+            $ case r of+                  Yield x s -> Yield x (TapDone s)+                  Skip s -> Skip (TapDone s)+                  Stop -> Stop++data TapOffState fs s a+    = TapOffInit+    | TapOffTapping !fs s Int+    | TapOffDone s++-- XXX Multiple yield points+{-# INLINE_NORMAL tapOffsetEvery #-}+tapOffsetEvery :: Monad m+    => Int -> Int -> Fold m a b -> Stream m a -> Stream m a+tapOffsetEvery offset n (Fold fstep initial _ final) (Stream step state) =+    Stream step' TapOffInit++    where++    {-# INLINE_LATE step' #-}+    step' _ TapOffInit = do+        res <- initial+        return+            $ Skip+            $ case res of+                  FL.Partial s -> TapOffTapping s state (offset `mod` n)+                  FL.Done _ -> TapOffDone state+    step' gst (TapOffTapping acc st count) = do+        r <- step gst st+        case r of+            Yield x s -> do+                next <-+                    if count <= 0+                    then do+                        res <- fstep acc x+                        return+                            $ case res of+                                  FL.Partial sres ->+                                    TapOffTapping sres s (n - 1)+                                  FL.Done _ -> TapOffDone s+                    else return $ TapOffTapping acc s (count - 1)+                return $ Yield x next+            Skip s -> return $ Skip (TapOffTapping acc s count)+            Stop -> do+                void $ final acc+                return Stop+    step' gst (TapOffDone st) = do+        r <- step gst st+        return+            $ case r of+                  Yield x s -> Yield x (TapOffDone s)+                  Skip s -> Skip (TapOffDone s)+                  Stop -> Stop++-- | Apply a monadic function to each element flowing through the stream and+-- discard the results.+--+-- >>> s = Stream.enumerateFromTo 1 2+-- >>> Stream.fold Fold.drain $ Stream.trace print s+-- 1+-- 2+--+-- Compare with 'tap'.+--+{-# INLINE trace #-}+trace :: Monad m => (a -> m b) -> Stream m a -> Stream m a+trace f = mapM (\x -> void (f x) >> return x)++-- | Perform a side effect before yielding each element of the stream and+-- discard the results.+--+-- >>> s = Stream.enumerateFromTo 1 2+-- >>> Stream.fold Fold.drain $ Stream.trace_ (print "got here") s+-- "got here"+-- "got here"+--+-- Same as 'intersperseMPrefix_' but always serial.+--+-- See also: 'trace'+--+-- /Pre-release/+{-# INLINE trace_ #-}+trace_ :: Monad m => m b -> Stream m a -> Stream m a+trace_ eff = mapM (\x -> eff >> return x)++------------------------------------------------------------------------------+-- Scanning with a Fold+------------------------------------------------------------------------------++data ScanState s f = ScanInit s | ScanDo s !f | ScanDone++-- | Postscan a stream using the given monadic fold.+--+-- The following example extracts the input stream up to a point where the+-- running average of elements is no more than 10:+--+-- >>> import Data.Maybe (fromJust)+-- >>> let avg = Fold.teeWith (/) Fold.sum (fmap fromIntegral Fold.length)+-- >>> s = Stream.enumerateFromTo 1.0 100.0+-- >>> :{+--  Stream.fold Fold.toList+--   $ fmap (fromJust . fst)+--   $ Stream.takeWhile (\(_,x) -> x <= 10)+--   $ Stream.postscan (Fold.tee Fold.latest avg) s+-- :}+-- [1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0,16.0,17.0,18.0,19.0]+--+{-# INLINE_NORMAL postscan #-}+postscan :: Monad m => FL.Fold m a b -> Stream m a -> Stream m b+postscan (FL.Fold fstep initial extract final) (Stream sstep state) =+    Stream step (ScanInit state)++    where++    {-# INLINE_LATE step #-}+    step _ (ScanInit st) = do+        res <- initial+        return+            $ case res of+                  FL.Partial fs -> Skip $ ScanDo st fs+                  FL.Done b -> Yield b ScanDone+    step gst (ScanDo st fs) = do+        res <- sstep (adaptState gst) st+        case res of+            Yield x s -> do+                r <- fstep fs x+                case r of+                    FL.Partial fs1 -> do+                        !b <- extract fs1+                        return $ Yield b $ ScanDo s fs1+                    FL.Done b -> return $ Yield b ScanDone+            Skip s -> return $ Skip $ ScanDo s fs+            Stop -> final fs >> return Stop+    step _ ScanDone = return Stop++{-# INLINE scanWith #-}+scanWith :: Monad m+    => Bool -> Fold m a b -> Stream m a -> Stream m b+scanWith restart (Fold fstep initial extract final) (Stream sstep state) =+    Stream step (ScanInit state)++    where++    {-# INLINE runStep #-}+    runStep st action = do+        res <- action+        case res of+            FL.Partial fs -> do+                !b <- extract fs+                return $ Yield b $ ScanDo st fs+            FL.Done b ->+                let next = if restart then ScanInit st else ScanDone+                 in return $ Yield b next++    {-# INLINE_LATE step #-}+    step _ (ScanInit st) = runStep st initial+    step gst (ScanDo st fs) = do+        res <- sstep (adaptState gst) st+        case res of+            Yield x s -> runStep s (fstep fs x)+            Skip s -> return $ Skip $ ScanDo s fs+            Stop -> final fs >> return Stop+    step _ ScanDone = return Stop++-- XXX It may be useful to have a version of scan where we can keep the+-- accumulator independent of the value emitted. So that we do not necessarily+-- have to keep a value in the accumulator which we are not using. We can pass+-- an extraction function that will take the accumulator and the current value+-- of the element and emit the next value in the stream. That will also make it+-- possible to modify the accumulator after using it. In fact, the step function+-- can return new accumulator and the value to be emitted. The signature would+-- be more like mapAccumL.++-- | Strict left scan. Scan a stream using the given monadic fold.+--+-- >>> s = Stream.fromList [1..10]+-- >>> Stream.fold Fold.toList $ Stream.takeWhile (< 10) $ Stream.scan Fold.sum s+-- [0,1,3,6]+--+-- See also: 'usingStateT'+--++-- EXPLANATION:+-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)+--+-- Like 'map', 'scanl'' too is a one to one transformation,+-- however it adds an extra element.+--+-- >>> s = Stream.fromList [1,2,3,4]+-- >>> Stream.fold Fold.toList $ scanl' (+) 0 s+-- [0,1,3,6,10]+--+-- >>> Stream.fold Fold.toList $ scanl' (flip (:)) [] s+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]+--+-- The output of 'scanl'' is the initial value of the accumulator followed by+-- all the intermediate steps and the final result of 'foldl''.+--+-- By streaming the accumulated state after each fold step, we can share the+-- state across multiple stages of stream composition. Each stage can modify or+-- extend the state, do some processing with it and emit it for the next stage,+-- thus modularizing the stream processing. This can be useful in+-- stateful or event-driven programming.+--+-- Consider the following monolithic example, computing the sum and the product+-- of the elements in a stream in one go using a @foldl'@:+--+-- >>> foldl' step z = Stream.fold (Fold.foldl' step z)+-- >>> foldl' (\(s, p) x -> (s + x, p * x)) (0,1) s+-- (10,24)+--+-- Using @scanl'@ we can make it modular by computing the sum in the first+-- stage and passing it down to the next stage for computing the product:+--+-- >>> :{+--   foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1)+--   $ scanl' (\(s, _) x -> (s + x, x)) (0,1)+--   $ Stream.fromList [1,2,3,4]+-- :}+-- (10,24)+--+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF.  To avoid building+-- lazy expressions inside the accumulator, it is recommended that a strict+-- data structure is used for accumulator.+--+{-# INLINE_NORMAL scan #-}+scan :: Monad m+    => FL.Fold m a b -> Stream m a -> Stream m b+scan = scanWith False++-- | Like 'scan' but restarts scanning afresh when the scanning fold+-- terminates.+--+{-# INLINE_NORMAL scanMany #-}+scanMany :: Monad m+    => FL.Fold m a b -> Stream m a -> Stream m b+scanMany = scanWith True++------------------------------------------------------------------------------+-- Scanning - Prescans+------------------------------------------------------------------------------++-- Adapted from the vector package.+--+-- XXX Is a prescan useful, discarding the last step does not sound useful?  I+-- am not sure about the utility of this function, so this is implemented but+-- not exposed. We can expose it if someone provides good reasons why this is+-- useful.+--+-- XXX We have to execute the stream one step ahead to know that we are at the+-- last step.  The vector implementation of prescan executes the last fold step+-- but does not yield the result. This means we have executed the effect but+-- discarded value. This does not sound right. In this implementation we are+-- not executing the last fold step.+{-# INLINE_NORMAL prescanlM' #-}+prescanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+prescanlM' f mz (Stream step state) = Stream step' (state, mz)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, prev) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                acc <- prev+                return $ Yield acc (s, f acc x)+            Skip s -> return $ Skip (s, prev)+            Stop   -> return Stop++{-# INLINE prescanl' #-}+prescanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+prescanl' f z = prescanlM' (\a b -> return (f a b)) (return z)++------------------------------------------------------------------------------+-- Monolithic postscans (postscan followed by a map)+------------------------------------------------------------------------------++-- The performance of a modular postscan followed by a map seems to be+-- equivalent to this monolithic scan followed by map therefore we may not need+-- this implementation. We just have it for performance comparison and in case+-- modular version does not perform well in some situation.+--+{-# INLINE_NORMAL postscanlMx' #-}+postscanlMx' :: Monad m+    => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b+postscanlMx' fstep begin done (Stream step state) = do+    Stream step' (state, begin)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, acc) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                old <- acc+                y <- fstep old x+                v <- done y+                v `seq` y `seq` return (Yield v (s, return y))+            Skip s -> return $ Skip (s, acc)+            Stop   -> return Stop++{-# INLINE_NORMAL postscanlx' #-}+postscanlx' :: Monad m+    => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b+postscanlx' fstep begin done =+    postscanlMx' (\b a -> return (fstep b a)) (return begin) (return . done)++-- XXX do we need consM strict to evaluate the begin value?+{-# INLINE scanlMx' #-}+scanlMx' :: Monad m+    => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> Stream m b+scanlMx' fstep begin done s =+    (begin >>= \x -> x `seq` done x) `consM` postscanlMx' fstep begin done s++{-# INLINE scanlx' #-}+scanlx' :: Monad m+    => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> Stream m b+scanlx' fstep begin done =+    scanlMx' (\b a -> return (fstep b a)) (return begin) (return . done)++------------------------------------------------------------------------------+-- postscans+------------------------------------------------------------------------------++-- Adapted from the vector package.+{-# INLINE_NORMAL postscanlM' #-}+postscanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+postscanlM' fstep begin (Stream step state) =+    Stream step' Nothing+  where+    {-# INLINE_LATE step' #-}+    step' _ Nothing = do+        !x <- begin+        return $ Skip (Just (state, x))++    step' gst (Just (st, acc)) =  do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                !y <- fstep acc x+                return $ Yield y (Just (s, y))+            Skip s -> return $ Skip (Just (s, acc))+            Stop   -> return Stop++{-# INLINE_NORMAL postscanl' #-}+postscanl' :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a+postscanl' f seed = postscanlM' (\a b -> return (f a b)) (return seed)++{-# ANN type PScanAfterState Fuse #-}+data PScanAfterState m st acc =+      PScanAfterStep st (m acc)+    | PScanAfterYield acc (PScanAfterState m st acc)+    | PScanAfterStop++-- We can possibly have the "done" function as a Maybe to provide an option to+-- emit or not emit the accumulator when the stream stops.+--+-- TBD: use a single Yield point+--+{-# INLINE_NORMAL postscanlMAfter' #-}+postscanlMAfter' :: Monad m+    => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b+postscanlMAfter' fstep initial done (Stream step1 state1) = do+    Stream step (PScanAfterStep state1 initial)++    where++    {-# INLINE_LATE step #-}+    step gst (PScanAfterStep st acc) = do+        r <- step1 (adaptState gst) st+        case r of+            Yield x s -> do+                !old <- acc+                !y <- fstep old x+                return (Skip $ PScanAfterYield y (PScanAfterStep s (return y)))+            Skip s -> return $ Skip $ PScanAfterStep s acc+            -- Strictness is important for fusion+            Stop -> do+                !v <- acc+                !res <- done v+                return (Skip $ PScanAfterYield res PScanAfterStop)+    step _ (PScanAfterYield acc next) = return $ Yield acc next+    step _ PScanAfterStop = return Stop++{-# INLINE_NORMAL postscanlM #-}+postscanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+postscanlM fstep begin (Stream step state) = Stream step' Nothing+  where+    {-# INLINE_LATE step' #-}+    step' _ Nothing = do+        r <- begin+        return $ Skip (Just (state, r))++    step' gst (Just (st, acc)) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                y <- fstep acc x+                return (Yield y (Just (s, y)))+            Skip s -> return $ Skip (Just (s, acc))+            Stop   -> return Stop++{-# INLINE_NORMAL postscanl #-}+postscanl :: Monad m => (a -> b -> a) -> a -> Stream m b -> Stream m a+postscanl f seed = postscanlM (\a b -> return (f a b)) (return seed)++-- | Like 'scanl'' but with a monadic step function and a monadic seed.+--+{-# INLINE_NORMAL scanlM' #-}+scanlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+scanlM' fstep begin (Stream step state) = Stream step' Nothing+  where+    {-# INLINE_LATE step' #-}+    step' _ Nothing = do+        !x <- begin+        return $ Yield x (Just (state, x))+    step' gst (Just (st, acc)) =  do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                !y <- fstep acc x+                return $ Yield y (Just (s, y))+            Skip s -> return $ Skip (Just (s, acc))+            Stop   -> return Stop++-- | @scanlMAfter' accumulate initial done stream@ is like 'scanlM'' except+-- that it provides an additional @done@ function to be applied on the+-- accumulator when the stream stops. The result of @done@ is also emitted in+-- the stream.+--+-- This function can be used to allocate a resource in the beginning of the+-- scan and release it when the stream ends or to flush the internal state of+-- the scan at the end.+--+-- /Pre-release/+--+{-# INLINE scanlMAfter' #-}+scanlMAfter' :: Monad m+    => (b -> a -> m b) -> m b -> (b -> m b) -> Stream m a -> Stream m b+scanlMAfter' fstep initial done s =+    initial `consM` postscanlMAfter' fstep initial done s++-- >>> scanl' f z xs = z `Stream.cons` postscanl' f z xs++-- | Strict left scan. Like 'map', 'scanl'' too is a one to one transformation,+-- however it adds an extra element.+--+-- >>> Stream.toList $ Stream.scanl' (+) 0 $ Stream.fromList [1,2,3,4]+-- [0,1,3,6,10]+--+-- >>> Stream.toList $ Stream.scanl' (flip (:)) [] $ Stream.fromList [1,2,3,4]+-- [[],[1],[2,1],[3,2,1],[4,3,2,1]]+--+-- The output of 'scanl'' is the initial value of the accumulator followed by+-- all the intermediate steps and the final result of 'foldl''.+--+-- By streaming the accumulated state after each fold step, we can share the+-- state across multiple stages of stream composition. Each stage can modify or+-- extend the state, do some processing with it and emit it for the next stage,+-- thus modularizing the stream processing. This can be useful in+-- stateful or event-driven programming.+--+-- Consider the following monolithic example, computing the sum and the product+-- of the elements in a stream in one go using a @foldl'@:+--+-- >>> Stream.fold (Fold.foldl' (\(s, p) x -> (s + x, p * x)) (0,1)) $ Stream.fromList [1,2,3,4]+-- (10,24)+--+-- Using @scanl'@ we can make it modular by computing the sum in the first+-- stage and passing it down to the next stage for computing the product:+--+-- >>> :{+--   Stream.fold (Fold.foldl' (\(_, p) (s, x) -> (s, p * x)) (0,1))+--   $ Stream.scanl' (\(s, _) x -> (s + x, x)) (0,1)+--   $ Stream.fromList [1,2,3,4]+-- :}+-- (10,24)+--+-- IMPORTANT: 'scanl'' evaluates the accumulator to WHNF.  To avoid building+-- lazy expressions inside the accumulator, it is recommended that a strict+-- data structure is used for accumulator.+--+-- >>> scanl' step z = Stream.scan (Fold.foldl' step z)+-- >>> scanl' f z xs = Stream.scanlM' (\a b -> return (f a b)) (return z) xs+--+-- See also: 'usingStateT'+--+{-# INLINE scanl' #-}+scanl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+scanl' f seed = scanlM' (\a b -> return (f a b)) (return seed)++{-# INLINE_NORMAL scanlM #-}+scanlM :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> Stream m b+scanlM fstep begin (Stream step state) = Stream step' Nothing+  where+    {-# INLINE_LATE step' #-}+    step' _ Nothing = do+        x <- begin+        return $ Yield x (Just (state, x))+    step' gst (Just (st, acc)) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                y <- fstep acc x+                return $ Yield y (Just (s, y))+            Skip s -> return $ Skip (Just (s, acc))+            Stop   -> return Stop++{-# INLINE scanl #-}+scanl :: Monad m => (b -> a -> b) -> b -> Stream m a -> Stream m b+scanl f seed = scanlM (\a b -> return (f a b)) (return seed)++-- Adapted from the vector package+{-# INLINE_NORMAL scanl1M #-}+scanl1M :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a+scanl1M fstep (Stream step state) = Stream step' (state, Nothing)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, Nothing) = do+        r <- step gst st+        case r of+            Yield x s -> return $ Yield x (s, Just x)+            Skip s -> return $ Skip (s, Nothing)+            Stop   -> return Stop++    step' gst (st, Just acc) = do+        r <- step gst st+        case r of+            Yield y s -> do+                z <- fstep acc y+                return $ Yield z (s, Just z)+            Skip s -> return $ Skip (s, Just acc)+            Stop   -> return Stop++{-# INLINE scanl1 #-}+scanl1 :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a+scanl1 f = scanl1M (\x y -> return (f x y))++-- Adapted from the vector package++-- | Like 'scanl1'' but with a monadic step function.+--+{-# INLINE_NORMAL scanl1M' #-}+scanl1M' :: Monad m => (a -> a -> m a) -> Stream m a -> Stream m a+scanl1M' fstep (Stream step state) = Stream step' (state, Nothing)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, Nothing) = do+        r <- step gst st+        case r of+            Yield x s -> x `seq` return $ Yield x (s, Just x)+            Skip s -> return $ Skip (s, Nothing)+            Stop   -> return Stop++    step' gst (st, Just acc) = acc `seq` do+        r <- step gst st+        case r of+            Yield y s -> do+                z <- fstep acc y+                z `seq` return $ Yield z (s, Just z)+            Skip s -> return $ Skip (s, Just acc)+            Stop   -> return Stop++-- | Like 'scanl'' but for a non-empty stream. The first element of the stream+-- is used as the initial value of the accumulator. Does nothing if the stream+-- is empty.+--+-- >>> Stream.toList $ Stream.scanl1' (+) $ Stream.fromList [1,2,3,4]+-- [1,3,6,10]+--+{-# INLINE scanl1' #-}+scanl1' :: Monad m => (a -> a -> a) -> Stream m a -> Stream m a+scanl1' f = scanl1M' (\x y -> return (f x y))++-------------------------------------------------------------------------------+-- Filtering+-------------------------------------------------------------------------------++-- | Modify a @Stream m a -> Stream m a@ stream transformation that accepts a+-- predicate @(a -> b)@ to accept @((s, a) -> b)@ instead, provided a+-- transformation @Stream m a -> Stream m (s, a)@. Convenient to filter with+-- index or time.+--+-- >>> filterWithIndex = Stream.with Stream.indexed Stream.filter+--+-- /Pre-release/+{-# INLINE with #-}+with :: Monad m =>+       (Stream m a -> Stream m (s, a))+    -> (((s, a) -> b) -> Stream m (s, a) -> Stream m (s, a))+    -> (((s, a) -> b) -> Stream m a -> Stream m a)+with f comb g = fmap snd . comb g . f++-- Adapted from the vector package++-- | Same as 'filter' but with a monadic predicate.+--+-- >>> f p x = p x >>= \r -> return $ if r then Just x else Nothing+-- >>> filterM p = Stream.mapMaybeM (f p)+--+{-# INLINE_NORMAL filterM #-}+filterM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+filterM f (Stream step state) = Stream step' state+  where+    {-# INLINE_LATE step' #-}+    step' gst st = do+        r <- step gst st+        case r of+            Yield x s -> do+                b <- f x+                return $ if b+                         then Yield x s+                         else Skip s+            Skip s -> return $ Skip s+            Stop   -> return Stop++-- | Include only those elements that pass a predicate.+--+-- >>> filter p = Stream.filterM (return . p)+-- >>> filter p = Stream.mapMaybe (\x -> if p x then Just x else Nothing)+-- >>> filter p = Stream.scanMaybe (Fold.filtering p)+--+{-# INLINE filter #-}+filter :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+filter f = filterM (return . f)+-- filter p = scanMaybe (FL.filtering p)++-- | Drop repeated elements that are adjacent to each other using the supplied+-- comparison function.+--+-- >>> uniq = Stream.uniqBy (==)+--+-- To strip duplicate path separators:+--+-- >>> input = Stream.fromList "//a//b"+-- >>> f x y = x == '/' && y == '/'+-- >>> Stream.fold Fold.toList $ Stream.uniqBy f input+-- "/a/b"+--+-- Space: @O(1)@+--+-- /Pre-release/+--+{-# INLINE uniqBy #-}+uniqBy :: Monad m =>+    (a -> a -> Bool) -> Stream m a -> Stream m a+-- uniqBy eq = scanMaybe (FL.uniqBy eq)+uniqBy eq = catMaybes . rollingMap f++    where++    f pre curr =+        case pre of+            Nothing -> Just curr+            Just x -> if x `eq` curr then Nothing else Just curr++-- Adapted from the vector package++-- | Drop repeated elements that are adjacent to each other.+--+-- >>> uniq = Stream.uniqBy (==)+--+{-# INLINE_NORMAL uniq #-}+uniq :: (Eq a, Monad m) => Stream m a -> Stream m a+-- uniq = scanMaybe FL.uniq+uniq (Stream step state) = Stream step' (Nothing, state)+  where+    {-# INLINE_LATE step' #-}+    step' gst (Nothing, st) = do+        r <- step gst st+        case r of+            Yield x s -> return $ Yield x (Just x, s)+            Skip  s   -> return $ Skip  (Nothing, s)+            Stop      -> return Stop+    step' gst (Just x, st)  = do+         r <- step gst st+         case r of+             Yield y s | x == y   -> return $ Skip (Just x, s)+                       | otherwise -> return $ Yield y (Just y, s)+             Skip  s   -> return $ Skip (Just x, s)+             Stop      -> return Stop++-- | Deletes the first occurrence of the element in the stream that satisfies+-- the given equality predicate.+--+-- >>> input = Stream.fromList [1,3,3,5]+-- >>> Stream.fold Fold.toList $ Stream.deleteBy (==) 3 input+-- [1,3,5]+--+{-# INLINE_NORMAL deleteBy #-}+deleteBy :: Monad m => (a -> a -> Bool) -> a -> Stream m a -> Stream m a+-- deleteBy cmp x = scanMaybe (FL.deleteBy cmp x)+deleteBy eq x (Stream step state) = Stream step' (state, False)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, False) = do+        r <- step gst st+        case r of+            Yield y s -> return $+                if eq x y then Skip (s, True) else Yield y (s, False)+            Skip s -> return $ Skip (s, False)+            Stop   -> return Stop++    step' gst (st, True) = do+        r <- step gst st+        case r of+            Yield y s -> return $ Yield y (s, True)+            Skip s -> return $ Skip (s, True)+            Stop   -> return Stop++-- | Strip all leading and trailing occurrences of an element passing a+-- predicate and make all other consecutive occurrences uniq.+--+-- >> prune p = Stream.dropWhileAround p $ Stream.uniqBy (x y -> p x && p y)+--+-- @+-- > Stream.prune isSpace (Stream.fromList "  hello      world!   ")+-- "hello world!"+--+-- @+--+-- Space: @O(1)@+--+-- /Unimplemented/+{-# INLINE prune #-}+prune ::+    -- (Monad m, Eq a) =>+    (a -> Bool) -> Stream m a -> Stream m a+prune = error "Not implemented yet!"++-- Possible implementation:+-- @repeated =+--      Stream.catMaybes . Stream.parseMany (Parser.groupBy (==) Fold.repeated)@+--+-- 'Fold.repeated' should return 'Just' when repeated, and 'Nothing' for a+-- single element.++-- | Emit only repeated elements, once.+--+-- /Unimplemented/+repeated :: -- (Monad m, Eq a) =>+    Stream m a -> Stream m a+repeated = undefined++------------------------------------------------------------------------------+-- Trimming+------------------------------------------------------------------------------++-- | Take all consecutive elements at the end of the stream for which the+-- predicate is true.+--+-- O(n) space, where n is the number elements taken.+--+-- /Unimplemented/+{-# INLINE takeWhileLast #-}+takeWhileLast :: -- Monad m =>+    (a -> Bool) -> Stream m a -> Stream m a+takeWhileLast = undefined -- fromStreamD $ D.takeWhileLast n $ toStreamD m++-- | Like 'takeWhile' and 'takeWhileLast' combined.+--+-- O(n) space, where n is the number elements taken from the end.+--+-- /Unimplemented/+{-# INLINE takeWhileAround #-}+takeWhileAround :: -- Monad m =>+    (a -> Bool) -> Stream m a -> Stream m a+takeWhileAround = undefined -- fromStreamD $ D.takeWhileAround n $ toStreamD m++-- Adapted from the vector package++-- | Discard first 'n' elements from the stream and take the rest.+--+{-# INLINE_NORMAL drop #-}+drop :: Monad m => Int -> Stream m a -> Stream m a+drop n (Stream step state) = Stream step' (state, Just n)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, Just i)+      | i > 0 = do+          r <- step gst st+          return $+            case r of+              Yield _ s -> Skip (s, Just (i - 1))+              Skip s    -> Skip (s, Just i)+              Stop      -> Stop+      | otherwise = return $ Skip (st, Nothing)++    step' gst (st, Nothing) = do+      r <- step gst st+      return $+        case r of+          Yield x s -> Yield x (s, Nothing)+          Skip  s   -> Skip (s, Nothing)+          Stop      -> Stop++-- Adapted from the vector package+data DropWhileState s a+    = DropWhileDrop s+    | DropWhileYield a s+    | DropWhileNext s++-- | Same as 'dropWhile' but with a monadic predicate.+--+{-# INLINE_NORMAL dropWhileM #-}+dropWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+-- dropWhileM p = scanMaybe (FL.droppingWhileM p)+dropWhileM f (Stream step state) = Stream step' (DropWhileDrop state)+  where+    {-# INLINE_LATE step' #-}+    step' gst (DropWhileDrop st) = do+        r <- step gst st+        case r of+            Yield x s -> do+                b <- f x+                if b+                then return $ Skip (DropWhileDrop s)+                else return $ Skip (DropWhileYield x s)+            Skip s -> return $ Skip (DropWhileDrop s)+            Stop -> return Stop++    step' gst (DropWhileNext st) =  do+        r <- step gst st+        case r of+            Yield x s -> return $ Skip (DropWhileYield x s)+            Skip s    -> return $ Skip (DropWhileNext s)+            Stop      -> return Stop++    step' _ (DropWhileYield x st) = return $ Yield x (DropWhileNext st)++-- | Drop elements in the stream as long as the predicate succeeds and then+-- take the rest of the stream.+--+{-# INLINE dropWhile #-}+dropWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+-- dropWhile p = scanMaybe (FL.droppingWhile p)+dropWhile f = dropWhileM (return . f)++-- | Drop @n@ elements at the end of the stream.+--+-- O(n) space, where n is the number elements dropped.+--+-- /Unimplemented/+{-# INLINE dropLast #-}+dropLast :: -- Monad m =>+    Int -> Stream m a -> Stream m a+dropLast = undefined -- fromStreamD $ D.dropLast n $ toStreamD m++-- | Drop all consecutive elements at the end of the stream for which the+-- predicate is true.+--+-- O(n) space, where n is the number elements dropped.+--+-- /Unimplemented/+{-# INLINE dropWhileLast #-}+dropWhileLast :: -- Monad m =>+    (a -> Bool) -> Stream m a -> Stream m a+dropWhileLast = undefined -- fromStreamD $ D.dropWhileLast n $ toStreamD m++-- | Like 'dropWhile' and 'dropWhileLast' combined.+--+-- O(n) space, where n is the number elements dropped from the end.+--+-- /Unimplemented/+{-# INLINE dropWhileAround #-}+dropWhileAround :: -- Monad m =>+    (a -> Bool) -> Stream m a -> Stream m a+dropWhileAround = undefined -- fromStreamD $ D.dropWhileAround n $ toStreamD m++------------------------------------------------------------------------------+-- Inserting Elements+------------------------------------------------------------------------------++-- | @insertBy cmp elem stream@ inserts @elem@ before the first element in+-- @stream@ that is less than @elem@ when compared using @cmp@.+--+-- >>> insertBy cmp x = Stream.mergeBy cmp (Stream.fromPure x)+--+-- >>> input = Stream.fromList [1,3,5]+-- >>> Stream.fold Fold.toList $ Stream.insertBy compare 2 input+-- [1,2,3,5]+--+{-# INLINE_NORMAL insertBy #-}+insertBy :: Monad m => (a -> a -> Ordering) -> a -> Stream m a -> Stream m a+insertBy cmp a (Stream step state) = Stream step' (state, False, Nothing)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, False, _) = do+        r <- step gst st+        case r of+            Yield x s -> case cmp a x of+                GT -> return $ Yield x (s, False, Nothing)+                _  -> return $ Yield a (s, True, Just x)+            Skip s -> return $ Skip (s, False, Nothing)+            Stop   -> return $ Yield a (st, True, Nothing)++    step' _ (_, True, Nothing) = return Stop++    step' gst (st, True, Just prev) = do+        r <- step gst st+        case r of+            Yield x s -> return $ Yield prev (s, True, Just x)+            Skip s    -> return $ Skip (s, True, Just prev)+            Stop      -> return $ Yield prev (st, True, Nothing)++data LoopState x s = FirstYield s+                   | InterspersingYield s+                   | YieldAndCarry x s++-- intersperseM = intersperseMWith 1++-- | Insert an effect and its output before consuming an element of a stream+-- except the first one.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseM (putChar '.' >> return ',') input+-- h.,e.,l.,l.,o"h,e,l,l,o"+--+-- Be careful about the order of effects. In the above example we used trace+-- after the intersperse, if we use it before the intersperse the output would+-- be he.l.l.o."h,e,l,l,o".+--+-- >>> Stream.fold Fold.toList $ Stream.intersperseM (putChar '.' >> return ',') $ Stream.trace putChar input+-- he.l.l.o."h,e,l,l,o"+--+{-# INLINE_NORMAL intersperseM #-}+intersperseM :: Monad m => m a -> Stream m a -> Stream m a+intersperseM m (Stream step state) = Stream step' (FirstYield state)+  where+    {-# INLINE_LATE step' #-}+    step' gst (FirstYield st) = do+        r <- step gst st+        return $+            case r of+                Yield x s -> Skip (YieldAndCarry x s)+                Skip s -> Skip (FirstYield s)+                Stop -> Stop++    step' gst (InterspersingYield st) = do+        r <- step gst st+        case r of+            Yield x s -> do+                a <- m+                return $ Yield a (YieldAndCarry x s)+            Skip s -> return $ Skip $ InterspersingYield s+            Stop -> return Stop++    step' _ (YieldAndCarry x st) = return $ Yield x (InterspersingYield st)++-- | Insert a pure value between successive elements of a stream.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.intersperse ',' input+-- "h,e,l,l,o"+--+{-# INLINE intersperse #-}+intersperse :: Monad m => a -> Stream m a -> Stream m a+intersperse a = intersperseM (return a)++-- | Insert a side effect before consuming an element of a stream except the+-- first one.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.drain $ Stream.trace putChar $ Stream.intersperseM_ (putChar '.') input+-- h.e.l.l.o+--+-- /Pre-release/+{-# INLINE_NORMAL intersperseM_ #-}+intersperseM_ :: Monad m => m b -> Stream m a -> Stream m a+intersperseM_ m (Stream step1 state1) = Stream step (Left (pure (), state1))+  where+    {-# INLINE_LATE step #-}+    step gst (Left (eff, st)) = do+        r <- step1 gst st+        case r of+            Yield x s -> eff >> return (Yield x (Right s))+            Skip s -> return $ Skip (Left (eff, s))+            Stop -> return Stop++    step _ (Right st) = return $ Skip $ Left (void m, st)++-- | Intersperse a monadic action into the input stream after every @n@+-- elements.+--+-- >> input = Stream.fromList "hello"+-- >> Stream.fold Fold.toList $ Stream.intersperseMWith 2 (return ',') input+-- "he,ll,o"+--+-- /Unimplemented/+{-# INLINE intersperseMWith #-}+intersperseMWith :: -- Monad m =>+    Int -> m a -> Stream m a -> Stream m a+intersperseMWith _n _f _xs = undefined++data SuffixState s a+    = SuffixElem s+    | SuffixSuffix s+    | SuffixYield a (SuffixState s a)++-- | Insert an effect and its output after consuming an element of a stream.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMSuffix (putChar '.' >> return ',') input+-- h.,e.,l.,l.,o.,"h,e,l,l,o,"+--+-- /Pre-release/+{-# INLINE_NORMAL intersperseMSuffix #-}+intersperseMSuffix :: forall m a. Monad m => m a -> Stream m a -> Stream m a+intersperseMSuffix action (Stream step state) = Stream step' (SuffixElem state)+    where+    {-# INLINE_LATE step' #-}+    step' gst (SuffixElem st) = do+        r <- step gst st+        return $ case r of+            Yield x s -> Skip (SuffixYield x (SuffixSuffix s))+            Skip s -> Skip (SuffixElem s)+            Stop -> Stop++    step' _ (SuffixSuffix st) = do+        action >>= \r -> return $ Skip (SuffixYield r (SuffixElem st))++    step' _ (SuffixYield x next) = return $ Yield x next++-- | Insert a side effect after consuming an element of a stream.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffix_ (threadDelay 1000000) input+-- "hello"+--+-- /Pre-release/+--+{-# INLINE_NORMAL intersperseMSuffix_ #-}+intersperseMSuffix_ :: Monad m => m b -> Stream m a -> Stream m a+intersperseMSuffix_ m (Stream step1 state1) = Stream step (Left state1)+  where+    {-# INLINE_LATE step #-}+    step gst (Left st) = do+        r <- step1 gst st+        case r of+            Yield x s -> return $ Yield x (Right s)+            Skip s -> return $ Skip $ Left s+            Stop -> return Stop++    step _ (Right st) = m >> return (Skip (Left st))++data SuffixSpanState s a+    = SuffixSpanElem s Int+    | SuffixSpanSuffix s+    | SuffixSpanYield a (SuffixSpanState s a)+    | SuffixSpanLast+    | SuffixSpanStop++-- | Like 'intersperseMSuffix' but intersperses an effectful action into the+-- input stream after every @n@ elements and after the last element.+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.intersperseMSuffixWith 2 (return ',') input+-- "he,ll,o,"+--+-- /Pre-release/+--+{-# INLINE_NORMAL intersperseMSuffixWith #-}+intersperseMSuffixWith :: forall m a. Monad m+    => Int -> m a -> Stream m a -> Stream m a+intersperseMSuffixWith n action (Stream step state) =+    Stream step' (SuffixSpanElem state n)+    where+    {-# INLINE_LATE step' #-}+    step' gst (SuffixSpanElem st i) | i > 0 = do+        r <- step gst st+        return $ case r of+            Yield x s -> Skip (SuffixSpanYield x (SuffixSpanElem s (i - 1)))+            Skip s -> Skip (SuffixSpanElem s i)+            Stop -> if i == n then Stop else Skip SuffixSpanLast+    step' _ (SuffixSpanElem st _) = return $ Skip (SuffixSpanSuffix st)++    step' _ (SuffixSpanSuffix st) = do+        action >>= \r -> return $ Skip (SuffixSpanYield r (SuffixSpanElem st n))++    step' _ SuffixSpanLast = do+        action >>= \r -> return $ Skip (SuffixSpanYield r SuffixSpanStop)++    step' _ (SuffixSpanYield x next) = return $ Yield x next++    step' _ SuffixSpanStop = return Stop++-- | Insert a side effect before consuming an element of a stream.+--+-- Definition:+--+-- >>> intersperseMPrefix_ m = Stream.mapM (\x -> void m >> return x)+--+-- >>> input = Stream.fromList "hello"+-- >>> Stream.fold Fold.toList $ Stream.trace putChar $ Stream.intersperseMPrefix_ (putChar '.' >> return ',') input+-- .h.e.l.l.o"hello"+--+-- Same as 'trace_'.+--+-- /Pre-release/+--+{-# INLINE intersperseMPrefix_ #-}+intersperseMPrefix_ :: Monad m => m b -> Stream m a -> Stream m a+intersperseMPrefix_ m = mapM (\x -> void m >> return x)++------------------------------------------------------------------------------+-- Inserting Time+------------------------------------------------------------------------------++-- XXX This should be in Prelude, should we export this as a helper function?++-- | Block the current thread for specified number of seconds.+{-# INLINE sleep #-}+sleep :: MonadIO m => Double -> m ()+sleep n = liftIO $ threadDelay $ round $ n * 1000000++-- | Introduce a delay of specified seconds between elements of the stream.+--+-- Definition:+--+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000+-- >>> delay = Stream.intersperseM_ . sleep+--+-- Example:+--+-- >>> input = Stream.enumerateFromTo 1 3+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delay 1 input+-- 1+-- 2+-- 3+--+{-# INLINE delay #-}+delay :: MonadIO m => Double -> Stream m a -> Stream m a+delay = intersperseM_ . sleep++-- | Introduce a delay of specified seconds after consuming an element of a+-- stream.+--+-- Definition:+--+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000+-- >>> delayPost = Stream.intersperseMSuffix_ . sleep+--+-- Example:+--+-- >>> input = Stream.enumerateFromTo 1 3+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPost 1 input+-- 1+-- 2+-- 3+--+-- /Pre-release/+--+{-# INLINE delayPost #-}+delayPost :: MonadIO m => Double -> Stream m a -> Stream m a+delayPost n = intersperseMSuffix_ $ liftIO $ threadDelay $ round $ n * 1000000++-- | Introduce a delay of specified seconds before consuming an element of a+-- stream.+--+-- Definition:+--+-- >>> sleep n = liftIO $ threadDelay $ round $ n * 1000000+-- >>> delayPre = Stream.intersperseMPrefix_. sleep+--+-- Example:+--+-- >>> input = Stream.enumerateFromTo 1 3+-- >>> Stream.fold (Fold.drainMapM print) $ Stream.delayPre 1 input+-- 1+-- 2+-- 3+--+-- /Pre-release/+--+{-# INLINE delayPre #-}+delayPre :: MonadIO m => Double -> Stream m a -> Stream m a+delayPre = intersperseMPrefix_. sleep++------------------------------------------------------------------------------+-- Reordering+------------------------------------------------------------------------------++-- | Returns the elements of the stream in reverse order.  The stream must be+-- finite. Note that this necessarily buffers the entire stream in memory.+--+-- Definition:+--+-- >>> reverse m = Stream.concatEffect $ Stream.fold Fold.toListRev m >>= return . Stream.fromList+--+{-# INLINE_NORMAL reverse #-}+reverse :: Monad m => Stream m a -> Stream m a+reverse m = concatEffect $ fold FL.toListRev m <&> fromList+{-+reverse m = Stream step Nothing+    where+    {-# INLINE_LATE step #-}+    step _ Nothing = do+        xs <- foldl' (flip (:)) [] m+        return $ Skip (Just xs)+    step _ (Just (x:xs)) = return $ Yield x (Just xs)+    step _ (Just []) = return Stop+-}++-- | Like 'reverse' but several times faster, requires an 'Unbox' instance.+--+-- /O(n) space/+--+-- /Pre-release/+{-# INLINE reverseUnbox #-}+reverseUnbox :: (MonadIO m, Unbox a) => Stream m a -> Stream m a+reverseUnbox =+    A.flattenArraysRev -- unfoldMany A.readRev+        . fromStreamK+        . K.reverse+        . toStreamK+        . A.chunksOf defaultChunkSize++-- | Buffer until the next element in sequence arrives. The function argument+-- determines the difference in sequence numbers. This could be useful in+-- implementing sequenced streams, for example, TCP reassembly.+--+-- /Unimplemented/+--+{-# INLINE reassembleBy #-}+reassembleBy+    :: -- Monad m =>+       Fold m a b+    -> (a -> a -> Int)+    -> Stream m a+    -> Stream m b+reassembleBy = undefined++------------------------------------------------------------------------------+-- Position Indexing+------------------------------------------------------------------------------++-- Adapted from the vector package++-- |+-- >>> f = Fold.foldl' (\(i, _) x -> (i + 1, x)) (-1,undefined)+-- >>> indexed = Stream.postscan f+-- >>> indexed = Stream.zipWith (,) (Stream.enumerateFrom 0)+-- >>> indexedR n = fmap (\(i, a) -> (n - i, a)) . indexed+--+-- Pair each element in a stream with its index, starting from index 0.+--+-- >>> Stream.fold Fold.toList $ Stream.indexed $ Stream.fromList "hello"+-- [(0,'h'),(1,'e'),(2,'l'),(3,'l'),(4,'o')]+--+{-# INLINE_NORMAL indexed #-}+indexed :: Monad m => Stream m a -> Stream m (Int, a)+-- indexed = scanMaybe FL.indexing+indexed (Stream step state) = Stream step' (state, 0)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, i) = i `seq` do+         r <- step (adaptState gst) st+         case r of+             Yield x s -> return $ Yield (i, x) (s, i+1)+             Skip    s -> return $ Skip (s, i)+             Stop      -> return Stop++-- Adapted from the vector package++-- |+-- >>> f n = Fold.foldl' (\(i, _) x -> (i - 1, x)) (n + 1,undefined)+-- >>> indexedR n = Stream.postscan (f n)+--+-- >>> s n = Stream.enumerateFromThen n (n - 1)+-- >>> indexedR n = Stream.zipWith (,) (s n)+--+-- Pair each element in a stream with its index, starting from the+-- given index @n@ and counting down.+--+-- >>> Stream.fold Fold.toList $ Stream.indexedR 10 $ Stream.fromList "hello"+-- [(10,'h'),(9,'e'),(8,'l'),(7,'l'),(6,'o')]+--+{-# INLINE_NORMAL indexedR #-}+indexedR :: Monad m => Int -> Stream m a -> Stream m (Int, a)+-- indexedR n = scanMaybe (FL.indexingRev n)+indexedR m (Stream step state) = Stream step' (state, m)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, i) = i `seq` do+         r <- step (adaptState gst) st+         case r of+             Yield x s -> let i' = i - 1+                          in return $ Yield (i, x) (s, i')+             Skip    s -> return $ Skip (s, i)+             Stop      -> return Stop++-------------------------------------------------------------------------------+-- Time Indexing+-------------------------------------------------------------------------------++-- Note: The timestamp stream must be the second stream in the zip so that the+-- timestamp is generated after generating the stream element and not before.+-- If we do not do that then the following example will generate the same+-- timestamp for first two elements:+--+-- Stream.fold Fold.toList $ Stream.timestamped $ Stream.delay $ Stream.enumerateFromTo 1 3++-- | Pair each element in a stream with an absolute timestamp, using a clock of+-- specified granularity.  The timestamp is generated just before the element+-- is consumed.+--+-- >>> Stream.fold Fold.toList $ Stream.timestampWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3+-- [(AbsTime (TimeSpec {sec = ..., nsec = ...}),1),(AbsTime (TimeSpec {sec = ..., nsec = ...}),2),(AbsTime (TimeSpec {sec = ..., nsec = ...}),3)]+--+-- /Pre-release/+--+{-# INLINE timestampWith #-}+timestampWith :: (MonadIO m)+    => Double -> Stream m a -> Stream m (AbsTime, a)+timestampWith g stream = zipWith (flip (,)) stream (absTimesWith g)++-- TBD: check performance vs a custom implementation without using zipWith.+--+-- /Pre-release/+--+{-# INLINE timestamped #-}+timestamped :: (MonadIO m)+    => Stream m a -> Stream m (AbsTime, a)+timestamped = timestampWith 0.01++-- | Pair each element in a stream with relative times starting from 0, using a+-- clock with the specified granularity. The time is measured just before the+-- element is consumed.+--+-- >>> Stream.fold Fold.toList $ Stream.timeIndexWith 0.01 $ Stream.delay 1 $ Stream.enumerateFromTo 1 3+-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]+--+-- /Pre-release/+--+{-# INLINE timeIndexWith #-}+timeIndexWith :: (MonadIO m)+    => Double -> Stream m a -> Stream m (RelTime64, a)+timeIndexWith g stream = zipWith (flip (,)) stream (relTimesWith g)++-- | Pair each element in a stream with relative times starting from 0, using a+-- 10 ms granularity clock. The time is measured just before the element is+-- consumed.+--+-- >>> Stream.fold Fold.toList $ Stream.timeIndexed $ Stream.delay 1 $ Stream.enumerateFromTo 1 3+-- [(RelTime64 (NanoSecond64 ...),1),(RelTime64 (NanoSecond64 ...),2),(RelTime64 (NanoSecond64 ...),3)]+--+-- /Pre-release/+--+{-# INLINE timeIndexed #-}+timeIndexed :: (MonadIO m)+    => Stream m a -> Stream m (RelTime64, a)+timeIndexed = timeIndexWith 0.01++------------------------------------------------------------------------------+-- Searching+------------------------------------------------------------------------------++-- | Find all the indices where the element in the stream satisfies the given+-- predicate.+--+-- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)+--+{-# INLINE_NORMAL findIndices #-}+findIndices :: Monad m => (a -> Bool) -> Stream m a -> Stream m Int+findIndices p (Stream step state) = Stream step' (state, 0)+  where+    {-# INLINE_LATE step' #-}+    step' gst (st, i) = i `seq` do+      r <- step (adaptState gst) st+      return $ case r of+          Yield x s -> if p x then Yield i (s, i+1) else Skip (s, i+1)+          Skip s -> Skip (s, i)+          Stop   -> Stop++-- | Find all the indices where the value of the element in the stream is equal+-- to the given value.+--+-- >>> elemIndices a = Stream.findIndices (== a)+--+{-# INLINE elemIndices #-}+elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int+elemIndices a = findIndices (== a)++{-# INLINE_NORMAL slicesBy #-}+slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)+slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))++    where++    {-# INLINE_LATE step #-}+    step gst (Just (st, i, len)) = i `seq` len `seq` do+      r <- step1 (adaptState gst) st+      return+        $ case r of+              Yield x s ->+                if p x+                then Yield (i, len + 1) (Just (s, i + len + 1, 0))+                else Skip (Just (s, i, len + 1))+              Skip s -> Skip (Just (s, i, len))+              Stop -> if len == 0 then Stop else Yield (i, len) Nothing+    step _ Nothing = return Stop++------------------------------------------------------------------------------+-- Rolling map+------------------------------------------------------------------------------++data RollingMapState s a = RollingMapGo s a++-- | Like 'rollingMap' but with an effectful map function.+--+-- /Pre-release/+--+{-# INLINE rollingMapM #-}+rollingMapM :: Monad m => (Maybe a -> a -> m b) -> Stream m a -> Stream m b+-- rollingMapM f = scanMaybe (FL.slide2 $ Window.rollingMapM f)+rollingMapM f (Stream step1 state1) = Stream step (RollingMapGo state1 Nothing)++    where++    step gst (RollingMapGo s1 curr) = do+        r <- step1 (adaptState gst) s1+        case r of+            Yield x s -> do+                !res <- f curr x+                return $ Yield res $ RollingMapGo s (Just x)+            Skip s -> return $ Skip $ RollingMapGo s curr+            Stop   -> return Stop++-- rollingMap is a special case of an incremental sliding fold. It can be+-- written as:+--+-- > fld f = slidingWindow 1 (Fold.foldl' (\_ (x,y) -> f y x)+-- > rollingMap f = Stream.postscan (fld f) undefined++-- | Apply a function on every two successive elements of a stream. The first+-- argument of the map function is the previous element and the second argument+-- is the current element. When the current element is the first element, the+-- previous element is 'Nothing'.+--+-- /Pre-release/+--+{-# INLINE rollingMap #-}+rollingMap :: Monad m => (Maybe a -> a -> b) -> Stream m a -> Stream m b+-- rollingMap f = scanMaybe (FL.slide2 $ Window.rollingMap f)+rollingMap f = rollingMapM (\x y -> return $ f x y)++-- | Like 'rollingMap' but requires at least two elements in the stream,+-- returns an empty stream otherwise.+--+-- This is the stream equivalent of the list idiom @zipWith f xs (tail xs)@.+--+-- /Pre-release/+--+{-# INLINE rollingMap2 #-}+rollingMap2 :: Monad m => (a -> a -> b) -> Stream m a -> Stream m b+rollingMap2 f = catMaybes . rollingMap g++    where++    g Nothing _ = Nothing+    g (Just x) y = Just (f x y)++------------------------------------------------------------------------------+-- Maybe Streams+------------------------------------------------------------------------------++-- XXX Will this always fuse properly?++-- | Map a 'Maybe' returning function to a stream, filter out the 'Nothing'+-- elements, and return a stream of values extracted from 'Just'.+--+-- Equivalent to:+--+-- >>> mapMaybe f = Stream.catMaybes . fmap f+--+{-# INLINE_NORMAL mapMaybe #-}+mapMaybe :: Monad m => (a -> Maybe b) -> Stream m a -> Stream m b+mapMaybe f = fmap fromJust . filter isJust . map f++-- | Like 'mapMaybe' but maps a monadic function.+--+-- Equivalent to:+--+-- >>> mapMaybeM f = Stream.catMaybes . Stream.mapM f+--+-- >>> mapM f = Stream.mapMaybeM (\x -> Just <$> f x)+--+{-# INLINE_NORMAL mapMaybeM #-}+mapMaybeM :: Monad m => (a -> m (Maybe b)) -> Stream m a -> Stream m b+mapMaybeM f = fmap fromJust . filter isJust . mapM f++-- | In a stream of 'Maybe's, discard 'Nothing's and unwrap 'Just's.+--+-- >>> catMaybes = Stream.mapMaybe id+-- >>> catMaybes = fmap fromJust . Stream.filter isJust+--+-- /Pre-release/+--+{-# INLINE catMaybes #-}+catMaybes :: Monad m => Stream m (Maybe a) -> Stream m a+-- catMaybes = fmap fromJust . filter isJust+catMaybes (Stream step state) = Stream step1 state++    where++    {-# INLINE_LATE step1 #-}+    step1 gst st = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> do+                return+                    $ case x of+                        Just a -> Yield a s+                        Nothing -> Skip s+            Skip s -> return $ Skip s+            Stop -> return Stop++-- | Use a filtering fold on a stream.+--+-- >>> scanMaybe f = Stream.catMaybes . Stream.postscan f+--+{-# INLINE scanMaybe #-}+scanMaybe :: Monad m => Fold m a (Maybe b) -> Stream m a -> Stream m b+scanMaybe f = catMaybes . postscan f++------------------------------------------------------------------------------+-- Either streams+------------------------------------------------------------------------------++-- | Discard 'Right's and unwrap 'Left's in an 'Either' stream.+--+-- >>> catLefts = fmap (fromLeft undefined) . Stream.filter isLeft+--+-- /Pre-release/+--+{-# INLINE catLefts #-}+catLefts :: Monad m => Stream m (Either a b) -> Stream m a+catLefts = fmap (fromLeft undefined) . filter isLeft++-- | Discard 'Left's and unwrap 'Right's in an 'Either' stream.+--+-- >>> catRights = fmap (fromRight undefined) . Stream.filter isRight+--+-- /Pre-release/+--+{-# INLINE catRights #-}+catRights :: Monad m => Stream m (Either a b) -> Stream m b+catRights = fmap (fromRight undefined) . filter isRight++-- | Remove the either wrapper and flatten both lefts and as well as rights in+-- the output stream.+--+-- >>> catEithers = fmap (either id id)+--+-- /Pre-release/+--+{-# INLINE catEithers #-}+catEithers :: Monad m => Stream m (Either a a) -> Stream m a+catEithers = fmap (either id id)++------------------------------------------------------------------------------+-- Splitting+------------------------------------------------------------------------------++-- | Split on an infixed separator element, dropping the separator.  The+-- supplied 'Fold' is applied on the split segments.  Splits the stream on+-- separator elements determined by the supplied predicate, separator is+-- considered as infixed between two segments:+--+-- >>> splitOn' p xs = Stream.fold Fold.toList $ Stream.splitOn p Fold.toList (Stream.fromList xs)+-- >>> splitOn' (== '.') "a.b"+-- ["a","b"]+--+-- An empty stream is folded to the default value of the fold:+--+-- >>> splitOn' (== '.') ""+-- [""]+--+-- If one or both sides of the separator are missing then the empty segment on+-- that side is folded to the default output of the fold:+--+-- >>> splitOn' (== '.') "."+-- ["",""]+--+-- >>> splitOn' (== '.') ".a"+-- ["","a"]+--+-- >>> splitOn' (== '.') "a."+-- ["a",""]+--+-- >>> splitOn' (== '.') "a..b"+-- ["a","","b"]+--+-- splitOn is an inverse of intercalating single element:+--+-- > Stream.intercalate (Stream.fromPure '.') Unfold.fromList . Stream.splitOn (== '.') Fold.toList === id+--+-- Assuming the input stream does not contain the separator:+--+-- > Stream.splitOn (== '.') Fold.toList . Stream.intercalate (Stream.fromPure '.') Unfold.fromList === id+--+{-# INLINE splitOn #-}+splitOn :: Monad m => (a -> Bool) -> Fold m a b -> Stream m a -> Stream m b+splitOn predicate f =+    -- We can express the infix splitting in terms of optional suffix split+    -- fold.  After applying a suffix split fold repeatedly if the last segment+    -- ends with a suffix then we need to return the default output of the fold+    -- after that to make it an infix split.+    --+    -- Alternately, we can also express it using an optional prefix split fold.+    -- If the first segment starts with a prefix then we need to emit the+    -- default output of the fold before that to make it an infix split, and+    -- then apply prefix split fold repeatedly.+    --+    -- Since a suffix split fold can be easily expressed using a+    -- non-backtracking fold, we use that.+    foldManyPost (FL.takeEndBy_ predicate f)
src/Streamly/Internal/Data/Stream/Transformer.hs view
@@ -1,44 +1,57 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Data.Stream.Transformer--- Copyright   : (c) 2019 Composewell Technologies+-- Copyright   : (c) 2018 Composewell Technologies -- License     : BSD-3-Clause -- Maintainer  : streamly@composewell.com -- Stability   : experimental -- Portability : GHC+--+-- Transform the underlying monad of a stream using a monad transfomer.  module Streamly.Internal.Data.Stream.Transformer     (       foldlT     , foldrT +    -- * Transform Inner Monad     , liftInner-    , usingReaderT     , runReaderT+    , usingReaderT     , evalStateT-    , usingStateT     , runStateT+    , usingStateT     ) where -import Control.Monad.Trans.Class (MonadTrans)+#include "inline.hs"++import Control.Monad.Trans.Class (MonadTrans(lift)) import Control.Monad.Trans.Reader (ReaderT) import Control.Monad.Trans.State.Strict (StateT)-import Streamly.Internal.Data.Stream.Type (Stream, fromStreamD, toStreamD)+import GHC.Types (SPEC(..))+import Streamly.Internal.Data.SVar.Type (defState, adaptState) -import qualified Streamly.Internal.Data.Stream.StreamD.Transformer as D+import qualified Control.Monad.Trans.Reader as Reader+import qualified Control.Monad.Trans.State.Strict as State --- $setup--- >>> :m--- >>> import Control.Monad.Trans.Class (lift)--- >>> import Control.Monad.Trans.Identity (runIdentityT)--- >>> import qualified Streamly.Internal.Data.Stream as Stream+import Streamly.Internal.Data.Stream.Type +#include "DocTestDataStream.hs"+ -- | Lazy left fold to a transformer monad. ---{-# INLINE foldlT #-}+{-# INLINE_NORMAL foldlT #-} foldlT :: (Monad m, Monad (s m), MonadTrans s)     => (s m b -> a -> s m b) -> s m b -> Stream m a -> s m b-foldlT f z s = D.foldlT f z (toStreamD s)+foldlT fstep begin (Stream step state) = go SPEC begin state+  where+    go !_ acc st = do+        r <- lift $ step defState st+        case r of+            Yield x s -> go SPEC (fstep acc x) s+            Skip s -> go SPEC acc s+            Stop   -> acc  -- | Right fold to a transformer monad.  This is the most general right fold -- function. 'foldrS' is a special case of 'foldrT', however 'foldrS'@@ -53,22 +66,38 @@ -- monads e.g.  to a different streaming type. -- -- /Pre-release/-{-# INLINE foldrT #-}-foldrT :: (Monad m, Monad (s m), MonadTrans s)-    => (a -> s m b -> s m b) -> s m b -> Stream m a -> s m b-foldrT f z s = D.foldrT f z (toStreamD s)+{-# INLINE_NORMAL foldrT #-}+foldrT :: (Monad m, Monad (t m), MonadTrans t)+    => (a -> t m b -> t m b) -> t m b -> Stream m a -> t m b+foldrT f final (Stream step state) = go SPEC state+  where+    {-# INLINE_LATE go #-}+    go !_ st = do+          r <- lift $ step defState st+          case r of+            Yield x s -> f x (go SPEC s)+            Skip s    -> go SPEC s+            Stop      -> final ---------------------------------------------------------------------------------- Add and remove a monad transformer-------------------------------------------------------------------------------+-------------------------------------------------------------------------------+-- Transform Inner Monad+-------------------------------------------------------------------------------  -- | Lift the inner monad @m@ of @Stream m a@ to @t m@ where @t@ is a monad -- transformer. ---{-# INLINE liftInner #-}+{-# INLINE_NORMAL liftInner #-} liftInner :: (Monad m, MonadTrans t, Monad (t m))     => Stream m a -> Stream (t m) a-liftInner xs = fromStreamD $ D.liftInner (toStreamD xs)+liftInner (Stream step state) = Stream step' state+    where+    {-# INLINE_LATE step' #-}+    step' gst st = do+        r <- lift $ step (adaptState gst) st+        return $ case r of+            Yield x s -> Yield x s+            Skip s    -> Skip s+            Stop      -> Stop  ------------------------------------------------------------------------------ -- Sharing read only state in a stream@@ -76,16 +105,21 @@  -- | Evaluate the inner monad of a stream as 'ReaderT'. ---{-# INLINE runReaderT #-}+{-# INLINE_NORMAL runReaderT #-} runReaderT :: Monad m => m s -> Stream (ReaderT s m) a -> Stream m a-runReaderT s xs = fromStreamD $ D.runReaderT s (toStreamD xs)+runReaderT env (Stream step state) = Stream step' (state, env)+    where+    {-# INLINE_LATE step' #-}+    step' gst (st, action) = do+        sv <- action+        r <- Reader.runReaderT (step (adaptState gst) st) sv+        return $ case r of+            Yield x s -> Yield x (s, return sv)+            Skip  s   -> Skip (s, return sv)+            Stop      -> Stop  -- | Run a stream transformation using a given environment. ----- See also: 'Serial.map'------ / Internal/--- {-# INLINE usingReaderT #-} usingReaderT     :: Monad m@@ -103,12 +137,34 @@ -- -- >>> evalStateT s = fmap snd . Stream.runStateT s ----- / Internal/+{-# INLINE_NORMAL evalStateT #-}+evalStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m a+evalStateT initial (Stream step state) = Stream step' (state, initial)+    where+    {-# INLINE_LATE step' #-}+    step' gst (st, action) = do+        sv <- action+        (r, !sv') <- State.runStateT (step (adaptState gst) st) sv+        return $ case r of+            Yield x s -> Yield x (s, return sv')+            Skip  s   -> Skip (s, return sv')+            Stop      -> Stop++-- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting+-- state and value pair after each step. ---{-# INLINE evalStateT #-}-evalStateT ::  Monad m => m s -> Stream (StateT s m) a -> Stream m a--- evalStateT s = fmap snd . runStateT s-evalStateT s xs = fromStreamD $ D.evalStateT s (toStreamD xs)+{-# INLINE_NORMAL runStateT #-}+runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)+runStateT initial (Stream step state) = Stream step' (state, initial)+    where+    {-# INLINE_LATE step' #-}+    step' gst (st, action) = do+        sv <- action+        (r, !sv') <- State.runStateT (step (adaptState gst) st) sv+        return $ case r of+            Yield x s -> Yield (sv', x) (s, return sv')+            Skip  s   -> Skip (s, return sv')+            Stop      -> Stop  -- | Run a stateful (StateT) stream transformation using a given state. --@@ -116,8 +172,6 @@ -- -- See also: 'scan' ----- / Internal/--- {-# INLINE usingStateT #-} usingStateT     :: Monad m@@ -126,10 +180,3 @@     -> Stream m a     -> Stream m a usingStateT s f = evalStateT s . f . liftInner---- | Evaluate the inner monad of a stream as 'StateT' and emit the resulting--- state and value pair after each step.----{-# INLINE runStateT #-}-runStateT :: Monad m => m s -> Stream (StateT s m) a -> Stream m (s, a)-runStateT s xs = fromStreamD $ D.runStateT s (toStreamD xs)
src/Streamly/Internal/Data/Stream/Type.hs view
@@ -1,491 +1,2091 @@-{-# LANGUAGE UndecidableInstances #-}---- |--- Module      : Streamly.Internal.Data.Stream.Type--- Copyright   : (c) 2017 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC----module Streamly.Internal.Data.Stream.Type-    (-    -- * Stream Type-      Stream -- XXX To be removed-    , StreamK--    -- * Type Conversion-    , fromStreamK-    , toStreamK-    , fromStreamD-    , toStreamD-    , fromStream-    , toStream-    , Streamly.Internal.Data.Stream.Type.fromList--    -- * Construction-    , cons-    , consM-    , nil-    , nilM-    , fromPure-    , fromEffect--    -- * Applicative-    , crossApply-    , crossApplySnd-    , crossApplyFst-    , crossWith-    , cross--    -- * Bind/Concat-    , bindWith-    , concatMapWith--    -- * Double folds-    , eqBy-    , cmpBy-    )-where--#include "inline.hs"--import Control.Applicative (liftA2)-import Data.Foldable (Foldable(foldl'), fold)-import Data.Functor.Identity (Identity(..), runIdentity)-import Data.Maybe (fromMaybe)-import Data.Semigroup (Endo(..))-import GHC.Exts (IsList(..), IsString(..), oneShot)-import Streamly.Internal.BaseCompat ((#.))-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)-import Text.Read-       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec-       , readListPrecDefault)--import qualified Streamly.Internal.Data.Stream.Common as P-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K---- $setup--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Data.Unfold as Unfold--- >>> import qualified Streamly.Internal.Data.Stream as Stream----------------------------------------------------------------------------------- Stream----------------------------------------------------------------------------------- | Semigroup instance appends two streams:------ >>> (<>) = Stream.append----newtype StreamK m a = StreamK (K.StreamK m a)-    -- XXX when deriving do we inherit an INLINE?-    deriving (Semigroup, Monoid)--type Stream = StreamK----------------------------------------------------------------------------------- Conversions---------------------------------------------------------------------------------{-# INLINE_EARLY fromStreamK #-}-fromStreamK :: K.StreamK m a -> Stream m a-fromStreamK = StreamK--{-# INLINE_EARLY toStreamK #-}-toStreamK :: Stream m a -> K.StreamK m a-toStreamK (StreamK k) = k--{-# INLINE_EARLY fromStreamD #-}-fromStreamD :: Monad m => D.Stream m a -> Stream m a-fromStreamD = fromStreamK . D.toStreamK--{-# INLINE_EARLY toStreamD #-}-toStreamD :: Applicative m => Stream m a -> D.Stream m a-toStreamD = D.fromStreamK . toStreamK--{-# INLINE fromStream #-}-fromStream :: Monad m => D.Stream m a -> Stream m a-fromStream = fromStreamD--{-# INLINE toStream #-}-toStream :: Applicative m => Stream m a -> D.Stream m a-toStream = toStreamD----------------------------------------------------------------------------------- Generation----------------------------------------------------------------------------------- |--- >>> fromList = Prelude.foldr Stream.cons Stream.nil------ Construct a stream from a list of pure values. This is more efficient than--- 'fromFoldable'.----{-# INLINE fromList #-}-fromList :: Monad m => [a] -> Stream m a-fromList = fromStreamK . P.fromList----------------------------------------------------------------------------------- Comparison----------------------------------------------------------------------------------- | Compare two streams for equality----{-# INLINE eqBy #-}-eqBy :: Monad m =>-    (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool-eqBy f m1 m2 = D.eqBy f (toStreamD m1) (toStreamD m2)---- | Compare two streams----{-# INLINE cmpBy #-}-cmpBy-    :: Monad m-    => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering-cmpBy f m1 m2 = D.cmpBy f (toStreamD m1) (toStreamD m2)----------------------------------------------------------------------------------- Functor---------------------------------------------------------------------------------instance Monad m => Functor (Stream m) where-    {-# INLINE fmap #-}-    -- IMPORTANT: do not use eta reduction.-    fmap f m = fromStreamD $ D.mapM (return . f) $ toStreamD m--    {-# INLINE (<$) #-}-    (<$) = fmap . const----------------------------------------------------------------------------------- Lists----------------------------------------------------------------------------------- Serial streams can act like regular lists using the Identity monad---- XXX Show instance is 10x slower compared to read, we can do much better.--- The list show instance itself is really slow.---- XXX The default definitions of "<" in the Ord instance etc. do not perform--- well, because they do not get inlined. Need to add INLINE in Ord class in--- base?--instance IsList (Stream Identity a) where-    type (Item (Stream Identity a)) = a--    {-# INLINE fromList #-}-    fromList xs = StreamK $ P.fromList xs--    {-# INLINE toList #-}-    toList (StreamK xs) = runIdentity $ P.toList xs--instance Eq a => Eq (Stream Identity a) where-    {-# INLINE (==) #-}-    (==) (StreamK xs) (StreamK ys) = runIdentity $ P.eqBy (==) xs ys--instance Ord a => Ord (Stream Identity a) where-    {-# INLINE compare #-}-    compare (StreamK xs) (StreamK ys) = runIdentity $ P.cmpBy compare xs ys--    {-# INLINE (<) #-}-    x < y =-        case compare x y of-            LT -> True-            _ -> False--    {-# INLINE (<=) #-}-    x <= y =-        case compare x y of-            GT -> False-            _ -> True--    {-# INLINE (>) #-}-    x > y =-        case compare x y of-            GT -> True-            _ -> False--    {-# INLINE (>=) #-}-    x >= y =-        case compare x y of-            LT -> False-            _ -> True--    {-# INLINE max #-}-    max x y = if x <= y then y else x--    {-# INLINE min #-}-    min x y = if x <= y then x else y--instance Show a => Show (Stream Identity a) where-    showsPrec p dl = showParen (p > 10) $-        showString "fromList " . shows (toList dl)--instance Read a => Read (Stream Identity a) where-    readPrec = parens $ prec 10 $ do-        Ident "fromList" <- lexP-        Streamly.Internal.Data.Stream.Type.fromList <$> readPrec--    readListPrec = readListPrecDefault--instance (a ~ Char) => IsString (Stream Identity a) where-    {-# INLINE fromString #-}-    fromString xs = StreamK $ P.fromList xs------------------------------------------------------------------------------------ Foldable------------------------------------------------------------------------------------ The default Foldable instance has several issues:--- 1) several definitions do not have INLINE on them, so we provide---    re-implementations with INLINE pragmas.--- 2) the definitions of sum/product/maximum/minimum are inefficient as they---    use right folds, they cannot run in constant memory. We provide---    implementations using strict left folds here.--instance (Foldable m, Monad m) => Foldable (Stream m) where--    {-# INLINE foldMap #-}-    foldMap f (StreamK xs) = fold $ P.foldr (mappend . f) mempty xs--    {-# INLINE foldr #-}-    foldr f z t = appEndo (foldMap (Endo #. f) t) z--    {-# INLINE foldl' #-}-    foldl' f z0 xs = foldr f' id xs z0-        where f' x k = oneShot $ \z -> k $! f z x--    {-# INLINE length #-}-    length = foldl' (\n _ -> n + 1) 0--    {-# INLINE elem #-}-    elem = any . (==)--    {-# INLINE maximum #-}-    maximum =-          fromMaybe (errorWithoutStackTrace "maximum: empty stream")-        . toMaybe-        . foldl' getMax Nothing'--        where--        getMax Nothing' x = Just' x-        getMax (Just' mx) x = Just' $! max mx x--    {-# INLINE minimum #-}-    minimum =-          fromMaybe (errorWithoutStackTrace "minimum: empty stream")-        . toMaybe-        . foldl' getMin Nothing'--        where--        getMin Nothing' x = Just' x-        getMin (Just' mn) x = Just' $! min mn x--    {-# INLINE sum #-}-    sum = foldl' (+) 0--    {-# INLINE product #-}-    product = foldl' (*) 1------------------------------------------------------------------------------------ Traversable----------------------------------------------------------------------------------instance Traversable (Stream Identity) where-    {-# INLINE traverse #-}-    traverse f (StreamK xs) =-        fmap StreamK $ runIdentity $ P.foldr consA (pure mempty) xs--        where--        consA x ys = liftA2 K.cons (f x) ys------------------------------------------------------------------------------------ Construction----------------------------------------------------------------------------------infixr 5 `cons`---- | A right associative prepend operation to add a pure value at the head of--- an existing stream::------ >>> s = 1 `Stream.cons` 2 `Stream.cons` 3 `Stream.cons` Stream.nil--- >>> Stream.fold Fold.toList s--- [1,2,3]------ It can be used efficiently with 'Prelude.foldr':------ >>> fromFoldable = Prelude.foldr Stream.cons Stream.nil------ Same as the following but more efficient:------ >>> cons x xs = return x `Stream.consM` xs------ /CPS/----{-# INLINE_NORMAL cons #-}-cons ::  a -> Stream m a -> Stream m a-cons x = fromStreamK . K.cons x . toStreamK--infixr 5 `consM`---- | A right associative prepend operation to add an effectful value at the--- head of an existing stream::------ >>> s = putStrLn "hello" `consM` putStrLn "world" `consM` Stream.nil--- >>> Stream.fold Fold.drain s--- hello--- world------ It can be used efficiently with 'Prelude.foldr':------ >>> fromFoldableM = Prelude.foldr Stream.consM Stream.nil------ Same as the following but more efficient:------ >>> consM x xs = Stream.fromEffect x `Stream.append` xs------ /CPS/----{-# INLINE consM #-}-{-# SPECIALIZE consM :: IO a -> Stream IO a -> Stream IO a #-}-consM :: Monad m => m a -> Stream m a -> Stream m a-consM m = fromStreamK . K.consM m . toStreamK---- | A stream that terminates without producing any output or side effect.------ >>> Stream.fold Fold.toList Stream.nil--- []----{-# INLINE_NORMAL nil #-}-nil ::  Stream m a-nil = fromStreamK K.nil---- | A stream that terminates without producing any output, but produces a side--- effect.------ >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))--- "nil"--- []------ /Pre-release/-{-# INLINE_NORMAL nilM #-}-nilM :: Monad m => m b -> Stream m a-nilM = fromStreamK . K.nilM---- | Create a singleton stream from a pure value.------ >>> fromPure a = a `cons` Stream.nil--- >>> fromPure = pure--- >>> fromPure = fromEffect . pure----{-# INLINE_NORMAL fromPure #-}-fromPure :: a -> Stream m a-fromPure = fromStreamK . K.fromPure---- | Create a singleton stream from a monadic action.------ >>> fromEffect m = m `consM` Stream.nil--- >>> fromEffect = Stream.sequence . Stream.fromPure------ >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")--- hello----{-# INLINE_NORMAL fromEffect #-}-fromEffect :: Monad m => m a -> Stream m a-fromEffect = fromStreamK . K.fromEffect------------------------------------------------------------------------------------ Applicative------------------------------------------------------------------------------------ | Apply a stream of functions to a stream of values and flatten the results.------ Note that the second stream is evaluated multiple times.------ >>> crossApply = Stream.crossWith id----{-# INLINE crossApply #-}-crossApply :: Stream m (a -> b) -> Stream m a -> Stream m b-crossApply m1 m2 =-    fromStreamK $ K.crossApply (toStreamK m1) (toStreamK m2)--{-# INLINE crossApplySnd #-}-crossApplySnd :: Stream m a -> Stream m b -> Stream m b-crossApplySnd m1 m2 =-    fromStreamK $ K.crossApplySnd (toStreamK m1) (toStreamK m2)--{-# INLINE crossApplyFst #-}-crossApplyFst :: Stream m a -> Stream m b -> Stream m a-crossApplyFst m1 m2 =-    fromStreamK $ K.crossApplyFst (toStreamK m1) (toStreamK m2)---- |--- Definition:------ >>> crossWith f m1 m2 = fmap f m1 `Stream.crossApply` m2------ Note that the second stream is evaluated multiple times.----{-# INLINE crossWith #-}-crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c-crossWith f m1 m2 = fmap f m1 `crossApply` m2---- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible--- combinations of the tuple @(a, b)@.------ Definition:------ >>> cross = Stream.crossWith (,)------ The second stream is evaluated multiple times. If that is not desired it can--- be cached in an 'Data.Array.Array' and then generated from the array before--- calling this function. Caching may also improve performance if the stream is--- expensive to evaluate.------ See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused--- alternative.------ Time: O(m x n)------ /Pre-release/-{-# INLINE cross #-}-cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)-cross = crossWith (,)------------------------------------------------------------------------------------ Bind/Concat------------------------------------------------------------------------------------ |------ /CPS/-{-# INLINE bindWith #-}-bindWith-    :: (Stream m b -> Stream m b -> Stream m b)-    -> Stream m a-    -> (a -> Stream m b)-    -> Stream m b-bindWith par m1 f =-    fromStreamK-        $ K.bindWith-            (\s1 s2 -> toStreamK $ par (fromStreamK s1) (fromStreamK s2))-            (toStreamK m1)-            (toStreamK . f)---- | @concatMapWith mixer generator stream@ is a two dimensional looping--- combinator.  The @generator@ function is used to generate streams from the--- elements in the input @stream@ and the @mixer@ function is used to merge--- those streams.------ /CPS/-{-# INLINE concatMapWith #-}-concatMapWith-    :: (Stream m b -> Stream m b -> Stream m b)-    -> (a -> Stream m b)-    -> Stream m a-    -> Stream m b-concatMapWith par f xs = bindWith par xs f+{-# LANGUAGE CPP #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module      : Streamly.Internal.Data.Stream.Type+-- Copyright   : (c) 2018 Composewell Technologies+--               (c) Roman Leshchinskiy 2008-2010+-- License     : BSD-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC++-- The stream type is inspired by the vector package.  A few functions in this+-- module have been originally adapted from the vector package (c) Roman+-- Leshchinskiy. See the notes in specific functions.++module Streamly.Internal.Data.Stream.Type+    (+    -- * The stream type+      Step (..)+    -- XXX UnStream is exported to avoid a performance issue in some+    -- combinators if we use the pattern synonym "Stream".+    , Stream (Stream, UnStream)++    -- * CrossStream type wrapper+    , CrossStream+    , unCross+    , mkCross++    -- * Conversion to StreamK+    , fromStreamK+    , toStreamK++    -- * From Unfold+    , unfold++    -- * Construction+    -- ** Primitives+    , nilM+    , consM++    -- ** From Values+    , fromPure+    , fromEffect++    -- ** From Containers+    , Streamly.Internal.Data.Stream.Type.fromList++    -- * Elimination+    -- ** Primitives+    , uncons++    -- ** Strict Left Folds+    , Streamly.Internal.Data.Stream.Type.fold+    , foldBreak+    , foldAddLazy+    , foldAdd+    , foldEither++    , Streamly.Internal.Data.Stream.Type.foldl'+    , foldlM'+    , foldlx'+    , foldlMx'++    -- ** Lazy Right Folds+    , foldrM+    , foldrMx+    , Streamly.Internal.Data.Stream.Type.foldr+    , foldrS++    -- ** Specific Folds+    , drain+    , Streamly.Internal.Data.Stream.Type.toList++    -- * Mapping+    , map+    , mapM++    -- * Stateful Filters+    , take+    , takeWhile+    , takeWhileM+    , takeEndBy+    , takeEndByM++    -- * Combining Two Streams+    -- ** Zipping+    , zipWithM+    , zipWith++    -- ** Cross Product+    , crossApply+    , crossApplyFst+    , crossApplySnd+    , crossWith+    , cross++    -- * Unfold Many+    , ConcatMapUState (..)+    , unfoldMany++    -- * Concat+    , concatEffect+    , concatMap+    , concatMapM+    , concat++    -- * Unfold Iterate+    , unfoldIterateDfs+    , unfoldIterateBfs+    , unfoldIterateBfsRev++    -- * Concat Iterate+    , concatIterateScan+    , concatIterateDfs+    , concatIterateBfs+    , concatIterateBfsRev++    -- * Fold Many+    , FoldMany (..) -- for inspection testing+    , FoldManyPost (..)+    , foldMany+    , foldManyPost+    , groupsOf+    , refoldMany++    -- * Fold Iterate+    , reduceIterateBfs+    , foldIterateBfs++    -- * Multi-stream folds+    , eqBy+    , cmpBy+    )+where++#include "inline.hs"++#if !MIN_VERSION_base(4,18,0)+import Control.Applicative (liftA2)+#endif+import Control.Monad.Catch (MonadThrow, throwM)+import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.IO.Class (MonadIO(..))+import Data.Foldable (Foldable(foldl'), fold, foldr)+import Data.Functor (($>))+import Data.Functor.Identity (Identity(..))+import Data.Maybe (fromMaybe)+import Data.Semigroup (Endo(..))+import Fusion.Plugin.Types (Fuse(..))+import GHC.Base (build)+import GHC.Exts (IsList(..), IsString(..), oneShot)+import GHC.Types (SPEC(..))+import Prelude hiding (map, mapM, take, concatMap, takeWhile, zipWith, concat)+import Text.Read+       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec+       , readListPrecDefault)++import Streamly.Internal.BaseCompat ((#.))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)+import Streamly.Internal.Data.Refold.Type (Refold(..))+import Streamly.Internal.Data.Stream.Step (Step (..))+import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)+import Streamly.Internal.Data.Unfold.Type (Unfold(..))++import qualified Streamly.Internal.Data.Fold.Type as FL hiding (foldr)+import qualified Streamly.Internal.Data.StreamK.Type as K+#ifdef USE_UNFOLDS_EVERYWHERE+import qualified Streamly.Internal.Data.Unfold.Type as Unfold+#endif++#include "DocTestDataStream.hs"++------------------------------------------------------------------------------+-- The direct style stream type+------------------------------------------------------------------------------++-- gst = global state++-- | A stream consists of a step function that generates the next step given a+-- current state, and the current state.+data Stream m a =+    forall s. UnStream (State K.StreamK m a -> s -> m (Step s a)) s++-- XXX This causes perf trouble when pattern matching with "Stream"  in a+-- recursive way, e.g. in uncons, foldBreak, concatMap. We need to get rid of+-- this.+unShare :: Stream m a -> Stream m a+unShare (UnStream step state) = UnStream step' state+    where step' gst = step (adaptState gst)++pattern Stream :: (State K.StreamK m a -> s -> m (Step s a)) -> s -> Stream m a+pattern Stream step state <- (unShare -> UnStream step state)+    where Stream = UnStream++{-# COMPLETE Stream #-}++------------------------------------------------------------------------------+-- Primitives+------------------------------------------------------------------------------++-- | A stream that terminates without producing any output, but produces a side+-- effect.+--+-- >>> Stream.fold Fold.toList (Stream.nilM (print "nil"))+-- "nil"+-- []+--+-- /Pre-release/+{-# INLINE_NORMAL nilM #-}+nilM :: Applicative m => m b -> Stream m a+nilM m = Stream (\_ _ -> m $> Stop) ()++-- | Like 'cons' but fuses an effect instead of a pure value.+{-# INLINE_NORMAL consM #-}+consM :: Applicative m => m a -> Stream m a -> Stream m a+consM m (Stream step state) = Stream step1 Nothing++    where++    {-# INLINE_LATE step1 #-}+    step1 _ Nothing = (`Yield` Just state) <$> m+    step1 gst (Just st) = do+          (\case+            Yield a s -> Yield a (Just s)+            Skip  s   -> Skip (Just s)+            Stop      -> Stop) <$> step gst st++-- | Decompose a stream into its head and tail. If the stream is empty, returns+-- 'Nothing'. If the stream is non-empty, returns @Just (a, ma)@, where @a@ is+-- the head of the stream and @ma@ its tail.+--+-- Properties:+--+-- >>> Nothing <- Stream.uncons Stream.nil+-- >>> Just ("a", t) <- Stream.uncons (Stream.cons "a" Stream.nil)+--+-- This can be used to consume the stream in an imperative manner one element+-- at a time, as it just breaks down the stream into individual elements and we+-- can loop over them as we deem fit. For example, this can be used to convert+-- a streamly stream into other stream types.+--+-- All the folds in this module can be expressed in terms of 'uncons', however,+-- this is generally less efficient than specific folds because it takes apart+-- the stream one element at a time, therefore, does not take adavantage of+-- stream fusion.+--+-- 'foldBreak' is a more general way of consuming a stream piecemeal.+--+-- >>> :{+-- uncons xs = do+--     r <- Stream.foldBreak Fold.one xs+--     return $ case r of+--         (Nothing, _) -> Nothing+--         (Just h, t) -> Just (h, t)+-- :}+--+{-# INLINE_NORMAL uncons #-}+uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))+uncons (UnStream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield x s -> return $ Just (x, Stream step s)+            Skip  s   -> go SPEC s+            Stop      -> return Nothing++------------------------------------------------------------------------------+-- From 'Unfold'+------------------------------------------------------------------------------++data UnfoldState s = UnfoldNothing | UnfoldJust s++-- | Convert an 'Unfold' into a stream by supplying it an input seed.+--+-- >>> s = Stream.unfold Unfold.replicateM (3, putStrLn "hello")+-- >>> Stream.fold Fold.drain s+-- hello+-- hello+-- 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++------------------------------------------------------------------------------+-- From Values+------------------------------------------------------------------------------++-- | Create a singleton stream from a pure value.+--+-- >>> fromPure a = a `Stream.cons` Stream.nil+-- >>> fromPure = pure+-- >>> fromPure = Stream.fromEffect . pure+--+{-# INLINE_NORMAL fromPure #-}+fromPure :: Applicative m => a -> Stream m a+fromPure x = Stream (\_ s -> pure $ step undefined s) True+  where+    {-# INLINE_LATE step #-}+    step _ True  = Yield x False+    step _ False = Stop++-- | Create a singleton stream from a monadic action.+--+-- >>> fromEffect m = m `Stream.consM` Stream.nil+-- >>> fromEffect = Stream.sequence . Stream.fromPure+--+-- >>> Stream.fold Fold.drain $ Stream.fromEffect (putStrLn "hello")+-- hello+--+{-# INLINE_NORMAL fromEffect #-}+fromEffect :: Applicative m => m a -> Stream m a+fromEffect m = Stream step True++    where++    {-# INLINE_LATE step #-}+    step _ True  = (`Yield` False) <$> m+    step _ False = pure Stop++------------------------------------------------------------------------------+-- From Containers+------------------------------------------------------------------------------++-- Adapted from the vector package.++-- | Construct a stream from a list of pure values.+{-# INLINE_LATE fromList #-}+fromList :: Applicative m => [a] -> Stream m a+#ifdef USE_UNFOLDS_EVERYWHERE+fromList = unfold Unfold.fromList+#else+fromList = Stream step+  where+    {-# INLINE_LATE step #-}+    step _ (x:xs) = pure $ Yield x xs+    step _ []     = pure Stop+#endif++------------------------------------------------------------------------------+-- Conversions From/To+------------------------------------------------------------------------------++-- | Convert a CPS encoded StreamK to direct style step encoded StreamD+{-# INLINE_LATE fromStreamK #-}+fromStreamK :: Applicative m => K.StreamK m a -> Stream m a+fromStreamK = Stream step+    where+    step gst m1 =+        let stop       = pure Stop+            single a   = pure $ Yield a K.nil+            yieldk a r = pure $ Yield a r+         in K.foldStreamShared gst yieldk single stop m1++-- | Convert a direct style step encoded StreamD to a CPS encoded StreamK+{-# INLINE_LATE toStreamK #-}+toStreamK :: Monad m => Stream m a -> K.StreamK m a+toStreamK (Stream step state) = go state+    where+    go st = K.MkStream $ \gst yld _ stp ->+      let go' ss = do+           r <- step gst ss+           case r of+               Yield x s -> yld x (go s)+               Skip  s   -> go' s+               Stop      -> stp+      in go' st++{-# RULES "fromStreamK/toStreamK fusion"+    forall s. toStreamK (fromStreamK s) = s #-}+{-# RULES "toStreamK/fromStreamK fusion"+    forall s. fromStreamK (toStreamK s) = s #-}++------------------------------------------------------------------------------+-- Running a 'Fold'+------------------------------------------------------------------------------++-- | Fold resulting in either breaking the stream or continuation of the fold.+-- Instead of supplying the input stream in one go we can run the fold multiple+-- times, each time supplying the next segment of the input stream. If the fold+-- has not yet finished it returns a fold that can be run again otherwise it+-- returns the fold result and the residual stream.+--+-- /Internal/+{-# INLINE_NORMAL foldEither #-}+foldEither :: Monad m =>+    Fold m a b -> Stream m a -> m (Either (Fold m a b) (b, Stream m a))+foldEither (Fold fstep begin done final) (UnStream step state) = do+    res <- begin+    case res of+        FL.Partial fs -> go SPEC fs state+        FL.Done fb -> return $! Right (fb, Stream step state)++    where++    {-# INLINE go #-}+    go !_ !fs st = do+        r <- step defState st+        case r of+            Yield x s -> do+                res <- fstep fs x+                case res of+                    FL.Done b -> return $! Right (b, Stream step s)+                    FL.Partial fs1 -> go SPEC fs1 s+            Skip s -> go SPEC fs s+            Stop ->+                let f = Fold fstep (return $ FL.Partial fs) done final+                 in return $! Left f++-- | Like 'fold' but also returns the remaining stream. The resulting stream+-- would be 'Stream.nil' if the stream finished before the fold.+--+{-# INLINE_NORMAL foldBreak #-}+foldBreak :: Monad m => Fold m a b -> Stream m a -> m (b, Stream m a)+foldBreak fld strm = do+    r <- foldEither fld strm+    case r of+        Right res -> return res+        Left (Fold _ initial _ final) -> do+            res <- initial+            case res of+                FL.Done _ -> error "foldBreak: unreachable state"+                FL.Partial s -> do+                    b <- final s+                    return (b, nil)++    where++    nil = Stream (\_ _ -> return Stop) ()++-- >>> fold f = Fold.extractM . Stream.foldAddLazy f+-- >>> fold f = Stream.fold Fold.one . Stream.foldManyPost f+-- >>> fold f = Fold.extractM <=< Stream.foldAdd f++-- | Fold a stream using the supplied left 'Fold' and reducing the resulting+-- expression strictly at each step. The behavior is similar to 'foldl''. A+-- 'Fold' can terminate early without consuming the full stream. See the+-- documentation of individual 'Fold's for termination behavior.+--+-- Definitions:+--+-- >>> fold f = fmap fst . Stream.foldBreak f+-- >>> fold f = Stream.parse (Parser.fromFold f)+--+-- Example:+--+-- >>> Stream.fold Fold.sum (Stream.enumerateFromTo 1 100)+-- 5050+--+{-# INLINE_NORMAL fold #-}+fold :: Monad m => Fold m a b -> Stream m a -> m b+fold fld strm = do+    (b, _) <- foldBreak fld strm+    return b++-- | Append a stream to a fold lazily to build an accumulator incrementally.+--+-- Example, to continue folding a list of streams on the same sum fold:+--+-- >>> streams = [Stream.fromList [1..5], Stream.fromList [6..10]]+-- >>> f = Prelude.foldl Stream.foldAddLazy Fold.sum streams+-- >>> Stream.fold f Stream.nil+-- 55+--+{-# INLINE_NORMAL foldAddLazy #-}+foldAddLazy :: Monad m => Fold m a b -> Stream m a -> Fold m a b+foldAddLazy (Fold fstep finitial fextract ffinal) (Stream sstep state) =+    Fold fstep initial fextract ffinal++    where++    initial = do+        res <- finitial+        case res of+            FL.Partial fs -> go SPEC fs state+            FL.Done fb -> return $ FL.Done fb++    {-# INLINE go #-}+    go !_ !fs st = do+        r <- sstep defState st+        case r of+            Yield x s -> do+                res <- fstep fs x+                case res of+                    FL.Done b -> return $ FL.Done b+                    FL.Partial fs1 -> go SPEC fs1 s+            Skip s -> go SPEC fs s+            Stop -> return $ FL.Partial fs++-- >>> foldAdd f = Stream.foldAddLazy f >=> Fold.reduce++-- |+-- >>> foldAdd = flip Fold.addStream+--+foldAdd :: Monad m => Fold m a b -> Stream m a -> m (Fold m a b)+foldAdd f =+    Streamly.Internal.Data.Stream.Type.fold (FL.duplicate f)++------------------------------------------------------------------------------+-- Right Folds+------------------------------------------------------------------------------++-- Adapted from the vector package.+--+-- XXX Use of SPEC constructor in folds causes 2x performance degradation in+-- one shot operations, but helps immensely in operations composed of multiple+-- combinators or the same combinator many times. There seems to be an+-- opportunity to optimize here, can we get both, better perf for single ops+-- as well as composed ops? Without SPEC, all single operation benchmarks+-- become 2x faster.++-- The way we want a left fold to be strict, dually we want the right fold to+-- be lazy.  The correct signature of the fold function to keep it lazy must be+-- (a -> m b -> m b) instead of (a -> b -> m b). We were using the latter+-- earlier, which is incorrect. In the latter signature we have to feed the+-- value to the fold function after evaluating the monadic action, depending on+-- the bind behavior of the monad, the action may get evaluated immediately+-- introducing unnecessary strictness to the fold. If the implementation is+-- lazy the following example, must work:+--+-- S.foldrM (\x t -> if x then return t else return False) (return True)+--  (S.fromList [False,undefined] :: Stream IO Bool)++-- | Right associative/lazy pull fold. @foldrM build final stream@ constructs+-- an output structure using the step function @build@. @build@ is invoked with+-- the next input element and the remaining (lazy) tail of the output+-- structure. It builds a lazy output expression using the two. When the "tail+-- structure" in the output expression is evaluated it calls @build@ again thus+-- lazily consuming the input @stream@ until either the output expression built+-- by @build@ is free of the "tail" or the input is exhausted in which case+-- @final@ is used as the terminating case for the output structure. For more+-- details see the description in the previous section.+--+-- Example, determine if any element is 'odd' in a stream:+--+-- >>> s = Stream.fromList (2:4:5:undefined)+-- >>> step x xs = if odd x then return True else xs+-- >>> Stream.foldrM step (return False) s+-- True+--+{-# INLINE_NORMAL foldrM #-}+foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b+foldrM f z (Stream step state) = go SPEC state+  where+    {-# INLINE_LATE go #-}+    go !_ st = do+          r <- step defState st+          case r of+            Yield x s -> f x (go SPEC s)+            Skip s    -> go SPEC s+            Stop      -> z++{-# INLINE_NORMAL foldrMx #-}+foldrMx :: Monad m+    => (a -> m x -> m x) -> m x -> (m x -> m b) -> Stream m a -> m b+foldrMx fstep final convert (Stream step state) = convert $ go SPEC state+  where+    {-# INLINE_LATE go #-}+    go !_ st = do+          r <- step defState st+          case r of+            Yield x s -> fstep x (go SPEC s)+            Skip s    -> go SPEC s+            Stop      -> final++-- XXX Should we make all argument strict wherever we use SPEC?++-- Note that foldr works on pure values, therefore it becomes necessarily+-- strict when the monad m is strict. In that case it cannot terminate early,+-- it would evaluate all of its input.  Though, this should work fine with lazy+-- monads. For example, if "any" is implemented using "foldr" instead of+-- "foldrM" it performs the same with Identity monad but performs 1000x slower+-- with IO monad.++-- | Right fold, lazy for lazy monads and pure streams, and strict for strict+-- monads.+--+-- Please avoid using this routine in strict monads like IO unless you need a+-- strict right fold. This is provided only for use in lazy monads (e.g.+-- Identity) or pure streams. Note that with this signature it is not possible+-- to implement a lazy foldr when the monad @m@ is strict. In that case it+-- would be strict in its accumulator and therefore would necessarily consume+-- all its input.+--+-- >>> foldr f z = Stream.foldrM (\a b -> f a <$> b) (return z)+--+-- Note: This is similar to Fold.foldr' (the right fold via left fold), but+-- could be more efficient.+--+{-# INLINE_NORMAL foldr #-}+foldr :: Monad m => (a -> b -> b) -> b -> Stream m a -> m b+foldr f z = foldrM (liftA2 f . return) (return z)++-- this performs horribly, should not be used+{-# INLINE_NORMAL foldrS #-}+foldrS+    :: Monad m+    => (a -> Stream m b -> Stream m b)+    -> Stream m b+    -> Stream m a+    -> Stream m b+foldrS f final (Stream step state) = go SPEC state+  where+    {-# INLINE_LATE go #-}+    go !_ st = concatEffect $ fmap g $ step defState st++    g r =+        case r of+          Yield x s -> f x (go SPEC s)+          Skip s    -> go SPEC s+          Stop      -> final++------------------------------------------------------------------------------+-- Left Folds+------------------------------------------------------------------------------++-- XXX run begin action only if the stream is not empty.+{-# INLINE_NORMAL foldlMx' #-}+foldlMx' :: Monad m => (x -> a -> m x) -> m x -> (x -> m b) -> Stream m a -> m b+foldlMx' fstep begin done (Stream step state) =+    begin >>= \x -> go SPEC x state+  where+    -- XXX !acc?+    {-# INLINE_LATE go #-}+    go !_ acc st = acc `seq` do+        r <- step defState st+        case r of+            Yield x s -> do+                acc' <- fstep acc x+                go SPEC acc' s+            Skip s -> go SPEC acc s+            Stop   -> done acc++{-# INLINE foldlx' #-}+foldlx' :: Monad m => (x -> a -> x) -> x -> (x -> b) -> Stream m a -> m b+foldlx' fstep begin done =+    foldlMx' (\b a -> return (fstep b a)) (return begin) (return . done)++-- Adapted from the vector package.+-- XXX implement in terms of foldlMx'?+{-# INLINE_NORMAL foldlM' #-}+foldlM' :: Monad m => (b -> a -> m b) -> m b -> Stream m a -> m b+foldlM' fstep mbegin (Stream step state) = do+    begin <- mbegin+    go SPEC begin state+  where+    {-# INLINE_LATE go #-}+    go !_ acc st = acc `seq` do+        r <- step defState st+        case r of+            Yield x s -> do+                acc' <- fstep acc x+                go SPEC acc' s+            Skip s -> go SPEC acc s+            Stop   -> return acc++{-# INLINE foldl' #-}+foldl' :: Monad m => (b -> a -> b) -> b -> Stream m a -> m b+foldl' fstep begin = foldlM' (\b a -> return (fstep b a)) (return begin)++------------------------------------------------------------------------------+-- Special folds+------------------------------------------------------------------------------++-- >>> drain = mapM_ (\_ -> return ())++-- |+-- Definitions:+--+-- >>> drain = Stream.fold Fold.drain+-- >>> drain = Stream.foldrM (\_ xs -> xs) (return ())+--+-- Run a stream, discarding the results.+--+{-# INLINE_LATE drain #-}+drain :: Monad m => Stream m a -> m ()+-- drain = foldrM (\_ xs -> xs) (return ())+drain (Stream step state) = go SPEC state+  where+    go !_ st = do+        r <- step defState st+        case r of+            Yield _ s -> go SPEC s+            Skip s    -> go SPEC s+            Stop      -> return ()++------------------------------------------------------------------------------+-- To Containers+------------------------------------------------------------------------------++-- This toList impl is faster (30% on streaming-benchmarks) than the+-- corresponding left fold. The left fold retains an additional argument in the+-- recursive loop.+--+-- Core for the right fold loop:+--+-- main_$s$wgo1+--   = \ sc_s3e6 sc1_s3e5 ->+--       case ># sc1_s3e5 100000# of {+--         __DEFAULT ->+--           case main_$s$wgo1 sc_s3e6 (+# sc1_s3e5 1#) of+--+-- Core for the left fold loop:+--+--  main_$s$wgo1+--   = \ sc_s3oT sc1_s3oS sc2_s3oR ->+--       case sc2_s3oR of fs2_a2lw { __DEFAULT ->+--       case ># sc1_s3oS 100000# of {+--         __DEFAULT ->+--           let { wild_a2og = I# sc1_s3oS } in+--           main_$s$wgo1+--             sc_s3oT (+# sc1_s3oS 1#) (\ x_X9 -> fs2_a2lw (: wild_a2og x_X9));++-- |+-- Definitions:+--+-- >>> toList = Stream.foldr (:) []+-- >>> toList = Stream.fold Fold.toList+--+-- Convert a stream into a list in the underlying monad. The list can be+-- consumed lazily in a lazy monad (e.g. 'Identity'). In a strict monad (e.g.+-- IO) the whole list is generated and buffered before it can be consumed.+--+-- /Warning!/ working on large lists accumulated as buffers in memory could be+-- very inefficient, consider using "Streamly.Data.Array" instead.+--+-- Note that this could a bit more efficient compared to @Stream.fold+-- Fold.toList@, and it can fuse with pure list consumers.+--+{-# INLINE_NORMAL toList #-}+toList :: Monad m => Stream m a -> m [a]+toList = Streamly.Internal.Data.Stream.Type.foldr (:) []++-- Use foldr/build fusion to fuse with list consumers+-- This can be useful when using the IsList instance+{-# INLINE_LATE toListFB #-}+toListFB :: (a -> b -> b) -> b -> Stream Identity a -> b+toListFB c n (Stream step state) = go state+  where+    go st = case runIdentity (step defState st) of+             Yield x s -> x `c` go s+             Skip s    -> go s+             Stop      -> n++{-# RULES "toList Identity" Streamly.Internal.Data.Stream.Type.toList = toListId #-}+{-# INLINE_EARLY toListId #-}+toListId :: Stream Identity a -> Identity [a]+toListId s = Identity $ build (\c n -> toListFB c n s)++------------------------------------------------------------------------------+-- Multi-stream folds+------------------------------------------------------------------------------++-- Adapted from the vector package.++-- | Compare two streams for equality+{-# INLINE_NORMAL eqBy #-}+eqBy :: Monad m => (a -> b -> Bool) -> Stream m a -> Stream m b -> m Bool+eqBy eq (Stream step1 t1) (Stream step2 t2) = eq_loop0 SPEC t1 t2+  where+    eq_loop0 !_ s1 s2 = do+      r <- step1 defState s1+      case r of+        Yield x s1' -> eq_loop1 SPEC x s1' s2+        Skip    s1' -> eq_loop0 SPEC   s1' s2+        Stop        -> eq_null s2++    eq_loop1 !_ x s1 s2 = do+      r <- step2 defState s2+      case r of+        Yield y s2'+          | eq x y    -> eq_loop0 SPEC   s1 s2'+          | otherwise -> return False+        Skip    s2'   -> eq_loop1 SPEC x s1 s2'+        Stop          -> return False++    eq_null s2 = do+      r <- step2 defState s2+      case r of+        Yield _ _ -> return False+        Skip s2'  -> eq_null s2'+        Stop      -> return True++-- Adapted from the vector package.++-- | Compare two streams lexicographically.+{-# INLINE_NORMAL cmpBy #-}+cmpBy+    :: Monad m+    => (a -> b -> Ordering) -> Stream m a -> Stream m b -> m Ordering+cmpBy cmp (Stream step1 t1) (Stream step2 t2) = cmp_loop0 SPEC t1 t2+  where+    cmp_loop0 !_ s1 s2 = do+      r <- step1 defState s1+      case r of+        Yield x s1' -> cmp_loop1 SPEC x s1' s2+        Skip    s1' -> cmp_loop0 SPEC   s1' s2+        Stop        -> cmp_null s2++    cmp_loop1 !_ x s1 s2 = do+      r <- step2 defState s2+      case r of+        Yield y s2' -> case x `cmp` y of+                         EQ -> cmp_loop0 SPEC s1 s2'+                         c  -> return c+        Skip    s2' -> cmp_loop1 SPEC x s1 s2'+        Stop        -> return GT++    cmp_null s2 = do+      r <- step2 defState s2+      case r of+        Yield _ _ -> return LT+        Skip s2'  -> cmp_null s2'+        Stop      -> return EQ++------------------------------------------------------------------------------+-- Transformations+------------------------------------------------------------------------------++-- Adapted from the vector package.++-- |+-- >>> mapM f = Stream.sequence . fmap f+--+-- Apply a monadic function to each element of the stream and replace it with+-- the output of the resulting action.+--+-- >>> s = Stream.fromList ["a", "b", "c"]+-- >>> Stream.fold Fold.drain $ Stream.mapM putStr s+-- abc+--+{-# INLINE_NORMAL mapM #-}+mapM :: Monad m => (a -> m b) -> Stream m a -> Stream m b+mapM f (Stream step state) = Stream step' state+  where+    {-# INLINE_LATE step' #-}+    step' gst st = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> f x >>= \a -> return $ Yield a s+            Skip s    -> return $ Skip s+            Stop      -> return Stop++{-# INLINE map #-}+map :: Monad m => (a -> b) -> Stream m a -> Stream m b+map f = mapM (return . f)++-- (Functor m) based implementation of fmap does not fuse well in+-- streaming-benchmarks. XXX need to investigate why.+instance Monad m => Functor (Stream m) where+    {-# INLINE fmap #-}+    fmap = map++    {-# INLINE (<$) #-}+    (<$) = fmap . const++------------------------------------------------------------------------------+-- Lists+------------------------------------------------------------------------------++-- XXX Show instance is 10x slower compared to read, we can do much better.+-- The list show instance itself is really slow.++-- XXX The default definitions of "<" in the Ord instance etc. do not perform+-- well, because they do not get inlined. Need to add INLINE in Ord class in+-- base?++instance IsList (Stream Identity a) where+    type (Item (Stream Identity a)) = a++    {-# INLINE fromList #-}+    fromList = Streamly.Internal.Data.Stream.Type.fromList++    {-# INLINE toList #-}+    toList = runIdentity . Streamly.Internal.Data.Stream.Type.toList++instance Eq a => Eq (Stream Identity a) where+    {-# INLINE (==) #-}+    (==) xs ys = runIdentity $ eqBy (==) xs ys++instance Ord a => Ord (Stream Identity a) where+    {-# INLINE compare #-}+    compare xs ys = runIdentity $ cmpBy compare xs ys++    {-# INLINE (<) #-}+    x < y =+        case compare x y of+            LT -> True+            _ -> False++    {-# INLINE (<=) #-}+    x <= y =+        case compare x y of+            GT -> False+            _ -> True++    {-# INLINE (>) #-}+    x > y =+        case compare x y of+            GT -> True+            _ -> False++    {-# INLINE (>=) #-}+    x >= y =+        case compare x y of+            LT -> False+            _ -> True++    {-# INLINE max #-}+    max x y = if x <= y then y else x++    {-# INLINE min #-}+    min x y = if x <= y then x else y++instance Show a => Show (Stream Identity a) where+    showsPrec p dl = showParen (p > 10) $+        showString "fromList " . shows (GHC.Exts.toList dl)++instance Read a => Read (Stream Identity a) where+    readPrec = parens $ prec 10 $ do+        Ident "fromList" <- lexP+        Streamly.Internal.Data.Stream.Type.fromList <$> readPrec++    readListPrec = readListPrecDefault++instance (a ~ Char) => IsString (Stream Identity a) where+    {-# INLINE fromString #-}+    fromString = Streamly.Internal.Data.Stream.Type.fromList++-------------------------------------------------------------------------------+-- Foldable+-------------------------------------------------------------------------------++-- The default Foldable instance has several issues:+-- 1) several definitions do not have INLINE on them, so we provide+--    re-implementations with INLINE pragmas.+-- 2) the definitions of sum/product/maximum/minimum are inefficient as they+--    use right folds, they cannot run in constant memory. We provide+--    implementations using strict left folds here.++-- There is no Traversable instance because, there is no scalable cons for+-- StreamD, use toList and fromList instead.++instance (Foldable m, Monad m) => Foldable (Stream m) where++    {-# INLINE foldMap #-}+    foldMap f =+        Data.Foldable.fold+            . Streamly.Internal.Data.Stream.Type.foldr (mappend . f) mempty++    {-# INLINE foldr #-}+    foldr f z t = appEndo (foldMap (Endo #. f) t) z++    {-# INLINE foldl' #-}+    foldl' f z0 xs = Data.Foldable.foldr f' id xs z0+        where f' x k = oneShot $ \z -> k $! f z x++    {-# INLINE length #-}+    length = Data.Foldable.foldl' (\n _ -> n + 1) 0++    {-# INLINE elem #-}+    elem = any . (==)++    {-# INLINE maximum #-}+    maximum =+          fromMaybe (errorWithoutStackTrace "maximum: empty stream")+        . toMaybe+        . Data.Foldable.foldl' getMax Nothing'++        where++        getMax Nothing' x = Just' x+        getMax (Just' mx) x = Just' $! max mx x++    {-# INLINE minimum #-}+    minimum =+          fromMaybe (errorWithoutStackTrace "minimum: empty stream")+        . toMaybe+        . Data.Foldable.foldl' getMin Nothing'++        where++        getMin Nothing' x = Just' x+        getMin (Just' mn) x = Just' $! min mn x++    {-# INLINE sum #-}+    sum = Data.Foldable.foldl' (+) 0++    {-# INLINE product #-}+    product = Data.Foldable.foldl' (*) 1++-------------------------------------------------------------------------------+-- Filtering+-------------------------------------------------------------------------------++-- Adapted from the vector package.++-- | Take first 'n' elements from the stream and discard the rest.+--+{-# INLINE_NORMAL take #-}+take :: Applicative m => Int -> Stream m a -> Stream m a+take n (Stream step state) = n `seq` Stream step' (state, 0)++    where++    {-# INLINE_LATE step' #-}+    step' gst (st, i) | i < n = do+        (\case+            Yield x s -> Yield x (s, i + 1)+            Skip s    -> Skip (s, i)+            Stop      -> Stop) <$> step gst st+    step' _ (_, _) = pure Stop++-- Adapted from the vector package.++-- | Same as 'takeWhile' but with a monadic predicate.+--+{-# INLINE_NORMAL takeWhileM #-}+takeWhileM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+-- takeWhileM p = scanMaybe (FL.takingEndByM_ (\x -> not <$> p x))+takeWhileM f (Stream step state) = Stream step' state+  where+    {-# INLINE_LATE step' #-}+    step' gst st = do+        r <- step gst st+        case r of+            Yield x s -> do+                b <- f x+                return $ if b then Yield x s else Stop+            Skip s -> return $ Skip s+            Stop   -> return Stop++-- | End the stream as soon as the predicate fails on an element.+--+{-# INLINE takeWhile #-}+takeWhile :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+takeWhile f = takeWhileM (return . f)++-- Like takeWhile but with an inverted condition and also taking+-- the matching element.++{-# INLINE_NORMAL takeEndByM #-}+takeEndByM :: Monad m => (a -> m Bool) -> Stream m a -> Stream m a+takeEndByM f (Stream step state) = Stream step' (Just state)+  where+    {-# INLINE_LATE step' #-}+    step' gst (Just st) = do+        r <- step gst st+        case r of+            Yield x s -> do+                b <- f x+                return $+                    if not b+                    then Yield x (Just s)+                    else Yield x Nothing+            Skip s -> return $ Skip (Just s)+            Stop   -> return Stop++    step' _ Nothing = return Stop++{-# INLINE takeEndBy #-}+takeEndBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m a+takeEndBy f = takeEndByM (return . f)++------------------------------------------------------------------------------+-- Zipping+------------------------------------------------------------------------------++-- | Like 'zipWith' but using a monadic zipping function.+--+{-# INLINE_NORMAL zipWithM #-}+zipWithM :: Monad m+    => (a -> b -> m c) -> Stream m a -> Stream m b -> Stream m c+zipWithM f (Stream stepa ta) (Stream stepb tb) = Stream step (ta, tb, Nothing)+  where+    {-# INLINE_LATE step #-}+    step gst (sa, sb, Nothing) = do+        r <- stepa (adaptState gst) sa+        return $+          case r of+            Yield x sa' -> Skip (sa', sb, Just x)+            Skip sa'    -> Skip (sa', sb, Nothing)+            Stop        -> Stop++    step gst (sa, sb, Just x) = do+        r <- stepb (adaptState gst) sb+        case r of+            Yield y sb' -> do+                z <- f x y+                return $ Yield z (sa, sb', Nothing)+            Skip sb' -> return $ Skip (sa, sb', Just x)+            Stop     -> return Stop++{-# RULES "zipWithM xs xs"+    forall f xs. zipWithM @Identity f xs xs = mapM (\x -> f x x) xs #-}++-- | WARNING! O(n^2) time complexity wrt number of streams. Suitable for+-- statically fusing a small number of streams. Use the O(n) complexity+-- StreamK.'Streamly.Data.StreamK.zipWith' otherwise.+--+-- Stream @a@ is evaluated first, followed by stream @b@, the resulting+-- elements @a@ and @b@ are then zipped using the supplied zip function and the+-- result @c@ is yielded to the consumer.+--+-- If stream @a@ or stream @b@ ends, the zipped stream ends. If stream @b@ ends+-- first, the element @a@ from previous evaluation of stream @a@ is discarded.+--+-- >>> s1 = Stream.fromList [1,2,3]+-- >>> s2 = Stream.fromList [4,5,6]+-- >>> Stream.fold Fold.toList $ Stream.zipWith (+) s1 s2+-- [5,7,9]+--+{-# INLINE zipWith #-}+zipWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c+zipWith f = zipWithM (\a b -> return (f a b))++------------------------------------------------------------------------------+-- Combine N Streams - concatAp+------------------------------------------------------------------------------++-- | Apply a stream of functions to a stream of values and flatten the results.+--+-- Note that the second stream is evaluated multiple times.+--+-- >>> crossApply = Stream.crossWith id+--+{-# INLINE_NORMAL crossApply #-}+crossApply :: Functor f => Stream f (a -> b) -> Stream f a -> Stream f b+crossApply (Stream stepa statea) (Stream stepb stateb) =+    Stream step' (Left statea)++    where++    {-# INLINE_LATE step' #-}+    step' gst (Left st) = fmap+        (\case+            Yield f s -> Skip (Right (f, s, stateb))+            Skip    s -> Skip (Left s)+            Stop      -> Stop)+        (stepa (adaptState gst) st)+    step' gst (Right (f, os, st)) = fmap+        (\case+            Yield a s -> Yield (f a) (Right (f, os, s))+            Skip s    -> Skip (Right (f,os, s))+            Stop      -> Skip (Left os))+        (stepb (adaptState gst) st)++{-# INLINE_NORMAL crossApplySnd #-}+crossApplySnd :: Functor f => Stream f a -> Stream f b -> Stream f b+crossApplySnd (Stream stepa statea) (Stream stepb stateb) =+    Stream step (Left statea)++    where++    {-# INLINE_LATE step #-}+    step gst (Left st) =+        fmap+            (\case+                 Yield _ s -> Skip (Right (s, stateb))+                 Skip s -> Skip (Left s)+                 Stop -> Stop)+            (stepa (adaptState gst) st)+    step gst (Right (ostate, st)) =+        fmap+            (\case+                 Yield b s -> Yield b (Right (ostate, s))+                 Skip s -> Skip (Right (ostate, s))+                 Stop -> Skip (Left ostate))+            (stepb gst st)++{-# INLINE_NORMAL crossApplyFst #-}+crossApplyFst :: Functor f => Stream f a -> Stream f b -> Stream f a+crossApplyFst (Stream stepa statea) (Stream stepb stateb) =+    Stream step (Left statea)++    where++    {-# INLINE_LATE step #-}+    step gst (Left st) =+        fmap+            (\case+                 Yield b s -> Skip (Right (s, stateb, b))+                 Skip s -> Skip (Left s)+                 Stop -> Stop)+            (stepa gst st)+    step gst (Right (ostate, st, b)) =+        fmap+            (\case+                 Yield _ s -> Yield b (Right (ostate, s, b))+                 Skip s -> Skip (Right (ostate, s, b))+                 Stop -> Skip (Left ostate))+            (stepb (adaptState gst) st)++{-+instance Applicative f => Applicative (Stream f) where+    {-# INLINE pure #-}+    pure = fromPure++    {-# INLINE (<*>) #-}+    (<*>) = crossApply++    {-# INLINE liftA2 #-}+    liftA2 f x = (<*>) (fmap f x)++    {-# INLINE (*>) #-}+    (*>) = crossApplySnd++    {-# INLINE (<*) #-}+    (<*) = crossApplyFst+-}++-- |+-- Definition:+--+-- >>> crossWith f m1 m2 = fmap f m1 `Stream.crossApply` m2+--+-- Note that the second stream is evaluated multiple times.+--+{-# INLINE crossWith #-}+crossWith :: Monad m => (a -> b -> c) -> Stream m a -> Stream m b -> Stream m c+crossWith f m1 m2 = fmap f m1 `crossApply` m2++-- | Given a @Stream m a@ and @Stream m b@ generate a stream with all possible+-- combinations of the tuple @(a, b)@.+--+-- Definition:+--+-- >>> cross = Stream.crossWith (,)+--+-- The second stream is evaluated multiple times. If that is not desired it can+-- be cached in an 'Data.Array.Array' and then generated from the array before+-- calling this function. Caching may also improve performance if the stream is+-- expensive to evaluate.+--+-- See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused+-- alternative.+--+-- Time: O(m x n)+--+-- /Pre-release/+{-# INLINE cross #-}+cross :: Monad m => Stream m a -> Stream m b -> Stream m (a, b)+cross = crossWith (,)++------------------------------------------------------------------------------+-- Combine N Streams - unfoldMany+------------------------------------------------------------------------------++{-# ANN type ConcatMapUState Fuse #-}+data ConcatMapUState o i =+      ConcatMapUOuter o+    | ConcatMapUInner o i++-- | @unfoldMany unfold stream@ uses @unfold@ to map the input stream elements+-- to streams and then flattens the generated streams into a single output+-- stream.++-- This is like 'concatMap' but uses an unfold with an explicit state to+-- generate the stream instead of a 'Stream' type generator. This allows better+-- optimization via fusion.  This can be many times more efficient than+-- 'concatMap'.++-- | Like 'concatMap' but uses an 'Unfold' for stream generation. Unlike+-- 'concatMap' this can fuse the 'Unfold' code with the inner loop and+-- therefore provide many times better performance.+--+{-# INLINE_NORMAL unfoldMany #-}+unfoldMany :: Monad m => Unfold m a b -> Stream m a -> Stream m b+unfoldMany (Unfold istep inject) (Stream ostep ost) =+    Stream step (ConcatMapUOuter ost)+  where+    {-# INLINE_LATE step #-}+    step gst (ConcatMapUOuter o) = do+        r <- ostep (adaptState gst) o+        case r of+            Yield a o' -> do+                i <- inject a+                i `seq` return (Skip (ConcatMapUInner o' i))+            Skip o' -> return $ Skip (ConcatMapUOuter o')+            Stop -> return Stop++    step _ (ConcatMapUInner o i) = do+        r <- istep i+        return $ case r of+            Yield x i' -> Yield x (ConcatMapUInner o i')+            Skip i'    -> Skip (ConcatMapUInner o i')+            Stop       -> Skip (ConcatMapUOuter o)++------------------------------------------------------------------------------+-- Combine N Streams - concatMap+------------------------------------------------------------------------------++-- Adapted from the vector package.++-- | Map a stream producing monadic function on each element of the stream+-- and then flatten the results into a single stream. Since the stream+-- generation function is monadic, unlike 'concatMap', it can produce an+-- effect at the beginning of each iteration of the inner loop.+--+-- See 'unfoldMany' for a fusible alternative.+--+{-# INLINE_NORMAL concatMapM #-}+concatMapM :: Monad m => (a -> m (Stream m b)) -> Stream m a -> Stream m b+concatMapM f (Stream step state) = Stream step' (Left state)+  where+    {-# INLINE_LATE step' #-}+    step' gst (Left st) = do+        r <- step (adaptState gst) st+        case r of+            Yield a s -> do+                b_stream <- f a+                return $ Skip (Right (b_stream, s))+            Skip s -> return $ Skip (Left s)+            Stop -> return Stop++    -- XXX flattenArrays is 5x faster than "concatMap fromArray". if somehow we+    -- can get inner_step to inline and fuse here we can perhaps get the same+    -- performance using "concatMap fromArray".+    --+    -- XXX using the pattern synonym "Stream" causes a major performance issue+    -- here even if the synonym does not include an adaptState call. Need to+    -- find out why. Is that something to be fixed in GHC?+    step' gst (Right (UnStream inner_step inner_st, st)) = do+        r <- inner_step (adaptState gst) inner_st+        case r of+            Yield b inner_s ->+                return $ Yield b (Right (Stream inner_step inner_s, st))+            Skip inner_s ->+                return $ Skip (Right (Stream inner_step inner_s, st))+            Stop -> return $ Skip (Left st)++-- | Map a stream producing function on each element of the stream and then+-- flatten the results into a single stream.+--+-- >>> concatMap f = Stream.concatMapM (return . f)+-- >>> concatMap f = Stream.concat . fmap f+-- >>> concatMap f = Stream.unfoldMany (Unfold.lmap f Unfold.fromStream)+--+-- See 'unfoldMany' for a fusible alternative.+--+{-# INLINE concatMap #-}+concatMap :: Monad m => (a -> Stream m b) -> Stream m a -> Stream m b+concatMap f = concatMapM (return . f)++-- | Flatten a stream of streams to a single stream.+--+-- >>> concat = Stream.concatMap id+--+-- /Pre-release/+{-# INLINE concat #-}+concat :: Monad m => Stream m (Stream m a) -> Stream m a+concat = concatMap id++-- XXX The idea behind this rule is to rewrite any calls to "concatMap+-- fromArray" automatically to flattenArrays which is much faster.  However, we+-- need an INLINE_EARLY on concatMap for this rule to fire. But if we use+-- INLINE_EARLY on concatMap or fromArray then direct uses of+-- "concatMap fromArray" (without the RULE) become much slower, this means+-- "concatMap f" in general would become slower. Need to find a solution to+-- this.+--+-- {-# RULES "concatMap Array.toStreamD"+--      concatMap Array.toStreamD = Array.flattenArray #-}++-- >>> concatEffect = Stream.concat . lift    -- requires (MonadTrans t)+-- >>> concatEffect = join . lift             -- requires (MonadTrans t, Monad (Stream m))++-- | Given a stream value in the underlying monad, lift and join the underlying+-- monad with the stream monad.+--+-- >>> concatEffect = Stream.concat . Stream.fromEffect+-- >>> concatEffect eff = Stream.concatMapM (\() -> eff) (Stream.fromPure ())+--+-- See also: 'concat', 'sequence'+--+{-# INLINE concatEffect #-}+concatEffect :: Monad m => m (Stream m a) -> Stream m a+concatEffect generator = concatMapM (\() -> generator) (fromPure ())++{-+-- NOTE: even though concatMap for StreamD is 4x faster compared to StreamK,+-- the monad instance does not seem to be significantly faster.+instance Monad m => Monad (Stream m) where+    {-# INLINE return #-}+    return = pure++    {-# INLINE (>>=) #-}+    (>>=) = flip concatMap++    {-# INLINE (>>) #-}+    (>>) = (*>)+-}++------------------------------------------------------------------------------+-- Traversing a tree top down+------------------------------------------------------------------------------++-- Next stream is to be generated by the return value of the previous stream. A+-- general intuitive way of doing that could be to use an appending monad+-- instance for streams where the result of the previous stream is used to+-- generate the next one. In the first pass we can just emit the values in the+-- stream and keep building a buffered list/stream, once done we can then+-- process the buffered stream.++-- | Generate a stream from an initial state, scan and concat the stream,+-- generate a stream again from the final state of the previous scan and repeat+-- the process.+{-# INLINE_NORMAL concatIterateScan #-}+concatIterateScan :: Monad m =>+       (b -> a -> m b)+    -> (b -> m (Maybe (b, Stream m a)))+    -> b+    -> Stream m a+concatIterateScan scanner generate initial = Stream step (Left initial)++    where++    {-# INLINE_LATE step #-}+    step _ (Left acc) = do+        r <- generate acc+        case r of+            Nothing -> return Stop+            Just v -> return $ Skip (Right v)++    step gst (Right (st, UnStream inner_step inner_st)) = do+        r <- inner_step (adaptState gst) inner_st+        case r of+            Yield b inner_s -> do+                acc <- scanner st b+                return $ Yield b (Right (acc, Stream inner_step inner_s))+            Skip inner_s ->+                return $ Skip (Right (st, Stream inner_step inner_s))+            Stop -> return $ Skip (Left st)++-- Note: The iterate function returns a Maybe Stream instead of returning a nil+-- stream for indicating a leaf node. This is to optimize so that we do not+-- have to store any state. This makes the stored state proportional to the+-- number of non-leaf nodes rather than total number of nodes.++-- | Same as 'concatIterateBfs' except that the traversal of the last+-- element on a level is emitted first and then going backwards up to the first+-- element (reversed ordering). This may be slightly faster than+-- 'concatIterateBfs'.+--+{-# INLINE_NORMAL concatIterateBfsRev #-}+concatIterateBfsRev :: Monad m =>+       (a -> Maybe (Stream m a))+    -> Stream m a+    -> Stream m a+concatIterateBfsRev f stream = Stream step (stream, [])++    where++    {-# INLINE_LATE step #-}+    step gst (UnStream step1 st, xs) = do+        r <- step1 (adaptState gst) st+        case r of+            Yield a s -> do+                let xs1 =+                        case f a of+                            Nothing -> xs+                            Just x -> x:xs+                return $ Yield a (Stream step1 s, xs1)+            Skip s -> return $ Skip (Stream step1 s, xs)+            Stop ->+                case xs of+                    (y:ys) -> return $ Skip (y, ys)+                    [] -> return Stop++-- | Similar to 'concatIterateDfs' except that it traverses the stream in+-- breadth first style (BFS). First, all the elements in the input stream are+-- emitted, and then their traversals are emitted.+--+-- Example, list a directory tree using BFS:+--+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)+-- >>> input = Stream.fromPure (Left ".")+-- >>> ls = Stream.concatIterateBfs f input+--+-- /Pre-release/+{-# INLINE_NORMAL concatIterateBfs #-}+concatIterateBfs :: Monad m =>+       (a -> Maybe (Stream m a))+    -> Stream m a+    -> Stream m a+concatIterateBfs f stream = Stream step (stream, [], [])++    where++    {-# INLINE_LATE step #-}+    step gst (UnStream step1 st, xs, ys) = do+        r <- step1 (adaptState gst) st+        case r of+            Yield a s -> do+                let ys1 =+                        case f a of+                            Nothing -> ys+                            Just y -> y:ys+                return $ Yield a (Stream step1 s, xs, ys1)+            Skip s -> return $ Skip (Stream step1 s, xs, ys)+            Stop ->+                case xs of+                    (x:xs1) -> return $ Skip (x, xs1, ys)+                    [] ->+                        case reverse ys of+                            (x:xs1) -> return $ Skip (x, xs1, [])+                            [] -> return Stop++-- | Traverse the stream in depth first style (DFS). Map each element in the+-- input stream to a stream and flatten, recursively map the resulting elements+-- as well to a stream and flatten until no more streams are generated.+--+-- Example, list a directory tree using DFS:+--+-- >>> f = either (Just . Dir.readEitherPaths) (const Nothing)+-- >>> input = Stream.fromPure (Left ".")+-- >>> ls = Stream.concatIterateDfs f input+--+-- This is equivalent to using @concatIterateWith StreamK.append@.+--+-- /Pre-release/+{-# INLINE_NORMAL concatIterateDfs #-}+concatIterateDfs :: Monad m =>+       (a -> Maybe (Stream m a))+    -> Stream m a+    -> Stream m a+concatIterateDfs f stream = Stream step (stream, [])++    where++    {-# INLINE_LATE step #-}+    step gst (UnStream step1 st, xs) = do+        r <- step1 (adaptState gst) st+        case r of+            Yield a s -> do+                let st1 =+                        case f a of+                            Nothing -> (Stream step1 s, xs)+                            Just x -> (x, Stream step1 s:xs)+                return $ Yield a st1+            Skip s -> return $ Skip (Stream step1 s, xs)+            Stop ->+                case xs of+                    (y:ys) -> return $ Skip (y, ys)+                    [] -> return Stop++{-# ANN type IterateUnfoldState Fuse #-}+data IterateUnfoldState o i =+      IterateUnfoldOuter o+    | IterateUnfoldInner o i [i]++-- | Same as @concatIterateDfs@ but more efficient due to stream fusion.+--+-- Example, list a directory tree using DFS:+--+-- >>> f = Unfold.either Dir.eitherReaderPaths Unfold.nil+-- >>> input = Stream.fromPure (Left ".")+-- >>> ls = Stream.unfoldIterateDfs f input+--+-- /Pre-release/+{-# INLINE_NORMAL unfoldIterateDfs #-}+unfoldIterateDfs :: Monad m =>+       Unfold m a a+    -> Stream m a+    -> Stream m a+unfoldIterateDfs (Unfold istep inject) (Stream ostep ost) =+    Stream step (IterateUnfoldOuter ost)++    where++    {-# 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++    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)+            Stop ->+                case ii of+                    (y:ys) -> return $ Skip (IterateUnfoldInner o y ys)+                    [] -> return $ Skip (IterateUnfoldOuter o)++{-# ANN type IterateUnfoldBFSRevState Fuse #-}+data IterateUnfoldBFSRevState o i =+      IterateUnfoldBFSRevOuter o [i]+    | IterateUnfoldBFSRevInner i [i]++-- | Like 'unfoldIterateBfs' but processes the children in reverse order,+-- therefore, may be slightly faster.+--+-- /Pre-release/+{-# INLINE_NORMAL unfoldIterateBfsRev #-}+unfoldIterateBfsRev :: Monad m =>+       Unfold m a a+    -> Stream m a+    -> Stream m a+unfoldIterateBfsRev (Unfold istep inject) (Stream ostep ost) =+    Stream step (IterateUnfoldBFSRevOuter ost [])++    where++    {-# 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)+            Stop ->+                case ii of+                    (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys)+                    [] -> return 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)+            Stop ->+                case ii of+                    (y:ys) -> return $ Skip (IterateUnfoldBFSRevInner y ys)+                    [] -> return Stop++{-# ANN type IterateUnfoldBFSState Fuse #-}+data IterateUnfoldBFSState o i =+      IterateUnfoldBFSOuter o [i]+    | IterateUnfoldBFSInner i [i] [i]++-- | Like 'unfoldIterateDfs' but uses breadth first style traversal.+--+-- /Pre-release/+{-# INLINE_NORMAL unfoldIterateBfs #-}+unfoldIterateBfs :: Monad m =>+       Unfold m a a+    -> Stream m a+    -> Stream m a+unfoldIterateBfs (Unfold istep inject) (Stream ostep ost) =+    Stream step (IterateUnfoldBFSOuter ost [])++    where++    {-# 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)+            Stop ->+                case reverse rii of+                    (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys [])+                    [] -> return 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)+            Stop ->+                case ii of+                    (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys rii)+                    [] ->+                        case reverse rii of+                            (y:ys) -> return $ Skip (IterateUnfoldBFSInner y ys [])+                            [] -> return Stop++------------------------------------------------------------------------------+-- Folding a tree bottom up+------------------------------------------------------------------------------++-- | Binary BFS style reduce, folds a level entirely using the supplied fold+-- function, collecting the outputs as next level of the tree, then repeats the+-- same process on the next level. The last elements of a previously folded+-- level are folded first.+{-# INLINE_NORMAL reduceIterateBfs #-}+reduceIterateBfs :: Monad m =>+    (a -> a -> m a) -> Stream m a -> m (Maybe a)+reduceIterateBfs f (Stream step state) = go SPEC state [] Nothing++    where++    go _ st xs Nothing = do+        r <- step defState st+        case r of+            Yield x1 s -> go SPEC s xs (Just x1)+            Skip s -> go SPEC s xs Nothing+            Stop ->+                case xs of+                    [] -> return Nothing+                    _ -> goBuf SPEC xs []+    go _ st xs (Just x1) = do+        r2 <- step defState st+        case r2 of+            Yield x2 s -> do+                x <- f x1 x2+                go SPEC s (x:xs) Nothing+            Skip s -> go SPEC s xs (Just x1)+            Stop ->+                case xs of+                    [] -> return (Just x1)+                    _ -> goBuf SPEC (x1:xs) []++    goBuf _ [] ys = goBuf SPEC ys []+    goBuf _ [x1] ys = do+        case ys of+            [] -> return (Just x1)+            (x2:xs) -> do+                y <- f x1 x2+                goBuf SPEC xs [y]+    goBuf _ (x1:x2:xs) ys = do+        y <- f x1 x2+        goBuf SPEC xs (y:ys)++-- | N-Ary BFS style iterative fold, if the input stream finished before the+-- fold then it returns Left otherwise Right. If the fold returns Left we+-- terminate.+--+-- /Unimplemented/+foldIterateBfs ::+    Fold m a (Either a a) -> Stream m a -> m (Maybe a)+foldIterateBfs = undefined++------------------------------------------------------------------------------+-- Grouping/Splitting+------------------------------------------------------------------------------++-- s = stream state, fs = fold state+{-# ANN type FoldManyPost Fuse #-}+data FoldManyPost s fs b a+    = FoldManyPostStart s+    | FoldManyPostLoop s fs+    | FoldManyPostYield b (FoldManyPost s fs b a)+    | FoldManyPostDone++-- XXX Need a more intuitive name, and need to reconcile the names+-- foldMany/fold/parse/parseMany/parseManyPost etc.++-- XXX foldManyPost keeps the last fold always partial. if the last fold is+-- complete then another fold is applied on empty input. This is used for+-- applying folds like takeEndBy such that the last element is not the+-- separator (infix style). But that looks like a hack. We should remove this+-- and use a custom combinator for infix parsing.++-- | Like 'foldMany' but evaluates the fold even if the fold did not receive+-- any input, therefore, always results in a non-empty output even on an empty+-- stream (default result of the fold).+--+-- Example, empty stream:+--+-- >>> f = Fold.take 2 Fold.sum+-- >>> fmany = Stream.fold Fold.toList . Stream.foldManyPost f+-- >>> fmany $ Stream.fromList []+-- [0]+--+-- Example, last fold empty:+--+-- >>> fmany $ Stream.fromList [1..4]+-- [3,7,0]+--+-- Example, last fold non-empty:+--+-- >>> fmany $ Stream.fromList [1..5]+-- [3,7,5]+--+-- Note that using a closed fold e.g. @Fold.take 0@, would result in an+-- infinite stream without consuming the input.+--+-- /Pre-release/+--+{-# INLINE_NORMAL foldManyPost #-}+foldManyPost :: Monad m => Fold m a b -> Stream m a -> Stream m b+foldManyPost (Fold fstep initial _ final) (Stream step state) =+    Stream step' (FoldManyPostStart state)++    where++    {-# INLINE consume #-}+    consume x s fs = do+        res <- fstep fs x+        return+            $ Skip+            $ case res of+                  FL.Done b -> FoldManyPostYield b (FoldManyPostStart s)+                  FL.Partial ps -> FoldManyPostLoop s ps++    {-# INLINE_LATE step' #-}+    step' _ (FoldManyPostStart st) = do+        r <- initial+        return+            $ Skip+            $ case r of+                  FL.Done b -> FoldManyPostYield b (FoldManyPostStart st)+                  FL.Partial fs -> FoldManyPostLoop st fs+    step' gst (FoldManyPostLoop st fs) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> consume x s fs+            Skip s -> return $ Skip (FoldManyPostLoop s fs)+            Stop -> do+                b <- final fs+                return $ Skip (FoldManyPostYield b FoldManyPostDone)+    step' _ (FoldManyPostYield b next) = return $ Yield b next+    step' _ FoldManyPostDone = return Stop++{-# ANN type FoldMany Fuse #-}+data FoldMany s fs b a+    = FoldManyStart s+    | FoldManyFirst fs s+    | FoldManyLoop s fs+    | FoldManyYield b (FoldMany s fs b a)+    | FoldManyDone++-- XXX Nested foldMany does not fuse.++-- | Apply a 'Fold' repeatedly on a stream and emit the results in the output+-- stream.+--+-- Definition:+--+-- >>> foldMany f = Stream.parseMany (Parser.fromFold f)+--+-- Example, empty stream:+--+-- >>> f = Fold.take 2 Fold.sum+-- >>> fmany = Stream.fold Fold.toList . Stream.foldMany f+-- >>> fmany $ Stream.fromList []+-- []+--+-- Example, last fold empty:+--+-- >>> fmany $ Stream.fromList [1..4]+-- [3,7]+--+-- Example, last fold non-empty:+--+-- >>> fmany $ Stream.fromList [1..5]+-- [3,7,5]+--+-- Note that using a closed fold e.g. @Fold.take 0@, would result in an+-- infinite stream on a non-empty input stream.+--+{-# INLINE_NORMAL foldMany #-}+foldMany :: Monad m => Fold m a b -> Stream m a -> Stream m b+foldMany (Fold fstep initial _ final) (Stream step state) =+    Stream step' (FoldManyStart state)++    where++    {-# INLINE consume #-}+    consume x s fs = do+        res <- fstep fs x+        return+            $ Skip+            $ case res of+                  FL.Done b -> FoldManyYield b (FoldManyStart s)+                  FL.Partial ps -> FoldManyLoop s ps++    {-# INLINE_LATE step' #-}+    step' _ (FoldManyStart st) = do+        r <- initial+        return+            $ Skip+            $ case r of+                  FL.Done b -> FoldManyYield b (FoldManyStart st)+                  FL.Partial fs -> FoldManyFirst fs st+    step' gst (FoldManyFirst fs st) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> consume x s fs+            Skip s -> return $ Skip (FoldManyFirst fs s)+            Stop -> final fs >> return Stop+    step' gst (FoldManyLoop st fs) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> consume x s fs+            Skip s -> return $ Skip (FoldManyLoop s fs)+            Stop -> do+                b <- final fs+                return $ Skip (FoldManyYield b FoldManyDone)+    step' _ (FoldManyYield b next) = return $ Yield b next+    step' _ FoldManyDone = return Stop++-- | Group the input stream into groups of @n@ elements each and then fold each+-- group using the provided fold function.+--+-- @groupsOf n f = foldMany (FL.take n f)@+--+-- >>> Stream.toList $ Stream.groupsOf 2 Fold.sum (Stream.enumerateFromTo 1 10)+-- [3,7,11,15,19]+--+-- This can be considered as an n-fold version of 'take' where we apply+-- 'take' repeatedly on the leftover stream until the stream exhausts.+--+{-# INLINE groupsOf #-}+groupsOf :: Monad m => Int -> Fold m a b -> Stream m a -> Stream m b+groupsOf n f = foldMany (FL.take n f)++-- Keep the argument order consistent with refoldIterateM.++-- | Like 'foldMany' but for the 'Refold' type.  The supplied action is used as+-- the initial value for each refold.+--+-- /Internal/+{-# INLINE_NORMAL refoldMany #-}+refoldMany :: Monad m => Refold m x a b -> m x -> Stream m a -> Stream m b+refoldMany (Refold fstep inject extract) action (Stream step state) =+    Stream step' (FoldManyStart state)++    where++    {-# INLINE consume #-}+    consume x s fs = do+        res <- fstep fs x+        return+            $ Skip+            $ case res of+                  FL.Done b -> FoldManyYield b (FoldManyStart s)+                  FL.Partial ps -> FoldManyLoop s ps++    {-# INLINE_LATE step' #-}+    step' _ (FoldManyStart st) = do+        r <- action >>= inject+        return+            $ Skip+            $ case r of+                  FL.Done b -> FoldManyYield b (FoldManyStart st)+                  FL.Partial fs -> FoldManyFirst fs st+    step' gst (FoldManyFirst fs st) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> consume x s fs+            Skip s -> return $ Skip (FoldManyFirst fs s)+            Stop -> return Stop+    step' gst (FoldManyLoop st fs) = do+        r <- step (adaptState gst) st+        case r of+            Yield x s -> consume x s fs+            Skip s -> return $ Skip (FoldManyLoop s fs)+            Stop -> do+                b <- extract fs+                return $ Skip (FoldManyYield b FoldManyDone)+    step' _ (FoldManyYield b next) = return $ Yield b next+    step' _ FoldManyDone = return Stop++------------------------------------------------------------------------------+-- Stream with a cross product style monad instance+------------------------------------------------------------------------------++-- XXX CrossStream performs better than the CrossStreamK when nesting two+-- loops, however, CrossStreamK seems to be better for more than two nestings,+-- need to do more perf investigation.++-- | A newtype wrapper for the 'Stream' type with a cross product style monad+-- instance.+--+-- A 'Monad' bind behaves like a @for@ loop:+--+-- >>> :{+-- Stream.fold Fold.toList $ Stream.unCross $ do+--     x <- Stream.mkCross $ Stream.fromList [1,2]+--     -- Perform the following actions for each x in the stream+--     return x+-- :}+-- [1,2]+--+-- Nested monad binds behave like nested @for@ loops:+--+-- >>> :{+-- Stream.fold Fold.toList $ Stream.unCross $ do+--     x <- Stream.mkCross $ Stream.fromList [1,2]+--     y <- Stream.mkCross $ Stream.fromList [3,4]+--     -- Perform the following actions for each x, for each y+--     return (x, y)+-- :}+-- [(1,3),(1,4),(2,3),(2,4)]+--+newtype CrossStream m a = CrossStream {unCrossStream :: Stream m a}+        deriving (Functor, Foldable)++{-# INLINE mkCross #-}+mkCross :: Stream m a -> CrossStream m a+mkCross = CrossStream++{-# INLINE unCross #-}+unCross :: CrossStream m a -> Stream m a+unCross = unCrossStream++-- Pure (Identity monad) stream instances+deriving instance IsList (CrossStream Identity a)+deriving instance (a ~ Char) => IsString (CrossStream Identity a)+deriving instance Eq a => Eq (CrossStream Identity a)+deriving instance Ord a => Ord (CrossStream Identity a)++-- Do not use automatic derivation for this to show as "fromList" rather than+-- "fromList Identity".+instance Show a => Show (CrossStream Identity a) where+    {-# INLINE show #-}+    show (CrossStream xs) = show xs++instance Read a => Read (CrossStream Identity a) where+    {-# INLINE readPrec #-}+    readPrec = fmap CrossStream readPrec++------------------------------------------------------------------------------+-- Applicative+------------------------------------------------------------------------------++-- Note: we need to define all the typeclass operations because we want to+-- INLINE them.+instance Monad m => Applicative (CrossStream m) where+    {-# INLINE pure #-}+    pure x = CrossStream (fromPure x)++    {-# INLINE (<*>) #-}+    (CrossStream s1) <*> (CrossStream s2) =+        CrossStream (crossApply s1 s2)++    {-# INLINE liftA2 #-}+    liftA2 f x = (<*>) (fmap f x)++    {-# INLINE (*>) #-}+    (CrossStream s1) *> (CrossStream s2) =+        CrossStream (crossApplySnd s1 s2)++    {-# INLINE (<*) #-}+    (CrossStream s1) <* (CrossStream s2) =+        CrossStream (crossApplyFst s1 s2)++------------------------------------------------------------------------------+-- Monad+------------------------------------------------------------------------------++instance Monad m => Monad (CrossStream m) where+    return = pure++    -- Benchmarks better with StreamD bind and pure:+    -- toList, filterAllout, *>, *<, >> (~2x)+    --++    -- Benchmarks better with CPS bind and pure:+    -- Prime sieve (25x)+    -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)+    --+    {-# INLINE (>>=) #-}+    (>>=) (CrossStream m) f = CrossStream (concatMap (unCrossStream . f) m)++    {-# INLINE (>>) #-}+    (>>) = (*>)++------------------------------------------------------------------------------+-- Transformers+------------------------------------------------------------------------------++instance (MonadIO m) => MonadIO (CrossStream m) where+    liftIO x = CrossStream (fromEffect $ liftIO x)++instance MonadTrans CrossStream where+    {-# INLINE lift #-}+    lift x = CrossStream (fromEffect x)++instance (MonadThrow m) => MonadThrow (CrossStream m) where+    throwM = lift . throwM
− src/Streamly/Internal/Data/Stream/Zip.hs
@@ -1,91 +0,0 @@-{-# LANGUAGE UndecidableInstances #-}---- |--- Module      : Streamly.Internal.Data.Stream.Zip--- Copyright   : (c) 2017 Composewell Technologies------ License     : BSD3--- Maintainer  : streamly@composewell.com--- Stability   : experimental--- Portability : GHC------ To run examples in this module:------ >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Stream as Stream--- >>> import qualified Streamly.Internal.Data.Stream.Zip as Stream----module Streamly.Internal.Data.Stream.Zip-    (-      ZipStream (..)-    , ZipSerialM-    , ZipSerial-    )-where--import Data.Functor.Identity (Identity(..))-import GHC.Exts (IsList(..), IsString(..))-import Streamly.Internal.Data.Stream.Type (Stream)-import Text.Read-       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec-       , readListPrecDefault)--import qualified Streamly.Internal.Data.Stream.Bottom as Stream-import qualified Streamly.Internal.Data.Stream.Generate as Stream---- $setup--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Stream as Stream--- >>> import qualified Streamly.Internal.Data.Stream.Zip as Stream----------------------------------------------------------------------------------- Serially Zipping Streams----------------------------------------------------------------------------------- | For 'ZipStream':------ @--- (<>) = 'Streamly.Data.Stream.append'--- (\<*>) = 'Streamly.Data.Stream.zipWith' id--- @------ Applicative evaluates the streams being zipped serially:------ >>> s1 = Stream.ZipStream $ Stream.fromFoldable [1, 2]--- >>> s2 = Stream.ZipStream $ Stream.fromFoldable [3, 4]--- >>> s3 = Stream.ZipStream $ Stream.fromFoldable [5, 6]--- >>> s = (,,) <$> s1 <*> s2 <*> s3--- >>> Stream.fold Fold.toList (Stream.unZipStream s)--- [(1,3,5),(2,4,6)]----newtype ZipStream m a = ZipStream {unZipStream :: Stream m a}-        deriving (Functor, Semigroup, Monoid)--deriving instance IsList (ZipStream Identity a)-deriving instance (a ~ Char) => IsString (ZipStream Identity a)-deriving instance Eq a => Eq (ZipStream Identity a)-deriving instance Ord a => Ord (ZipStream Identity a)-deriving instance (Foldable m, Monad m) => Foldable (ZipStream m)-deriving instance Traversable (ZipStream Identity)--instance Show a => Show (ZipStream Identity a) where-    showsPrec p dl = showParen (p > 10) $-        showString "fromList " . shows (toList dl)--instance Read a => Read (ZipStream Identity a) where-    readPrec = parens $ prec 10 $ do-        Ident "fromList" <- lexP-        fromList <$> readPrec-    readListPrec = readListPrecDefault--type ZipSerialM = ZipStream---- | An IO stream whose applicative instance zips streams serially.----type ZipSerial = ZipSerialM IO--instance Monad m => Applicative (ZipStream m) where-    pure = ZipStream . Stream.repeat--    {-# INLINE (<*>) #-}-    ZipStream m1 <*> ZipStream m2 = ZipStream $ Stream.zipWith id m1 m2
+ src/Streamly/Internal/Data/StreamK.hs view
@@ -0,0 +1,1739 @@+{-# LANGUAGE CPP #-}+-- |+-- Module      : Streamly.Internal.Data.StreamK+-- Copyright   : (c) 2017 Composewell Technologies+--+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.StreamK+    (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup++    -- * The stream type+      module Streamly.Internal.Data.StreamK.Type+    , module Streamly.Internal.Data.StreamK.Transformer++    , StreamK(..)+    , fromList+    , fromStream+    , toStream++    -- ** Specialized Generation+    , repeatM+    , replicate+    , replicateM+    , fromIndices+    , fromIndicesM+    , iterate+    , iterateM++    -- * Elimination+    -- ** General Folds+    , foldr1+    , foldlM'+    , foldlMx'+    , fold+    , foldBreak+    , foldEither+    , foldConcat+    , parseDBreak+    , parseD+    , parseBreakChunks+    , parseChunks+    , parseBreak+    , parse+    , parseBreakChunksGeneric+    , parseChunksGeneric++    -- ** Specialized Folds+    , head+    , elem+    , notElem+    , all+    , any+    , last+    , minimum+    , minimumBy+    , maximum+    , maximumBy+    , findIndices+    , lookup+    , findM+    , find+    , (!!)++    -- ** Map and Fold+    , mapM_++    -- ** Conversions+    , toList+    , hoist++    -- * Transformation+    -- ** By folding (scans)+    , scanl'+    , scanlx'++    -- ** Filtering+    , filter+    , take+    , takeWhile+    , drop+    , dropWhile++    -- ** Mapping+    , mapM+    , sequence++    -- ** Inserting+    , intersperseM+    , intersperse+    , insertBy++    -- ** Deleting+    , deleteBy++    -- ** Reordering+    , sortBy++    -- ** Map and Filter+    , mapMaybe++    -- ** Zipping+    , zipWith+    , zipWithM++    -- ** Merging+    , mergeBy+    , mergeByM++    -- ** Transformation comprehensions+    , the++    -- * Exceptions+    , handle++    -- * Resource Management+    , bracketIO+    )+where++#include "ArrayMacros.h"+#include "inline.hs"+#include "assert.hs"++import Control.Exception (mask_, Exception)+import Control.Monad (void, join)+import Control.Monad.Catch (MonadCatch)+import Control.Monad.IO.Class (MonadIO(..))+import Data.Proxy (Proxy(..))+import GHC.Types (SPEC(..))+import Streamly.Internal.Data.Array.Type (Array(..))+import Streamly.Internal.Data.Fold.Type (Fold(..))+import Streamly.Internal.Data.IOFinalizer (newIOFinalizer, runIOFinalizer)+import Streamly.Internal.Data.ParserK.Type (ParserK)+import Streamly.Internal.Data.Producer.Type (Producer(..))+import Streamly.Internal.Data.SVar.Type (adaptState, defState)+import Streamly.Internal.Data.Unbox (sizeOf, Unbox)++import qualified Control.Monad.Catch as MC+import qualified Streamly.Internal.Data.Array.Type as Array+import qualified Streamly.Internal.Data.Array.Generic as GenArr+import qualified Streamly.Internal.Data.Fold.Type as FL+import qualified Streamly.Internal.Data.Parser as Parser+import qualified Streamly.Internal.Data.Parser.Type as PR+import qualified Streamly.Internal.Data.ParserK.Type as ParserK+import qualified Streamly.Internal.Data.Stream as Stream+import qualified Prelude++import Prelude+       hiding (Foldable(..), last, map, mapM, mapM_, repeat, sequence,+               take, filter, all, any, takeWhile, drop, dropWhile,+               notElem, head, tail, init, zipWith, lookup,+               (!!), replicate, reverse, concatMap, iterate, splitAt)+import Data.Foldable (sum, length)+import Streamly.Internal.Data.StreamK.Type+import Streamly.Internal.Data.StreamK.Transformer+import Streamly.Internal.Data.Parser (ParseError(..))++#include "DocTestDataStreamK.hs"++-- | Convert a fused 'Stream' to 'StreamK'.+--+-- For example:+--+-- >>> s1 = StreamK.fromStream $ Stream.fromList [1,2]+-- >>> s2 = StreamK.fromStream $ Stream.fromList [3,4]+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ s1 `StreamK.append` s2+-- [1,2,3,4]+--+{-# INLINE fromStream #-}+fromStream :: Monad m => Stream.Stream m a -> StreamK m a+fromStream = Stream.toStreamK++-- | Convert a 'StreamK' to a fused 'Stream'.+--+{-# INLINE toStream #-}+toStream :: Applicative m => StreamK m a -> Stream.Stream m a+toStream = Stream.fromStreamK++-------------------------------------------------------------------------------+-- Generation+-------------------------------------------------------------------------------++{-+-- Generalization of concurrent streams/SVar via unfoldr.+--+-- Unfold a value into monadic actions and then run the resulting monadic+-- actions to generate a stream. Since the step of generating the monadic+-- action and running them are decoupled we can run the monadic actions+-- cooncurrently. For example, the seed could be a list of monadic actions or a+-- pure stream of monadic actions.+--+-- We can have different flavors of this depending on the stream type t. The+-- concurrent version could be async or ahead etc. Depending on how we queue+-- back the feedback portion b, it could be DFS or BFS style.+--+unfoldrA :: (b -> Maybe (m a, b)) -> b -> StreamK m a+unfoldrA = undefined+-}++-------------------------------------------------------------------------------+-- Special generation+-------------------------------------------------------------------------------++-- |+-- >>> repeatM = StreamK.sequence . StreamK.repeat+-- >>> repeatM = fix . StreamK.consM+-- >>> repeatM = cycle1 . StreamK.fromEffect+--+-- Generate a stream by repeatedly executing a monadic action forever.+--+-- >>> :{+-- repeatAction =+--        StreamK.repeatM (threadDelay 1000000 >> print 1)+--      & StreamK.take 10+--      & StreamK.fold Fold.drain+-- :}+--+repeatM :: Monad m => m a -> StreamK m a+repeatM = repeatMWith consM++{-# INLINE replicateM #-}+replicateM :: Monad m => Int -> m a -> StreamK m a+replicateM = replicateMWith consM+{-# INLINE replicate #-}+replicate :: Int -> a -> StreamK m a+replicate n a = go n+    where+    go cnt = if cnt <= 0 then nil else a `cons` go (cnt - 1)++{-# INLINE fromIndicesM #-}+fromIndicesM :: Monad m => (Int -> m a) -> StreamK m a+fromIndicesM = fromIndicesMWith consM+{-# INLINE fromIndices #-}+fromIndices :: (Int -> a) -> StreamK m a+fromIndices gen = go 0+  where+    go n = gen n `cons` go (n + 1)++-- |+-- >>> iterate f x = x `StreamK.cons` iterate f x+--+-- Generate an infinite stream with @x@ as the first element and each+-- successive element derived by applying the function @f@ on the previous+-- element.+--+-- >>> StreamK.toList $ StreamK.take 5 $ StreamK.iterate (+1) 1+-- [1,2,3,4,5]+--+{-# INLINE iterate #-}+iterate :: (a -> a) -> a -> StreamK m a+iterate step = go+    where+        go !s = cons s (go (step s))++-- |+-- >>> iterateM f m = m >>= \a -> return a `StreamK.consM` iterateM f (f a)+--+-- Generate an infinite stream with the first element generated by the action+-- @m@ and each successive element derived by applying the monadic function+-- @f@ on the previous element.+--+-- >>> :{+-- StreamK.iterateM (\x -> print x >> return (x + 1)) (return 0)+--     & StreamK.take 3+--     & StreamK.toList+-- :}+-- 0+-- 1+-- [0,1,2]+--+{-# INLINE iterateM #-}+iterateM :: Monad m => (a -> m a) -> m a -> StreamK m a+iterateM = iterateMWith consM++-------------------------------------------------------------------------------+-- Conversions+-------------------------------------------------------------------------------++{-# INLINE fromList #-}+fromList :: [a] -> StreamK m a+fromList = fromFoldable++-------------------------------------------------------------------------------+-- Elimination by Folding+-------------------------------------------------------------------------------++{-# INLINE foldr1 #-}+foldr1 :: Monad m => (a -> a -> a) -> StreamK m a -> m (Maybe a)+foldr1 step m = do+    r <- uncons m+    case r of+        Nothing -> return Nothing+        Just (h, t) -> fmap Just (go h t)+    where+    go p m1 =+        let stp = return p+            single a = return $ step a p+            yieldk a r = fmap (step p) (go a r)+         in foldStream defState yieldk single stp m1++-- XXX replace the recursive "go" with explicit continuations.+-- | Like 'foldx', but with a monadic step function.+{-# INLINABLE foldlMx' #-}+foldlMx' :: Monad m+    => (x -> a -> m x) -> m x -> (x -> m b) -> StreamK m a -> m b+foldlMx' step begin done = go begin+    where+    go !acc m1 =+        let stop = acc >>= done+            single a = acc >>= \b -> step b a >>= done+            yieldk a r = acc >>= \b -> step b a >>= \x -> go (return x) r+         in foldStream defState yieldk single stop m1++-- | Fold a stream using the supplied left 'Fold' and reducing the resulting+-- expression strictly at each step. The behavior is similar to 'foldl''. A+-- 'Fold' can terminate early without consuming the full stream. See the+-- documentation of individual 'Fold's for termination behavior.+--+-- Definitions:+--+-- >>> fold f = fmap fst . StreamK.foldBreak f+-- >>> fold f = StreamK.parseD (Parser.fromFold f)+--+-- Example:+--+-- >>> StreamK.fold Fold.sum $ StreamK.fromStream $ Stream.enumerateFromTo 1 100+-- 5050+--+{-# INLINABLE fold #-}+fold :: Monad m => FL.Fold m a b -> StreamK m a -> m b+fold (FL.Fold step begin _ final) m = do+    res <- begin+    case res of+        FL.Partial fs -> go fs m+        FL.Done fb -> return fb++    where+    go !acc m1 =+        let stop = final acc+            single a = step acc a+              >>= \case+                        FL.Partial s -> final s+                        FL.Done b1 -> return b1+            yieldk a r = step acc a+              >>= \case+                        FL.Partial s -> go s r+                        FL.Done b1 -> return b1+         in foldStream defState yieldk single stop m1++-- | Fold resulting in either breaking the stream or continuation of the fold.+-- Instead of supplying the input stream in one go we can run the fold multiple+-- times, each time supplying the next segment of the input stream. If the fold+-- has not yet finished it returns a fold that can be run again otherwise it+-- returns the fold result and the residual stream.+--+-- /Internal/+{-# INLINE foldEither #-}+foldEither :: Monad m =>+    Fold m a b -> StreamK m a -> m (Either (Fold m a b) (b, StreamK m a))+foldEither (FL.Fold step begin done final) m = do+    res <- begin+    case res of+        FL.Partial fs -> go fs m+        FL.Done fb -> return $ Right (fb, m)++    where++    go !acc m1 =+        let stop =+                let f = Fold step (return $ FL.Partial acc) done final+                 in return $ Left f+            single a =+                step acc a+                  >>= \case+                    FL.Partial s ->+                        let f = Fold step (return $ FL.Partial s) done final+                         in return $ Left f+                    FL.Done b1 -> return $ Right (b1, nil)+            yieldk a r =+                step acc a+                  >>= \case+                    FL.Partial s -> go s r+                    FL.Done b1 -> return $ Right (b1, r)+         in foldStream defState yieldk single stop m1++-- | Like 'fold' but also returns the remaining stream. The resulting stream+-- would be 'StreamK.nil' if the stream finished before the fold.+--+{-# INLINE foldBreak #-}+foldBreak :: Monad m => Fold m a b -> StreamK m a -> m (b, StreamK m a)+foldBreak fld strm = do+    r <- foldEither fld strm+    case r of+        Right res -> return res+        Left (Fold _ initial _ final) -> do+            res <- initial+            case res of+                FL.Done _ -> error "foldBreak: unreachable state"+                FL.Partial s -> do+                    b <- final s+                    return (b, nil)++-- XXX Array folds can be implemented using this.+-- foldContainers? Specialized to foldArrays.++-- | Generate streams from individual elements of a stream and fold the+-- concatenation of those streams using the supplied fold. Return the result of+-- the fold and residual stream.+--+-- For example, this can be used to efficiently fold an Array Word8 stream+-- using Word8 folds.+--+-- /Internal/+{-# INLINE foldConcat #-}+foldConcat :: Monad m =>+    Producer m a b -> Fold m b c -> StreamK m a -> m (c, StreamK m a)+foldConcat+    (Producer pstep pinject pextract)+    (Fold fstep begin _ final)+    stream = do++    res <- begin+    case res of+        FL.Partial fs -> go fs stream+        FL.Done fb -> return (fb, stream)++    where++    go !acc m1 = do+        let stop = do+                r <- final acc+                return (r, nil)+            single a = do+                st <- pinject a+                res <- go1 SPEC acc st+                case res of+                    Left fs -> do+                        r <- final fs+                        return (r, nil)+                    Right (b, s) -> do+                        x <- pextract s+                        return (b, fromPure x)+            yieldk a r = do+                st <- pinject a+                res <- go1 SPEC acc st+                case res of+                    Left fs -> go fs r+                    Right (b, s) -> do+                        x <- pextract s+                        return (b, x `cons` r)+         in foldStream defState yieldk single stop m1++    {-# INLINE go1 #-}+    go1 !_ !fs st = do+        r <- pstep st+        case r of+            Stream.Yield x s -> do+                res <- fstep fs x+                case res of+                    FL.Done b -> return $ Right (b, s)+                    FL.Partial fs1 -> go1 SPEC fs1 s+            Stream.Skip s -> go1 SPEC fs s+            Stream.Stop -> return $ Left fs++-- | Like 'foldl'' but with a monadic step function.+{-# INLINE foldlM' #-}+foldlM' :: Monad m => (b -> a -> m b) -> m b -> StreamK m a -> m b+foldlM' step begin = foldlMx' step begin return++------------------------------------------------------------------------------+-- Specialized folds+------------------------------------------------------------------------------++{-# INLINE head #-}+head :: Monad m => StreamK m a -> m (Maybe a)+-- head = foldrM (\x _ -> return $ Just x) (return Nothing)+head m =+    let stop      = return Nothing+        single a  = return (Just a)+        yieldk a _ = return (Just a)+    in foldStream defState yieldk single stop m++{-# INLINE elem #-}+elem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool+elem e = go+    where+    go m1 =+        let stop      = return False+            single a  = return (a == e)+            yieldk a r = if a == e then return True else go r+        in foldStream defState yieldk single stop m1++{-# INLINE notElem #-}+notElem :: (Monad m, Eq a) => a -> StreamK m a -> m Bool+notElem e = go+    where+    go m1 =+        let stop      = return True+            single a  = return (a /= e)+            yieldk a r = if a == e then return False else go r+        in foldStream defState yieldk single stop m1++{-# INLINABLE all #-}+all :: Monad m => (a -> Bool) -> StreamK m a -> m Bool+all p = go+    where+    go m1 =+        let single a   | p a       = return True+                       | otherwise = return False+            yieldk a r | p a       = go r+                       | otherwise = return False+         in foldStream defState yieldk single (return True) m1++{-# INLINABLE any #-}+any :: Monad m => (a -> Bool) -> StreamK m a -> m Bool+any p = go+    where+    go m1 =+        let single a   | p a       = return True+                       | otherwise = return False+            yieldk a r | p a       = return True+                       | otherwise = go r+         in foldStream defState yieldk single (return False) m1++-- | Extract the last element of the stream, if any.+{-# INLINE last #-}+last :: Monad m => StreamK m a -> m (Maybe a)+last = foldlx' (\_ y -> Just y) Nothing id++{-# INLINE minimum #-}+minimum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)+minimum = go Nothing+    where+    go Nothing m1 =+        let stop      = return Nothing+            single a  = return (Just a)+            yieldk a r = go (Just a) r+        in foldStream defState yieldk single stop m1++    go (Just res) m1 =+        let stop      = return (Just res)+            single a  =+                if res <= a+                then return (Just res)+                else return (Just a)+            yieldk a r =+                if res <= a+                then go (Just res) r+                else go (Just a) r+        in foldStream defState yieldk single stop m1++{-# INLINE minimumBy #-}+minimumBy+    :: (Monad m)+    => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)+minimumBy cmp = go Nothing+    where+    go Nothing m1 =+        let stop      = return Nothing+            single a  = return (Just a)+            yieldk a r = go (Just a) r+        in foldStream defState yieldk single stop m1++    go (Just res) m1 =+        let stop      = return (Just res)+            single a  = case cmp res a of+                GT -> return (Just a)+                _  -> return (Just res)+            yieldk a r = case cmp res a of+                GT -> go (Just a) r+                _  -> go (Just res) r+        in foldStream defState yieldk single stop m1++{-# INLINE maximum #-}+maximum :: (Monad m, Ord a) => StreamK m a -> m (Maybe a)+maximum = go Nothing+    where+    go Nothing m1 =+        let stop      = return Nothing+            single a  = return (Just a)+            yieldk a r = go (Just a) r+        in foldStream defState yieldk single stop m1++    go (Just res) m1 =+        let stop      = return (Just res)+            single a  =+                if res <= a+                then return (Just a)+                else return (Just res)+            yieldk a r =+                if res <= a+                then go (Just a) r+                else go (Just res) r+        in foldStream defState yieldk single stop m1++{-# INLINE maximumBy #-}+maximumBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> m (Maybe a)+maximumBy cmp = go Nothing+    where+    go Nothing m1 =+        let stop      = return Nothing+            single a  = return (Just a)+            yieldk a r = go (Just a) r+        in foldStream defState yieldk single stop m1++    go (Just res) m1 =+        let stop      = return (Just res)+            single a  = case cmp res a of+                GT -> return (Just res)+                _  -> return (Just a)+            yieldk a r = case cmp res a of+                GT -> go (Just res) r+                _  -> go (Just a) r+        in foldStream defState yieldk single stop m1++{-# INLINE (!!) #-}+(!!) :: Monad m => StreamK m a -> Int -> m (Maybe a)+m !! i = go i m+    where+    go n m1 =+      let single a | n == 0 = return $ Just a+                   | otherwise = return Nothing+          yieldk a x | n < 0 = return Nothing+                     | n == 0 = return $ Just a+                     | otherwise = go (n - 1) x+      in foldStream defState yieldk single (return Nothing) m1++{-# INLINE lookup #-}+lookup :: (Monad m, Eq a) => a -> StreamK m (a, b) -> m (Maybe b)+lookup e = go+    where+    go m1 =+        let single (a, b) | a == e = return $ Just b+                          | otherwise = return Nothing+            yieldk (a, b) x | a == e = return $ Just b+                            | otherwise = go x+        in foldStream defState yieldk single (return Nothing) m1++{-# INLINE findM #-}+findM :: Monad m => (a -> m Bool) -> StreamK m a -> m (Maybe a)+findM p = go+    where+    go m1 =+        let single a = do+                b <- p a+                if b then return $ Just a else return Nothing+            yieldk a x = do+                b <- p a+                if b then return $ Just a else go x+        in foldStream defState yieldk single (return Nothing) m1++{-# INLINE find #-}+find :: Monad m => (a -> Bool) -> StreamK m a -> m (Maybe a)+find p = findM (return . p)++{-# INLINE findIndices #-}+findIndices :: (a -> Bool) -> StreamK m a -> StreamK m Int+findIndices p = go 0+    where+    go offset m1 = mkStream $ \st yld sng stp ->+        let single a | p a = sng offset+                     | otherwise = stp+            yieldk a x | p a = yld offset $ go (offset + 1) x+                       | otherwise = foldStream (adaptState st) yld sng stp $+                            go (offset + 1) x+        in foldStream (adaptState st) yieldk single stp m1++------------------------------------------------------------------------------+-- Map and Fold+------------------------------------------------------------------------------++-- | Apply a monadic action to each element of the stream and discard the+-- output of the action.+{-# INLINE mapM_ #-}+mapM_ :: Monad m => (a -> m b) -> StreamK m a -> m ()+mapM_ f = go+    where+    go m1 =+        let stop = return ()+            single a = void (f a)+            yieldk a r = f a >> go r+         in foldStream defState yieldk single stop m1++{-# INLINE mapM #-}+mapM :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b+mapM = mapMWith consM++------------------------------------------------------------------------------+-- Converting folds+------------------------------------------------------------------------------++{-# INLINABLE toList #-}+toList :: Monad m => StreamK m a -> m [a]+toList = foldr (:) []++-- Based on suggestions by David Feuer and Pranay Sashank+{-# INLINE hoist #-}+hoist :: (Monad m, Monad n)+    => (forall x. m x -> n x) -> StreamK m a -> StreamK n a+hoist f str =+    mkStream $ \st yld sng stp ->+            let single = return . sng+                yieldk a s = return $ yld a (hoist f s)+                stop = return stp+                state = adaptState st+             in join . f $ foldStreamShared state yieldk single stop str++-------------------------------------------------------------------------------+-- Transformation by folding (Scans)+-------------------------------------------------------------------------------++{-# INLINE scanlx' #-}+scanlx' :: (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> StreamK m b+scanlx' step begin done m =+    cons (done begin) $ go m begin+    where+    go m1 !acc = mkStream $ \st yld sng stp ->+        let single a = sng (done $ step acc a)+            yieldk a r =+                let s = step acc a+                in yld (done s) (go r s)+        in foldStream (adaptState st) yieldk single stp m1++{-# INLINE scanl' #-}+scanl' :: (b -> a -> b) -> b -> StreamK m a -> StreamK m b+scanl' step begin = scanlx' step begin id++-------------------------------------------------------------------------------+-- Filtering+-------------------------------------------------------------------------------++{-# INLINE filter #-}+filter :: (a -> Bool) -> StreamK m a -> StreamK m a+filter p = go+    where+    go m1 = mkStream $ \st yld sng stp ->+        let single a   | p a       = sng a+                       | otherwise = stp+            yieldk a r | p a       = yld a (go r)+                       | otherwise = foldStream st yieldk single stp r+         in foldStream st yieldk single stp m1++{-# INLINE take #-}+take :: Int -> StreamK m a -> StreamK m a+take = go+    where+    go n1 m1 = mkStream $ \st yld sng stp ->+        let yieldk a r = yld a (go (n1 - 1) r)+        in if n1 <= 0+           then stp+           else foldStream st yieldk sng stp m1++{-# INLINE takeWhile #-}+takeWhile :: (a -> Bool) -> StreamK m a -> StreamK m a+takeWhile p = go+    where+    go m1 = mkStream $ \st yld sng stp ->+        let single a   | p a       = sng a+                       | otherwise = stp+            yieldk a r | p a       = yld a (go r)+                       | otherwise = stp+         in foldStream st yieldk single stp m1++{-# INLINE drop #-}+drop :: Int -> StreamK m a -> StreamK m a+drop n m = unShare (go n m)+    where+    go n1 m1 = mkStream $ \st yld sng stp ->+        let single _ = stp+            yieldk _ r = foldStreamShared st yld sng stp $ go (n1 - 1) r+        -- Somehow "<=" check performs better than a ">"+        in if n1 <= 0+           then foldStreamShared st yld sng stp m1+           else foldStreamShared st yieldk single stp m1++{-# INLINE dropWhile #-}+dropWhile :: (a -> Bool) -> StreamK m a -> StreamK m a+dropWhile p = go+    where+    go m1 = mkStream $ \st yld sng stp ->+        let single a   | p a       = stp+                       | otherwise = sng a+            yieldk a r | p a = foldStream st yieldk single stp r+                       | otherwise = yld a r+         in foldStream st yieldk single stp m1++-------------------------------------------------------------------------------+-- Mapping+-------------------------------------------------------------------------------++-- Be careful when modifying this, this uses a consM (|:) deliberately to allow+-- other stream types to overload it.+{-# INLINE sequence #-}+sequence :: Monad m => StreamK m (m a) -> StreamK m a+sequence = go+    where+    go m1 = mkStream $ \st yld sng stp ->+        let single ma = ma >>= sng+            yieldk ma r = foldStreamShared st yld sng stp $ ma `consM` go r+         in foldStream (adaptState st) yieldk single stp m1++-------------------------------------------------------------------------------+-- Inserting+-------------------------------------------------------------------------------++{-# INLINE intersperseM #-}+intersperseM :: Monad m => m a -> StreamK m a -> StreamK m a+intersperseM a = prependingStart+    where+    prependingStart m1 = mkStream $ \st yld sng stp ->+        let yieldk i x =+                foldStreamShared st yld sng stp $ return i `consM` go x+         in foldStream st yieldk sng stp m1+    go m2 = mkStream $ \st yld sng stp ->+        let single i = foldStreamShared st yld sng stp $ a `consM` fromPure i+            yieldk i x =+                foldStreamShared+                    st yld sng stp $ a `consM` return i `consM` go x+         in foldStream st yieldk single stp m2++{-# INLINE intersperse #-}+intersperse :: Monad m => a -> StreamK m a -> StreamK m a+intersperse a = intersperseM (return a)++{-# INLINE insertBy #-}+insertBy :: (a -> a -> Ordering) -> a -> StreamK m a -> StreamK m a+insertBy cmp x = go+  where+    go m1 = mkStream $ \st yld _ _ ->+        let single a = case cmp x a of+                GT -> yld a (fromPure x)+                _  -> yld x (fromPure a)+            stop = yld x nil+            yieldk a r = case cmp x a of+                GT -> yld a (go r)+                _  -> yld x (a `cons` r)+         in foldStream st yieldk single stop m1++------------------------------------------------------------------------------+-- Deleting+------------------------------------------------------------------------------++{-# INLINE deleteBy #-}+deleteBy :: (a -> a -> Bool) -> a -> StreamK m a -> StreamK m a+deleteBy eq x = go+  where+    go m1 = mkStream $ \st yld sng stp ->+        let single a = if eq x a then stp else sng a+            yieldk a r = if eq x a+              then foldStream st yld sng stp r+              else yld a (go r)+         in foldStream st yieldk single stp m1++-------------------------------------------------------------------------------+-- Map and Filter+-------------------------------------------------------------------------------++{-# INLINE mapMaybe #-}+mapMaybe :: (a -> Maybe b) -> StreamK m a -> StreamK m b+mapMaybe f = go+  where+    go m1 = mkStream $ \st yld sng stp ->+        let single a = maybe stp sng (f a)+            yieldk a r = case f a of+                Just b  -> yld b $ go r+                Nothing -> foldStream (adaptState st) yieldk single stp r+        in foldStream (adaptState st) yieldk single stp m1++-------------------------------------------------------------------------------+-- Exception Handling+-------------------------------------------------------------------------------++-- | Like Streamly.Data.Stream.'Streamly.Data.Stream.handle' but with one+-- significant difference, this function observes exceptions from the consumer+-- of the stream as well.+--+-- You can also convert 'StreamK' to 'Stream' and use exception handling from+-- 'Stream' module:+--+-- >>> handle f s = StreamK.fromStream $ Stream.handle (\e -> StreamK.toStream (f e)) (StreamK.toStream s)+--+{-# INLINABLE handle #-}+handle :: (MonadCatch m, Exception e)+    => (e -> m (StreamK m a)) -> StreamK m a -> StreamK m a+handle f stream = go stream++    where++    go m1 = mkStream $ \st yld sng stp ->+        let yieldk a r = yld a $ go r+        in do+            res <- MC.try (foldStream (adaptState st) yieldk sng stp m1)+            case res of+                Right r -> return r+                Left e -> do+                    r <- f e+                    foldStream (adaptState st) yld sng stp r++-------------------------------------------------------------------------------+-- Resource Management+-------------------------------------------------------------------------------++-- If we are folding the stream and we do not drain the entire stream (e.g. if+-- the fold terminates before the stream) then the finalizer will run on GC.+--+-- XXX To implement a prompt cleanup, we will have to yield a cleanup function+-- via the yield continuation. A chain of cleanup functions can be built and+-- the entire chain can be invoked when the stream ends voluntarily or if+-- someone decides to abandon the stream.++-- | Like Streamly.Data.Stream.'Streamly.Data.Stream.bracketIO' but with one+-- significant difference, this function observes exceptions from the consumer+-- of the stream as well. Therefore, it cleans up the resource promptly when+-- the consumer encounters an exception.+--+-- You can also convert 'StreamK' to 'Stream' and use resource handling from+-- 'Stream' module:+--+-- >>> bracketIO bef aft bet = StreamK.fromStream $ Stream.bracketIO bef aft (StreamK.toStream . bet)+--+{-# INLINABLE bracketIO #-}+bracketIO :: (MonadIO m, MonadCatch m)+    => IO b -> (b -> IO c) -> (b -> StreamK m a) -> StreamK m a+bracketIO bef aft bet =+    concatEffect $ do+        (r, ref) <- liftIO $ mask_ $ do+            r <- bef+            ref <- newIOFinalizer (aft r)+            return (r, ref)+        return $ go ref (bet r)++    where++    go ref m1 = mkStream $ \st yld sng stp ->+        let+            -- We can discard exceptions on continuations to make it equivalent+            -- to StreamD, but it seems like a desirable behavior.+            stop = liftIO (runIOFinalizer ref) >> stp+            single a = liftIO (runIOFinalizer ref) >> sng a+            yieldk a r = yld a $ go ref r+        in do+            -- Do not call the finalizer twice if it has already been+            -- called via stop continuation and stop continuation itself+            -- generated an exception. runIOFinalizer takes care of that.+            res <- MC.try (foldStream (adaptState st) yieldk single stop m1)+            case res of+                Right r -> return r+                Left (e :: MC.SomeException) ->+                    liftIO (runIOFinalizer ref) >> MC.throwM e++------------------------------------------------------------------------------+-- Serial Zipping+------------------------------------------------------------------------------++-- | Zipping of @n@ streams can be performed by combining the streams pair+-- wise using 'mergeMapWith' with O(n * log n) time complexity. If used+-- with 'concatMapWith' it will have O(n^2) performance.+{-# INLINE zipWith #-}+zipWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c+zipWith f = zipWithM (\a b -> return (f a b))++{-# INLINE zipWithM #-}+zipWithM :: Monad m =>+    (a -> b -> m c) -> StreamK m a -> StreamK m b -> StreamK m c+zipWithM f = go++    where++    go mx my = mkStream $ \st yld sng stp -> do+        let merge a ra =+                let single2 b   = f a b >>= sng+                    yield2 b rb = f a b >>= \x -> yld x (go ra rb)+                 in foldStream (adaptState st) yield2 single2 stp my+        let single1 a = merge a nil+            yield1 = merge+        foldStream (adaptState st) yield1 single1 stp mx++------------------------------------------------------------------------------+-- Merging+------------------------------------------------------------------------------++{-# INLINE mergeByM #-}+mergeByM :: Monad m =>+    (a -> a -> m Ordering) -> StreamK m a -> StreamK m a -> StreamK m a+mergeByM cmp = go++    where++    go mx my = mkStream $ \st yld sng stp -> do+        let stop = foldStream st yld sng stp my+            single x = foldStream st yld sng stp (goX0 x my)+            yield x rx = foldStream st yld sng stp (goX x rx my)+        foldStream st yield single stop mx++    goX0 x my = mkStream $ \st yld sng _ -> do+        let stop = sng x+            single y = do+                r <- cmp x y+                case r of+                    GT -> yld y (fromPure x)+                    _  -> yld x (fromPure y)+            yield y ry = do+                r <- cmp x y+                case r of+                    GT -> yld y (goX0 x ry)+                    _  -> yld x (y `cons` ry)+         in foldStream st yield single stop my++    goX x mx my = mkStream $ \st yld _ _ -> do+        let stop = yld x mx+            single y = do+                r <- cmp x y+                case r of+                    GT -> yld y (x `cons` mx)+                    _  -> yld x (goY0 mx y)+            yield y ry = do+                r <- cmp x y+                case r of+                    GT -> yld y (goX x mx ry)+                    _  -> yld x (goY mx y ry)+         in foldStream st yield single stop my++    goY0 mx y = mkStream $ \st yld sng _ -> do+        let stop = sng y+            single x = do+                r <- cmp x y+                case r of+                    GT -> yld y (fromPure x)+                    _  -> yld x (fromPure y)+            yield x rx = do+                r <- cmp x y+                case r of+                    GT -> yld y (x `cons` rx)+                    _  -> yld x (goY0 rx y)+         in foldStream st yield single stop mx++    goY mx y my = mkStream $ \st yld _ _ -> do+        let stop = yld y my+            single x = do+                r <- cmp x y+                case r of+                    GT -> yld y (goX0 x my)+                    _  -> yld x (y `cons` my)+            yield x rx = do+                r <- cmp x y+                case r of+                    GT -> yld y (goX x rx my)+                    _  -> yld x (goY rx y my)+         in foldStream st yield single stop mx++-- | Merging of @n@ streams can be performed by combining the streams pair+-- wise using 'mergeMapWith' to give O(n * log n) time complexity. If used+-- with 'concatMapWith' it will have O(n^2) performance.+--+{-# INLINE mergeBy #-}+mergeBy :: (a -> a -> Ordering) -> StreamK m a -> StreamK m a -> StreamK m a+-- XXX GHC: This has slightly worse performance than replacing "r <- cmp x y"+-- with "let r = cmp x y" in the monadic version. The definition below is+-- exactly the same as mergeByM except this change.+-- mergeBy cmp = mergeByM (\a b -> return $ cmp a b)+mergeBy cmp = go++    where++    go mx my = mkStream $ \st yld sng stp -> do+        let stop = foldStream st yld sng stp my+            single x = foldStream st yld sng stp (goX0 x my)+            yield x rx = foldStream st yld sng stp (goX x rx my)+        foldStream st yield single stop mx++    goX0 x my = mkStream $ \st yld sng _ -> do+        let stop = sng x+            single y = do+                case cmp x y of+                    GT -> yld y (fromPure x)+                    _  -> yld x (fromPure y)+            yield y ry = do+                case cmp x y of+                    GT -> yld y (goX0 x ry)+                    _  -> yld x (y `cons` ry)+         in foldStream st yield single stop my++    goX x mx my = mkStream $ \st yld _ _ -> do+        let stop = yld x mx+            single y = do+                case cmp x y of+                    GT -> yld y (x `cons` mx)+                    _  -> yld x (goY0 mx y)+            yield y ry = do+                case cmp x y of+                    GT -> yld y (goX x mx ry)+                    _  -> yld x (goY mx y ry)+         in foldStream st yield single stop my++    goY0 mx y = mkStream $ \st yld sng _ -> do+        let stop = sng y+            single x = do+                case cmp x y of+                    GT -> yld y (fromPure x)+                    _  -> yld x (fromPure y)+            yield x rx = do+                case cmp x y of+                    GT -> yld y (x `cons` rx)+                    _  -> yld x (goY0 rx y)+         in foldStream st yield single stop mx++    goY mx y my = mkStream $ \st yld _ _ -> do+        let stop = yld y my+            single x = do+                case cmp x y of+                    GT -> yld y (goX0 x my)+                    _  -> yld x (y `cons` my)+            yield x rx = do+                case cmp x y of+                    GT -> yld y (goX x rx my)+                    _  -> yld x (goY rx y my)+         in foldStream st yield single stop mx++------------------------------------------------------------------------------+-- Transformation comprehensions+------------------------------------------------------------------------------++{-# INLINE the #-}+the :: (Eq a, Monad m) => StreamK m a -> m (Maybe a)+the m = do+    r <- uncons m+    case r of+        Nothing -> return Nothing+        Just (h, t) -> go h t+    where+    go h m1 =+        let single a   | h == a    = return $ Just h+                       | otherwise = return Nothing+            yieldk a r | h == a    = go h r+                       | otherwise = return Nothing+         in foldStream defState yieldk single (return $ Just h) m1++------------------------------------------------------------------------------+-- Alternative & MonadPlus+------------------------------------------------------------------------------++_alt :: StreamK m a -> StreamK m a -> StreamK m a+_alt m1 m2 = mkStream $ \st yld sng stp ->+    let stop  = foldStream st yld sng stp m2+    in foldStream st yld sng stop m1++------------------------------------------------------------------------------+-- MonadError+------------------------------------------------------------------------------++{-+-- XXX handle and test cross thread state transfer+withCatchError+    :: MonadError e m+    => StreamK m a -> (e -> StreamK m a) -> StreamK m a+withCatchError m h =+    mkStream $ \_ stp sng yld ->+        let run x = unStream x Nothing stp sng yieldk+            handle r = r `catchError` \e -> run $ h e+            yieldk a r = yld a (withCatchError r h)+        in handle $ run m+-}++-------------------------------------------------------------------------------+-- Parsing+-------------------------------------------------------------------------------++-- Inlined definition.+{-# INLINE splitAt #-}+splitAt :: Int -> [a] -> ([a],[a])+splitAt n ls+  | n <= 0 = ([], ls)+  | otherwise          = splitAt' n ls+    where+        splitAt' :: Int -> [a] -> ([a], [a])+        splitAt' _  []     = ([], [])+        splitAt' 1  (x:xs) = ([x], xs)+        splitAt' m  (x:xs) = (x:xs', xs'')+          where+            (xs', xs'') = splitAt' (m - 1) xs++-- | Run a 'Parser' over a stream and return rest of the Stream.+{-# INLINE_NORMAL parseDBreak #-}+parseDBreak+    :: Monad m+    => PR.Parser a m b+    -> StreamK m a+    -> m (Either ParseError b, StreamK m a)+parseDBreak (PR.Parser pstep initial extract) stream = do+    res <- initial+    case res of+        PR.IPartial s -> goStream stream [] s+        PR.IDone b -> return (Right b, stream)+        PR.IError err -> return (Left (ParseError err), stream)++    where++    -- "buf" contains last few items in the stream that we may have to+    -- backtrack to.+    --+    -- XXX currently we are using a dumb list based approach for backtracking+    -- buffer. This can be replaced by a sliding/ring buffer using Data.Array.+    -- That will allow us more efficient random back and forth movement.+    goStream st buf !pst =+        let stop = do+                r <- extract pst+                case r of+                    PR.Error err -> do+                        let src = Prelude.reverse buf+                        return (Left (ParseError err), fromList src)+                    PR.Done n b -> do+                        assertM(n <= length buf)+                        let src0 = Prelude.take n buf+                            src  = Prelude.reverse src0+                        return (Right b, fromList src)+                    PR.Partial _ _ -> error "Bug: parseBreak: Partial in extract"+                    PR.Continue 0 s -> goStream nil buf s+                    PR.Continue n s -> do+                        assertM(n <= length buf)+                        let (src0, buf1) = splitAt n buf+                            src = Prelude.reverse src0+                        goBuf nil buf1 src s+            single x = yieldk x nil+            yieldk x r = do+                res <- pstep pst x+                case res of+                    PR.Partial 0 s -> goStream r [] s+                    PR.Partial n s -> do+                        assertM(n <= length (x:buf))+                        let src0 = Prelude.take n (x:buf)+                            src  = Prelude.reverse src0+                        goBuf r [] src s+                    PR.Continue 0 s -> goStream r (x:buf) s+                    PR.Continue n s -> do+                        assertM(n <= length (x:buf))+                        let (src0, buf1) = splitAt n (x:buf)+                            src = Prelude.reverse src0+                        goBuf r buf1 src s+                    PR.Done 0 b -> return (Right b, r)+                    PR.Done n b -> do+                        assertM(n <= length (x:buf))+                        let src0 = Prelude.take n (x:buf)+                            src  = Prelude.reverse src0+                        return (Right b, append (fromList src) r)+                    PR.Error err -> do+                        let src = Prelude.reverse (x:buf)+                        return (Left (ParseError err), append (fromList src) r)+         in foldStream defState yieldk single stop st++    goBuf st buf [] !pst = goStream st buf pst+    goBuf st buf (x:xs) !pst = do+        pRes <- pstep pst x+        case pRes of+            PR.Partial 0 s -> goBuf st [] xs s+            PR.Partial n s -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                goBuf st [] src s+            PR.Continue 0 s -> goBuf st (x:buf) xs s+            PR.Continue n s -> do+                assert (n <= length (x:buf)) (return ())+                let (src0, buf1) = splitAt n (x:buf)+                    src  = Prelude.reverse src0 ++ xs+                goBuf st buf1 src s+            PR.Done n b -> do+                assert (n <= length (x:buf)) (return ())+                let src0 = Prelude.take n (x:buf)+                    src  = Prelude.reverse src0+                return (Right b, append (fromList src) st)+            PR.Error err -> do+                let src = Prelude.reverse buf ++ x:xs+                return (Left (ParseError err), append (fromList src) st)++-- Using ParserD or ParserK on StreamK may not make much difference. We should+-- perhaps use only chunked parsing on StreamK. We can always convert a stream+-- to chunks before parsing. Or just have a ParserK element parser for StreamK+-- and convert ParserD to ParserK for element parsing using StreamK.+{-# INLINE parseD #-}+parseD :: Monad m =>+    Parser.Parser a m b -> StreamK m a -> m (Either ParseError b)+parseD f = fmap fst . parseDBreak f++-------------------------------------------------------------------------------+-- ParserK Chunked+-------------------------------------------------------------------------------++-- The backracking buffer consists of arrays in the most-recent-first order. We+-- want to take a total of n array elements from this buffer. Note: when we+-- have to take an array partially, we must take the last part of the array.+{-# INLINE backTrack #-}+backTrack :: forall m a. Unbox a =>+       Int+    -> [Array a]+    -> StreamK m (Array a)+    -> (StreamK m (Array a), [Array a])+backTrack = go++    where++    go _ [] stream = (stream, [])+    go n xs stream | n <= 0 = (stream, xs)+    go n (x:xs) stream =+        let len = Array.length x+        in if n > len+           then go (n - len) xs (cons x stream)+           else if n == len+           then (cons x stream, xs)+           else let !(Array contents start end) = x+                    !start1 = end - (n * SIZE_OF(a))+                    arr1 = Array contents start1 end+                    arr2 = Array contents start start1+                 in (cons arr1 stream, arr2:xs)++-- | A continuation to extract the result when a CPS parser is done.+{-# INLINE parserDone #-}+parserDone :: Applicative m =>+    ParserK.ParseResult b -> Int -> ParserK.Input a -> m (ParserK.Step a m b)+parserDone (ParserK.Success n b) _ _ = pure $ ParserK.Done n b+parserDone (ParserK.Failure n e) _ _ = pure $ ParserK.Error n e++-- XXX parseDBreakChunks may be faster than converting parserD to parserK and+-- using parseBreakChunks. We can also use parseBreak as an alternative to the+-- monad instance of ParserD.++-- | Run a 'ParserK' over a chunked 'StreamK' and return the parse result and+-- the remaining Stream.+{-# INLINE_NORMAL parseBreakChunks #-}+parseBreakChunks+    :: (Monad m, Unbox a)+    => ParserK (Array a) m b+    -> StreamK m (Array a)+    -> m (Either ParseError b, StreamK m (Array a))+parseBreakChunks parser input = do+    let parserk = ParserK.runParser parser parserDone 0 0+     in go [] parserk input++    where++    {-# INLINE goStop #-}+    goStop backBuf parserk = do+        pRes <- parserk ParserK.None+        case pRes of+            -- If we stop in an alternative, it will try calling the next+            -- parser, the next parser may call initial returning Partial and+            -- then immediately we have to call extract on it.+            ParserK.Partial 0 cont1 ->+                 go [] cont1 nil+            ParserK.Partial n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))+                let (s1, backBuf1) = backTrack n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Continue 0 cont1 ->+                go backBuf cont1 nil+            ParserK.Continue n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))+                let (s1, backBuf1) = backTrack n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Done 0 b ->+                return (Right b, nil)+            ParserK.Done n b -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map Array.length backBuf))+                let (s1, _) = backTrack n1 backBuf nil+                 in return (Right b, s1)+            ParserK.Error _ err -> do+                let (s1, _) = backTrack maxBound backBuf nil+                return (Left (ParseError err), s1)++    seekErr n len =+        error $ "parseBreak: Partial: forward seek not implemented n = "+            ++ show n ++ " len = " ++ show len++    yieldk backBuf parserk arr stream = do+        pRes <- parserk (ParserK.Chunk arr)+        let len = Array.length arr+        case pRes of+            ParserK.Partial n cont1 ->+                case compare n len of+                    EQ -> go [] cont1 stream+                    LT -> do+                        if n >= 0+                        then yieldk [] cont1 arr stream+                        else do+                            let n1 = negate n+                                bufLen = sum (Prelude.map Array.length backBuf)+                                s = cons arr stream+                            assertM(n1 >= 0 && n1 <= bufLen)+                            let (s1, _) = backTrack n1 backBuf s+                            go [] cont1 s1+                    GT -> seekErr n len+            ParserK.Continue n cont1 ->+                case compare n len of+                    EQ -> go (arr:backBuf) cont1 stream+                    LT -> do+                        if n >= 0+                        then yieldk backBuf cont1 arr stream+                        else do+                            let n1 = negate n+                                bufLen = sum (Prelude.map Array.length backBuf)+                                s = cons arr stream+                            assertM(n1 >= 0 && n1 <= bufLen)+                            let (s1, backBuf1) = backTrack n1 backBuf s+                            go backBuf1 cont1 s1+                    GT -> seekErr n len+            ParserK.Done n b -> do+                let n1 = len - n+                assertM(n1 <= sum (Prelude.map Array.length (arr:backBuf)))+                let (s1, _) = backTrack n1 (arr:backBuf) stream+                 in return (Right b, s1)+            ParserK.Error _ err -> do+                let (s1, _) = backTrack maxBound (arr:backBuf) stream+                return (Left (ParseError err), s1)++    go backBuf parserk stream = do+        let stop = goStop backBuf parserk+            single a = yieldk backBuf parserk a nil+         in foldStream+                defState (yieldk backBuf parserk) single stop stream++{-# INLINE parseChunks #-}+parseChunks :: (Monad m, Unbox a) =>+    ParserK (Array a) m b -> StreamK m (Array a) -> m (Either ParseError b)+parseChunks f = fmap fst . parseBreakChunks f++-------------------------------------------------------------------------------+-- ParserK Singular+-------------------------------------------------------------------------------++{-# INLINE backTrackSingular #-}+backTrackSingular :: Int -> [a] -> StreamK m a -> (StreamK m a, [a])+backTrackSingular = go++    where++    go _ [] stream = (stream, [])+    go n xs stream | n <= 0 = (stream, xs)+    go n xs stream =+        let (appendBuf, newBTBuf) = splitAt n xs+         in (append (fromList (Prelude.reverse appendBuf)) stream, newBTBuf)+++-- | Similar to 'parseBreak' but works on singular elements.+--+{-# INLINE_NORMAL parseBreak #-}+parseBreak+    :: forall m a b. Monad m+    => ParserK.ParserK a m b+    -> StreamK m a+    -> m (Either ParseError b, StreamK m a)+parseBreak parser input = do+    let parserk = ParserK.runParser parser parserDone 0 0+     in go [] parserk input++    where++    {-# INLINE goStop #-}+    goStop+        :: [a]+        -> (ParserK.Input a -> m (ParserK.Step a m b))+        -> m (Either ParseError b, StreamK m a)+    goStop backBuf parserk = do+        pRes <- parserk ParserK.None+        case pRes of+            -- If we stop in an alternative, it will try calling the next+            -- parser, the next parser may call initial returning Partial and+            -- then immediately we have to call extract on it.+            ParserK.Partial 0 cont1 ->+                 go [] cont1 nil+            ParserK.Partial n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= length backBuf)+                let (s1, backBuf1) = backTrackSingular n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Continue 0 cont1 ->+                go backBuf cont1 nil+            ParserK.Continue n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= length backBuf)+                let (s1, backBuf1) = backTrackSingular n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Done 0 b ->+                return (Right b, nil)+            ParserK.Done n b -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= length backBuf)+                let (s1, _) = backTrackSingular n1 backBuf nil+                 in return (Right b, s1)+            ParserK.Error _ err -> return (Left (ParseError err), nil)++    seekErr n =+        error $ "parseBreak: Partial: forward seek not implemented n = "+            ++ show n++    yieldk+        :: [a]+        -> (ParserK.Input a -> m (ParserK.Step a m b))+        -> a+        -> StreamK m a+        -> m (Either ParseError b, StreamK m a)+    yieldk backBuf parserk arr stream = do+        pRes <- parserk (ParserK.Chunk arr)+        case pRes of+            ParserK.Partial 1 cont1 -> go [] cont1 stream+            ParserK.Partial 0 cont1 -> go [] cont1 (cons arr stream)+            ParserK.Partial n _ | n > 1 -> seekErr n+            ParserK.Partial n cont1 -> do+                let n1 = negate n+                    bufLen = length backBuf+                    s = cons arr stream+                assertM(n1 >= 0 && n1 <= bufLen)+                let (s1, _) = backTrackSingular n1 backBuf s+                go [] cont1 s1+            ParserK.Continue 1 cont1 -> go (arr:backBuf) cont1 stream+            ParserK.Continue 0 cont1 ->+                go backBuf cont1 (cons arr stream)+            ParserK.Continue n _ | n > 1 -> seekErr n+            ParserK.Continue n cont1 -> do+                let n1 = negate n+                    bufLen = length backBuf+                    s = cons arr stream+                assertM(n1 >= 0 && n1 <= bufLen)+                let (s1, backBuf1) = backTrackSingular n1 backBuf s+                go backBuf1 cont1 s1+            ParserK.Done 1 b -> pure (Right b, stream)+            ParserK.Done 0 b -> pure (Right b, cons arr stream)+            ParserK.Done n _ | n > 1 -> seekErr n+            ParserK.Done n b -> do+                let n1 = negate n+                    bufLen = length backBuf+                    s = cons arr stream+                assertM(n1 >= 0 && n1 <= bufLen)+                let (s1, _) = backTrackSingular n1 backBuf s+                pure (Right b, s1)+            ParserK.Error _ err -> return (Left (ParseError err), nil)++    go+        :: [a]+        -> (ParserK.Input a -> m (ParserK.Step a m b))+        -> StreamK m a+        -> m (Either ParseError b, StreamK m a)+    go backBuf parserk stream = do+        let stop = goStop backBuf parserk+            single a = yieldk backBuf parserk a nil+         in foldStream+                defState (yieldk backBuf parserk) single stop stream++-- | Run a 'ParserK' over a 'StreamK'. Please use 'parseChunks' where possible,+-- for better performance.+{-# INLINE parse #-}+parse :: Monad m =>+    ParserK.ParserK a m b -> StreamK m a -> m (Either ParseError b)+parse f = fmap fst . parseBreak f++-------------------------------------------------------------------------------+-- ParserK Chunked Generic+-------------------------------------------------------------------------------++{-# INLINE backTrackGenericChunks #-}+backTrackGenericChunks ::+       Int+    -> [GenArr.Array a]+    -> StreamK m (GenArr.Array a)+    -> (StreamK m (GenArr.Array a), [GenArr.Array a])+backTrackGenericChunks = go++    where++    go _ [] stream = (stream, [])+    go n xs stream | n <= 0 = (stream, xs)+    go n (x:xs) stream =+        let len = GenArr.length x+        in if n > len+           then go (n - len) xs (cons x stream)+           else if n == len+           then (cons x stream, xs)+           else let arr1 = GenArr.getSliceUnsafe (len - n) n x+                    arr2 = GenArr.getSliceUnsafe 0 (len - n) x+                 in (cons arr1 stream, arr2:xs)++-- | Similar to 'parseBreak' but works on generic arrays+--+{-# INLINE_NORMAL parseBreakChunksGeneric #-}+parseBreakChunksGeneric+    :: forall m a b. Monad m+    => ParserK.ParserK (GenArr.Array a) m b+    -> StreamK m (GenArr.Array a)+    -> m (Either ParseError b, StreamK m (GenArr.Array a))+parseBreakChunksGeneric parser input = do+    let parserk = ParserK.runParser parser parserDone 0 0+     in go [] parserk input++    where++    {-# INLINE goStop #-}+    goStop+        :: [GenArr.Array a]+        -> (ParserK.Input (GenArr.Array a)+                -> m (ParserK.Step (GenArr.Array a) m b))+        -> m (Either ParseError b, StreamK m (GenArr.Array a))+    goStop backBuf parserk = do+        pRes <- parserk ParserK.None+        case pRes of+            -- If we stop in an alternative, it will try calling the next+            -- parser, the next parser may call initial returning Partial and+            -- then immediately we have to call extract on it.+            ParserK.Partial 0 cont1 ->+                 go [] cont1 nil+            ParserK.Partial n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map GenArr.length backBuf))+                let (s1, backBuf1) = backTrackGenericChunks n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Continue 0 cont1 ->+                go backBuf cont1 nil+            ParserK.Continue n cont1 -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map GenArr.length backBuf))+                let (s1, backBuf1) = backTrackGenericChunks n1 backBuf nil+                 in go backBuf1 cont1 s1+            ParserK.Done 0 b ->+                return (Right b, nil)+            ParserK.Done n b -> do+                let n1 = negate n+                assertM(n1 >= 0 && n1 <= sum (Prelude.map GenArr.length backBuf))+                let (s1, _) = backTrackGenericChunks n1 backBuf nil+                 in return (Right b, s1)+            ParserK.Error _ err -> return (Left (ParseError err), nil)++    seekErr n len =+        error $ "parseBreak: Partial: forward seek not implemented n = "+            ++ show n ++ " len = " ++ show len++    yieldk+        :: [GenArr.Array a]+        -> (ParserK.Input (GenArr.Array a)+                -> m (ParserK.Step (GenArr.Array a) m b))+        -> GenArr.Array a+        -> StreamK m (GenArr.Array a)+        -> m (Either ParseError b, StreamK m (GenArr.Array a))+    yieldk backBuf parserk arr stream = do+        pRes <- parserk (ParserK.Chunk arr)+        let len = GenArr.length arr+        case pRes of+            ParserK.Partial n cont1 ->+                case compare n len of+                    EQ -> go [] cont1 stream+                    LT -> do+                        if n >= 0+                        then yieldk [] cont1 arr stream+                        else do+                            let n1 = negate n+                                bufLen = sum (Prelude.map GenArr.length backBuf)+                                s = cons arr stream+                            assertM(n1 >= 0 && n1 <= bufLen)+                            let (s1, _) = backTrackGenericChunks n1 backBuf s+                            go [] cont1 s1+                    GT -> seekErr n len+            ParserK.Continue n cont1 ->+                case compare n len of+                    EQ -> go (arr:backBuf) cont1 stream+                    LT -> do+                        if n >= 0+                        then yieldk backBuf cont1 arr stream+                        else do+                            let n1 = negate n+                                bufLen = sum (Prelude.map GenArr.length backBuf)+                                s = cons arr stream+                            assertM(n1 >= 0 && n1 <= bufLen)+                            let (s1, backBuf1) = backTrackGenericChunks n1 backBuf s+                            go backBuf1 cont1 s1+                    GT -> seekErr n len+            ParserK.Done n b -> do+                let n1 = len - n+                assertM(n1 <= sum (Prelude.map GenArr.length (arr:backBuf)))+                let (s1, _) = backTrackGenericChunks n1 (arr:backBuf) stream+                 in return (Right b, s1)+            ParserK.Error _ err -> return (Left (ParseError err), nil)++    go+        :: [GenArr.Array a]+        -> (ParserK.Input (GenArr.Array a)+                -> m (ParserK.Step (GenArr.Array a) m b))+        -> StreamK m (GenArr.Array a)+        -> m (Either ParseError b, StreamK m (GenArr.Array a))+    go backBuf parserk stream = do+        let stop = goStop backBuf parserk+            single a = yieldk backBuf parserk a nil+         in foldStream+                defState (yieldk backBuf parserk) single stop stream++{-# INLINE parseChunksGeneric #-}+parseChunksGeneric ::+       (Monad m)+    => ParserK.ParserK (GenArr.Array a) m b+    -> StreamK m (GenArr.Array a)+    -> m (Either ParseError b)+parseChunksGeneric f = fmap fst . parseBreakChunksGeneric f++-------------------------------------------------------------------------------+-- Sorting+-------------------------------------------------------------------------------++-- | Sort the input stream using a supplied comparison function.+--+-- Sorting can be achieved by simply:+--+-- >>> sortBy cmp = StreamK.mergeMapWith (StreamK.mergeBy cmp) StreamK.fromPure+--+-- However, this combinator uses a parser to first split the input stream into+-- down and up sorted segments and then merges them to optimize sorting when+-- pre-sorted sequences exist in the input stream.+--+-- /O(n) space/+--+{-# INLINE sortBy #-}+sortBy :: Monad m => (a -> a -> Ordering) -> StreamK m a -> StreamK m a+-- sortBy f = Stream.concatPairsWith (Stream.mergeBy f) Stream.fromPure+sortBy cmp =+    let p =+            Parser.groupByRollingEither+                (\x -> (< GT) . cmp x)+                FL.toStreamKRev+                FL.toStreamK+     in   mergeMapWith (mergeBy cmp) id+        . Stream.toStreamK+        . Stream.catRights -- its a non-failing backtracking parser+        . Stream.parseMany (fmap (either id id) p)+        . Stream.fromStreamK
+ src/Streamly/Internal/Data/StreamK/Alt.hs view
@@ -0,0 +1,244 @@+-- |+-- Module      : Streamly.StreamDK.Type+-- Copyright   : (c) 2019 Composewell Technologies+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+-- A CPS style stream using a constructor based representation instead of a+-- function based representation.+--+-- Streamly internally uses two fundamental stream representations, (1) streams+-- with an open or arbitrary control flow (we call it StreamK), (2) streams+-- with a structured or closed loop control flow (we call it StreamD). The+-- higher level stream types can use any of these representations under the+-- hood and can interconvert between the two.+--+-- StreamD:+--+-- StreamD is a non-recursive data type in which the state of the stream and+-- the step function are separate. When the step function is called, a stream+-- element and the new stream state is yielded. The generated element and the+-- state are passed to the next consumer in the loop. The state is threaded+-- around in the loop until control returns back to the original step function+-- to run the next step. This creates a structured closed loop representation+-- (like "for" loops in C) with state of each step being hidden/abstracted or+-- existential within that step. This creates a loop representation identical+-- to the "for" or "while" loop constructs in imperative languages, the states+-- of the steps combined together constitute the state of the loop iteration.+--+-- Internally most combinators use a closed loop representation because it+-- provides very high efficiency due to stream fusion. The performance of this+-- representation is competitive to the C language implementations.+--+-- Pros and Cons of StreamD:+--+-- 1) stream-fusion: This representation can be optimized very efficiently by+-- the compiler because the state is explicitly separated from step functions,+-- represented using pure data constructors and visible to the compiler, the+-- stream steps can be fused using case-of-case transformations and the state+-- can be specialized using spec-constructor optimization, yielding a C like+-- tight loop/state machine with no constructors, the state is used unboxed and+-- therefore no unnecessary allocation.+--+-- 2) Because of a closed representation consing too many elements in this type+-- of stream does not scale, it will have quadratic performance slowdown. Each+-- cons creates a layer that needs to return the control back to the caller.+-- Another implementation of cons is possible but that will have to box/unbox+-- the state and will not fuse. So effectively cons breaks fusion.+--+-- 3) unconsing an item from the stream breaks fusion, we have to "pause" the+-- loop, rebox and save the state.+--+-- 3) Exception handling is easy to implement in this model because control+-- flow is structured in the loop and cannot be arbitrary. Therefore,+-- implementing "bracket" is natural.+--+-- 4) Round-robin scheduling for co-operative multitasking is easy to implement.+--+-- 5) It fuses well with the direct style Fold implementation.+--+-- StreamK/StreamDK:+--+-- StreamDK i.e. the stream defined in this module, like StreamK, is a+-- recursive data type which has no explicit state defined using constructors,+-- each step yields an element and a computation representing the rest of the+-- stream.  Stream state is part of the function representing the rest of the+-- stream.  This creates an open computation representation, or essentially a+-- continuation passing style computation.  After the stream step is executed,+-- the caller is free to consume the produced element and then send the control+-- wherever it wants, there is no restriction on the control to return back+-- somewhere, the control is free to go anywhere. The caller may decide not to+-- consume the rest of the stream. This representation is more like a "goto"+-- based implementation in imperative languages.+--+-- Pros and Cons of StreamK:+--+-- 1) The way StreamD can be optimized using stream-fusion, this type can be+-- optimized using foldr/build fusion. However, foldr/build has not yet been+-- fully implemented for StreamK/StreamDK.+--+-- 2) Using cons is natural in this representation, unlike in StreamD it does+-- not have a quadratic slowdown. Currently, we in fact wrap StreamD in StreamK+-- to support a better cons operation.+--+-- 3) Similarly, uncons is natural in this representation.+--+-- 4) Exception handling is not easy to implement because of the "goto" nature+-- of CPS.+--+-- 5) Composable folds are not implemented/proven, however, intuition says that+-- a push style CPS representation should be able to be used along with StreamK+-- to efficiently implement composable folds.++module Streamly.Internal.Data.StreamK.Alt+    (+    -- * Stream Type++      Stream+    , Step (..)++    -- * Construction+    , nil+    , cons+    , consM+    , unfoldr+    , unfoldrM+    , replicateM++    -- * Folding+    , uncons+    , foldrS++    -- * Specific Folds+    , drain+    )+where++#include "inline.hs"++-- XXX Use Cons and Nil instead of Yield and Stop?+data Step m a = Yield a (Stream m a) | Stop++newtype Stream m a = Stream (m (Step m a))++-------------------------------------------------------------------------------+-- Construction+-------------------------------------------------------------------------------++nil :: Monad m => Stream m a+nil = Stream $ return Stop++{-# INLINE_NORMAL cons #-}+cons :: Monad m => a -> Stream m a -> Stream m a+cons x xs = Stream $ return $ Yield x xs++consM :: Monad m => m a -> Stream m a -> Stream m a+consM eff xs = Stream $ eff >>= \x -> return $ Yield x xs++unfoldrM :: Monad m => (s -> m (Maybe (a, s))) -> s -> Stream m a+unfoldrM next state = Stream (step' state)+  where+    step' st = do+        r <- next st+        return $ case r of+            Just (x, s) -> Yield x (Stream (step' s))+            Nothing     -> Stop+{-+unfoldrM next s0 = buildM $ \yld stp ->+    let go s = do+            r <- next s+            case r of+                Just (a, b) -> yld a (go b)+                Nothing -> stp+    in go s0+-}++{-# INLINE unfoldr #-}+unfoldr :: Monad m => (b -> Maybe (a, b)) -> b -> Stream m a+unfoldr next s0 = build $ \yld stp ->+    let go s =+            case next s of+                Just (a, b) -> yld a (go b)+                Nothing -> stp+    in go s0++replicateM :: Monad m => Int -> a -> Stream m a+replicateM n x = Stream (step n)+    where+    step i = return $+        if i <= 0+        then Stop+        else Yield x (Stream (step (i - 1)))++-------------------------------------------------------------------------------+-- Folding+-------------------------------------------------------------------------------++uncons :: Monad m => Stream m a -> m (Maybe (a, Stream m a))+uncons (Stream step) = do+    r <- step+    return $ case r of+        Yield x xs -> Just (x, xs)+        Stop -> Nothing++-- | Lazy right associative fold to a stream.+{-# INLINE_NORMAL foldrS #-}+foldrS :: Monad m+       => (a -> Stream m b -> Stream m b)+       -> Stream m b+       -> Stream m a+       -> Stream m b+foldrS f streamb = go+    where+    go (Stream stepa) = Stream $ do+        r <- stepa+        case r of+            Yield x xs -> let Stream step = f x (go xs) in step+            Stop -> let Stream step = streamb in step++{-# INLINE_LATE foldrM #-}+foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b+foldrM fstep acc = go+    where+    go (Stream step) = do+        r <- step+        case r of+            Yield x xs -> fstep x (go xs)+            Stop -> acc++{-# INLINE_NORMAL build #-}+build :: Monad m+    => forall a. (forall b. (a -> b -> b) -> b -> b) -> Stream m a+build g = g cons nil++{-# RULES+"foldrM/build"  forall k z (g :: forall b. (a -> b -> b) -> b -> b).+                foldrM k z (build g) = g k z #-}++{-+-- To fuse foldrM with unfoldrM we need the type m1 to be polymorphic such that+-- it is either Monad m or Stream m.  So that we can use cons/nil as well as+-- monadic construction function as its arguments.+--+{-# INLINE_NORMAL buildM #-}+buildM :: Monad m+    => forall a. (forall b. (a -> m1 b -> m1 b) -> m1 b -> m1 b) -> Stream m a+buildM g = g cons nil+-}++-------------------------------------------------------------------------------+-- Specific folds+-------------------------------------------------------------------------------++{-# INLINE drain #-}+drain :: Monad m => Stream m a -> m ()+drain = foldrM (\_ xs -> xs) (return ())+{-+drain (Stream step) = do+    r <- step+    case r of+        Yield _ next -> drain next+        Stop      -> return ()+        -}
+ src/Streamly/Internal/Data/StreamK/Transformer.hs view
@@ -0,0 +1,79 @@+-- |+-- Module      : Streamly.Internal.Data.StreamK.Transformer+-- Copyright   : (c) 2017 Composewell Technologies+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.StreamK.Transformer+    (+      foldlT+    , foldrT++    , liftInner+    , evalStateT+    )+where++import Control.Monad.Trans.Class (MonadTrans(lift))+import Control.Monad.Trans.State.Strict (StateT)+import Streamly.Internal.Data.StreamK.Type+    (StreamK, nil, cons, uncons, concatEffect)++import qualified Control.Monad.Trans.State.Strict as State++-- | Lazy left fold to an arbitrary transformer monad.+{-# INLINE foldlT #-}+foldlT :: (Monad m, Monad (s m), MonadTrans s)+    => (s m b -> a -> s m b) -> s m b -> StreamK m a -> s m b+foldlT step = go+  where+    go acc m1 = do+        res <- lift $ uncons m1+        case res of+            Just (h, t) -> go (step acc h) t+            Nothing -> acc++-- | Right associative fold to an arbitrary transformer monad.+{-# INLINE foldrT #-}+foldrT :: (Monad m, Monad (s m), MonadTrans s)+    => (a -> s m b -> s m b) -> s m b -> StreamK m a -> s m b+foldrT step final = go+  where+    go m1 = do+        res <- lift $ uncons m1+        case res of+            Just (h, t) -> step h (go t)+            Nothing -> final++------------------------------------------------------------------------------+-- Lifting inner monad+------------------------------------------------------------------------------++{-# INLINE evalStateT #-}+evalStateT :: Monad m => m s -> StreamK (StateT s m) a -> StreamK m a+evalStateT = go++    where++    go st m1 = concatEffect $ fmap f (st >>= State.runStateT (uncons m1))++    f (res, s1) =+        case res of+            Just (h, t) -> cons h (go (return s1) t)+            Nothing -> nil++{-# INLINE liftInner #-}+liftInner :: (Monad m, MonadTrans t, Monad (t m)) =>+    StreamK m a -> StreamK (t m) a+liftInner = go++    where++    go m1 = concatEffect $ fmap f $ lift $ uncons m1++    f res =+        case res of+            Just (h, t) -> cons h (go t)+            Nothing -> nil
+ src/Streamly/Internal/Data/StreamK/Type.hs view
@@ -0,0 +1,2084 @@+{-# LANGUAGE CPP #-}+{-# LANGUAGE UndecidableInstances #-}+-- |+-- Module      : Streamly.Internal.Data.StreamK.Type+-- Copyright   : (c) 2017 Composewell Technologies+--+-- License     : BSD3+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+--+-- Continuation passing style (CPS) stream implementation. The symbol 'K' below+-- denotes a function as well as a Kontinuation.+--+module Streamly.Internal.Data.StreamK.Type+    (+    -- * StreamK type+      Stream+    , StreamK (..)++    -- * CrossStreamK type wrapper+    , CrossStreamK+    , unCross+    , mkCross++    -- * foldr/build Fusion+    , mkStream+    , foldStream+    , foldStreamShared+    , foldrM+    , foldrS+    , foldrSShared+    , foldrSM+    , build+    , buildS+    , buildM+    , buildSM+    , augmentS+    , augmentSM+    , unShare++    -- * Construction+    -- ** Primitives+    , fromStopK+    , fromYieldK+    , consK+    , cons+    , (.:)+    , consM+    , consMBy+    , nil+    , nilM++    -- ** Unfolding+    , unfoldr+    , unfoldrMWith+    , unfoldrM++    -- ** From Values+    , fromEffect+    , fromPure+    , repeat+    , repeatMWith+    , replicateMWith++    -- ** From Indices+    , fromIndicesMWith++    -- ** Iteration+    , iterateMWith++    -- ** From Containers+    , fromFoldable+    , fromFoldableM++    -- ** Cyclic+    , mfix++    -- * Elimination+    -- ** Primitives+    , uncons++    -- ** Strict Left Folds+    , Streamly.Internal.Data.StreamK.Type.foldl'+    , foldlx'++    -- ** Lazy Right Folds+    , Streamly.Internal.Data.StreamK.Type.foldr++    -- ** Specific Folds+    , drain+    , null+    , tail+    , init++    -- * Mapping+    , map+    , mapMWith+    , mapMSerial++    -- * Combining Two Streams+    -- ** Appending+    , conjoin+    , append++    -- ** Interleave+    , interleave+    , interleaveFst+    , interleaveMin++    -- ** Cross Product+    , crossApplyWith+    , crossApply+    , crossApplySnd+    , crossApplyFst+    , crossWith+    , cross++    -- * Concat+    , before+    , concatEffect+    , concatMapEffect+    , concatMapWith+    , concatMap+    , bindWith+    , concatIterateWith+    , concatIterateLeftsWith+    , concatIterateScanWith++    -- * Merge+    , mergeMapWith+    , mergeIterateWith++    -- * Buffered Operations+    , foldlS+    , reverse+    )+where++#include "inline.hs"++-- import Control.Applicative (liftA2)+import Control.Monad ((>=>))+import Control.Monad.Catch (MonadThrow, throwM)+import Control.Monad.Trans.Class (MonadTrans(lift))+#if !MIN_VERSION_base(4,18,0)+import Control.Applicative (liftA2)+#endif+import Control.Monad.IO.Class (MonadIO(..))+import Data.Foldable (Foldable(foldl'), fold, foldr)+import Data.Function (fix)+import Data.Functor.Identity (Identity(..))+import Data.Maybe (fromMaybe)+import Data.Semigroup (Endo(..))+import GHC.Exts (IsList(..), IsString(..), oneShot)+import Streamly.Internal.BaseCompat ((#.))+import Streamly.Internal.Data.Maybe.Strict (Maybe'(..), toMaybe)+import Streamly.Internal.Data.SVar.Type (State, adaptState, defState)+import Text.Read+       ( Lexeme(Ident), lexP, parens, prec, readPrec, readListPrec+       , readListPrecDefault)++import qualified Prelude++import Prelude hiding+    (map, mapM, concatMap, foldr, repeat, null, reverse, tail, init)++#include "DocTestDataStreamK.hs"++------------------------------------------------------------------------------+-- Basic stream type+------------------------------------------------------------------------------++-- It uses stop, singleton and yield continuations equivalent to the following+-- direct style type:+--+-- @+-- data StreamK m a = Stop | Singleton a | Yield a (StreamK m a)+-- @+--+-- To facilitate parallel composition we maintain a local state in an 'SVar'+-- that is shared across and is used for synchronization of the streams being+-- composed.+--+-- The singleton case can be expressed in terms of stop and yield but we have+-- it as a separate case to optimize composition operations for streams with+-- single element.  We build singleton streams in the implementation of 'pure'+-- for Applicative and Monad, and in 'lift' for MonadTrans.++-- XXX remove the State param.++-- | Continuation Passing Style (CPS) version of "Streamly.Data.Stream.Stream".+-- Unlike "Streamly.Data.Stream.Stream", 'StreamK' can be composed recursively+-- without affecting performance.+--+-- Semigroup instance appends two streams:+--+-- >>> (<>) = Stream.append+--+{-# DEPRECATED Stream "Please use StreamK instead." #-}+type Stream = StreamK++newtype StreamK m a =+    MkStream (forall r.+               State StreamK m a         -- state+            -> (a -> StreamK m a -> m r) -- yield+            -> (a -> m r)               -- singleton+            -> m r                      -- stop+            -> m r+            )++mkStream+    :: (forall r. State StreamK m a+        -> (a -> StreamK m a -> m r)+        -> (a -> m r)+        -> m r+        -> m r)+    -> StreamK m a+mkStream = MkStream++-- | A terminal function that has no continuation to follow.+type StopK m = forall r. m r -> m r++-- | A monadic continuation, it is a function that yields a value of type "a"+-- and calls the argument (a -> m r) as a continuation with that value. We can+-- also think of it as a callback with a handler (a -> m r).  Category+-- theorists call it a codensity type, a special type of right kan extension.+type YieldK m a = forall r. (a -> m r) -> m r++_wrapM :: Monad m => m a -> YieldK m a+_wrapM m = (m >>=)++-- | Make an empty stream from a stop function.+fromStopK :: StopK m -> StreamK m a+fromStopK k = mkStream $ \_ _ _ stp -> k stp++-- | Make a singleton stream from a callback function. The callback function+-- calls the one-shot yield continuation to yield an element.+fromYieldK :: YieldK m a -> StreamK m a+fromYieldK k = mkStream $ \_ _ sng _ -> k sng++-- | Add a yield function at the head of the stream.+consK :: YieldK m a -> StreamK m a -> StreamK m a+consK k r = mkStream $ \_ yld _ _ -> k (`yld` r)++-- XXX Build a stream from a repeating callback function.++------------------------------------------------------------------------------+-- Construction+------------------------------------------------------------------------------++infixr 5 `cons`++-- faster than consM because there is no bind.++-- | A right associative prepend operation to add a pure value at the head of+-- an existing stream::+--+-- >>> s = 1 `StreamK.cons` 2 `StreamK.cons` 3 `StreamK.cons` StreamK.nil+-- >>> Stream.fold Fold.toList (StreamK.toStream s)+-- [1,2,3]+--+-- It can be used efficiently with 'Prelude.foldr':+--+-- >>> fromFoldable = Prelude.foldr StreamK.cons StreamK.nil+--+-- Same as the following but more efficient:+--+-- >>> cons x xs = return x `StreamK.consM` xs+--+{-# INLINE_NORMAL cons #-}+cons :: a -> StreamK m a -> StreamK m a+cons a r = mkStream $ \_ yield _ _ -> yield a r++infixr 5 .:++-- | Operator equivalent of 'cons'.+--+-- @+-- > toList $ 1 .: 2 .: 3 .: nil+-- [1,2,3]+-- @+--+{-# INLINE (.:) #-}+(.:) :: a -> StreamK m a -> StreamK m a+(.:) = cons++-- | A stream that terminates without producing any output or side effect.+--+-- >>> Stream.fold Fold.toList (StreamK.toStream StreamK.nil)+-- []+--+{-# INLINE_NORMAL nil #-}+nil :: StreamK m a+nil = mkStream $ \_ _ _ stp -> stp++-- | A stream that terminates without producing any output, but produces a side+-- effect.+--+-- >>> Stream.fold Fold.toList (StreamK.toStream (StreamK.nilM (print "nil")))+-- "nil"+-- []+--+-- /Pre-release/+{-# INLINE_NORMAL nilM #-}+nilM :: Applicative m => m b -> StreamK m a+nilM m = mkStream $ \_ _ _ stp -> m *> stp++-- Create a singleton stream from a pure value.+--+-- >>> fromPure a = a `StreamK.cons` StreamK.nil+-- >>> fromPure = pure+-- >>> fromPure = StreamK.fromEffect . pure+--+{-# INLINE_NORMAL fromPure #-}+fromPure :: a -> StreamK m a+fromPure a = mkStream $ \_ _ single _ -> single a++-- Create a singleton stream from a monadic action.+--+-- >>> fromEffect m = m `StreamK.consM` StreamK.nil+--+-- >>> Stream.fold Fold.drain $ StreamK.toStream $ StreamK.fromEffect (putStrLn "hello")+-- hello+--+{-# INLINE_NORMAL fromEffect #-}+fromEffect :: Monad m => m a -> StreamK m a+fromEffect m = mkStream $ \_ _ single _ -> m >>= single++infixr 5 `consM`++-- NOTE: specializing the function outside the instance definition seems to+-- improve performance quite a bit at times, even if we have the same+-- SPECIALIZE in the instance definition.++-- | A right associative prepend operation to add an effectful value at the+-- head of an existing stream::+--+-- >>> s = putStrLn "hello" `StreamK.consM` putStrLn "world" `StreamK.consM` StreamK.nil+-- >>> Stream.fold Fold.drain (StreamK.toStream s)+-- hello+-- world+--+-- It can be used efficiently with 'Prelude.foldr':+--+-- >>> fromFoldableM = Prelude.foldr StreamK.consM StreamK.nil+--+-- Same as the following but more efficient:+--+-- >>> consM x xs = StreamK.fromEffect x `StreamK.append` xs+--+{-# INLINE consM #-}+{-# SPECIALIZE consM :: IO a -> StreamK IO a -> StreamK IO a #-}+consM :: Monad m => m a -> StreamK m a -> StreamK m a+consM m r = MkStream $ \_ yld _ _ -> m >>= (`yld` r)++-- XXX specialize to IO?+{-# INLINE consMBy #-}+consMBy :: Monad m =>+    (StreamK m a -> StreamK m a -> StreamK m a) -> m a -> StreamK m a -> StreamK m a+consMBy f m r = fromEffect m `f` r++------------------------------------------------------------------------------+-- Folding a stream+------------------------------------------------------------------------------++-- | Fold a stream by providing an SVar, a stop continuation, a singleton+-- continuation and a yield continuation. The stream would share the current+-- SVar passed via the State.+{-# INLINE_EARLY foldStreamShared #-}+foldStreamShared+    :: State StreamK m a+    -> (a -> StreamK m a -> m r)+    -> (a -> m r)+    -> m r+    -> StreamK m a+    -> m r+foldStreamShared s yield single stop (MkStream k) = k s yield single stop++-- | Fold a stream by providing a State, stop continuation, a singleton+-- continuation and a yield continuation. The stream will not use the SVar+-- passed via State.+{-# INLINE foldStream #-}+foldStream+    :: State StreamK m a+    -> (a -> StreamK m a -> m r)+    -> (a -> m r)+    -> m r+    -> StreamK m a+    -> m r+foldStream s yield single stop (MkStream k) =+    k (adaptState s) yield single stop++-------------------------------------------------------------------------------+-- foldr/build fusion+-------------------------------------------------------------------------------++-- XXX perhaps we can just use foldrSM/buildM everywhere as they are more+-- general and cover foldrS/buildS as well.++-- | The function 'f' decides how to reconstruct the stream. We could+-- reconstruct using a shared state (SVar) or without sharing the state.+--+{-# INLINE foldrSWith #-}+foldrSWith ::+    (forall r. State StreamK m b+        -> (b -> StreamK m b -> m r)+        -> (b -> m r)+        -> m r+        -> StreamK m b+        -> m r)+    -> (a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrSWith f step final m = go m+    where+    go m1 = mkStream $ \st yld sng stp ->+        let run x = f st yld sng stp x+            stop = run final+            single a = run $ step a final+            yieldk a r = run $ step a (go r)+         -- XXX if type a and b are the same we do not need adaptState, can we+         -- save some perf with that?+         -- XXX since we are using adaptState anyway here we can use+         -- foldStreamShared instead, will that save some perf?+         in foldStream (adaptState st) yieldk single stop m1++-- XXX we can use rewrite rules just for foldrSWith, if the function f is the+-- same we can rewrite it.++-- | Fold sharing the SVar state within the reconstructed stream+{-# INLINE_NORMAL foldrSShared #-}+foldrSShared ::+       (a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrSShared = foldrSWith foldStreamShared++-- XXX consM is a typeclass method, therefore rewritten already. Instead maybe+-- we can make consM polymorphic using rewrite rules.+-- {-# RULES "foldrSShared/id"     foldrSShared consM nil = \x -> x #-}+{-# RULES "foldrSShared/nil"+    forall k z. foldrSShared k z nil = z #-}+{-# RULES "foldrSShared/single"+    forall k z x. foldrSShared k z (fromPure x) = k x z #-}+-- {-# RULES "foldrSShared/app" [1]+--     forall ys. foldrSShared consM ys = \xs -> xs `conjoin` ys #-}++-- | Right fold to a streaming monad.+--+-- > foldrS StreamK.cons StreamK.nil === id+--+-- 'foldrS' can be used to perform stateless stream to stream transformations+-- like map and filter in general. It can be coupled with a scan to perform+-- stateful transformations. However, note that the custom map and filter+-- routines can be much more efficient than this due to better stream fusion.+--+-- >>> input = StreamK.fromStream $ Stream.fromList [1..5]+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS StreamK.cons StreamK.nil input+-- [1,2,3,4,5]+--+-- Find if any element in the stream is 'True':+--+-- >>> step x xs = if odd x then StreamK.fromPure True else xs+-- >>> input = StreamK.fromStream (Stream.fromList (2:4:5:undefined)) :: StreamK IO Int+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step (StreamK.fromPure False) input+-- [True]+--+-- Map (+2) on odd elements and filter out the even elements:+--+-- >>> step x xs = if odd x then (x + 2) `StreamK.cons` xs else xs+-- >>> input = StreamK.fromStream (Stream.fromList [1..5]) :: StreamK IO Int+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.foldrS step StreamK.nil input+-- [3,5,7]+--+-- /Pre-release/+{-# INLINE_NORMAL foldrS #-}+foldrS ::+       (a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrS = foldrSWith foldStream++{-# RULES "foldrS/id"     foldrS cons nil = \x -> x #-}+{-# RULES "foldrS/nil"    forall k z.   foldrS k z nil  = z #-}+-- See notes in GHC.Base about this rule+-- {-# RULES "foldr/cons"+--  forall k z x xs. foldrS k z (x `cons` xs) = k x (foldrS k z xs) #-}+{-# RULES "foldrS/single" forall k z x. foldrS k z (fromPure x) = k x z #-}+-- {-# RULES "foldrS/app" [1]+--  forall ys. foldrS cons ys = \xs -> xs `conjoin` ys #-}++-------------------------------------------------------------------------------+-- foldrS with monadic cons i.e. consM+-------------------------------------------------------------------------------++{-# INLINE foldrSMWith #-}+foldrSMWith :: Monad m+    => (forall r. State StreamK m b+        -> (b -> StreamK m b -> m r)+        -> (b -> m r)+        -> m r+        -> StreamK m b+        -> m r)+    -> (m a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrSMWith f step final m = go m+    where+    go m1 = mkStream $ \st yld sng stp ->+        let run x = f st yld sng stp x+            stop = run final+            single a = run $ step (return a) final+            yieldk a r = run $ step (return a) (go r)+         in foldStream (adaptState st) yieldk single stop m1++{-# INLINE_NORMAL foldrSM #-}+foldrSM :: Monad m+    => (m a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrSM = foldrSMWith foldStream++-- {-# RULES "foldrSM/id"     foldrSM consM nil = \x -> x #-}+{-# RULES "foldrSM/nil"    forall k z.   foldrSM k z nil  = z #-}+{-# RULES "foldrSM/single" forall k z x. foldrSM k z (fromEffect x) = k x z #-}+-- {-# RULES "foldrSM/app" [1]+--  forall ys. foldrSM consM ys = \xs -> xs `conjoin` ys #-}++-- Like foldrSM but sharing the SVar state within the recostructed stream.+{-# INLINE_NORMAL foldrSMShared #-}+foldrSMShared :: Monad m+    => (m a -> StreamK m b -> StreamK m b)+    -> StreamK m b+    -> StreamK m a+    -> StreamK m b+foldrSMShared = foldrSMWith foldStreamShared++-- {-# RULES "foldrSM/id"     foldrSM consM nil = \x -> x #-}+{-# RULES "foldrSMShared/nil"+    forall k z. foldrSMShared k z nil = z #-}+{-# RULES "foldrSMShared/single"+    forall k z x. foldrSMShared k z (fromEffect x) = k x z #-}+-- {-# RULES "foldrSM/app" [1]+--  forall ys. foldrSM consM ys = \xs -> xs `conjoin` ys #-}++-------------------------------------------------------------------------------+-- build+-------------------------------------------------------------------------------++{-# INLINE_NORMAL build #-}+build :: forall m a. (forall b. (a -> b -> b) -> b -> b) -> StreamK m a+build g = g cons nil++{-# RULES "foldrM/build"+    forall k z (g :: forall b. (a -> b -> b) -> b -> b).+    foldrM k z (build g) = g k z #-}++{-# RULES "foldrS/build"+      forall k z (g :: forall b. (a -> b -> b) -> b -> b).+      foldrS k z (build g) = g k z #-}++{-# RULES "foldrS/cons/build"+      forall k z x (g :: forall b. (a -> b -> b) -> b -> b).+      foldrS k z (x `cons` build g) = k x (g k z) #-}++{-# RULES "foldrSShared/build"+      forall k z (g :: forall b. (a -> b -> b) -> b -> b).+      foldrSShared k z (build g) = g k z #-}++{-# RULES "foldrSShared/cons/build"+      forall k z x (g :: forall b. (a -> b -> b) -> b -> b).+      foldrSShared k z (x `cons` build g) = k x (g k z) #-}++-- build a stream by applying cons and nil to a build function+{-# INLINE_NORMAL buildS #-}+buildS ::+       ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+    -> StreamK m a+buildS g = g cons nil++{-# RULES "foldrS/buildS"+      forall k z+        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+      foldrS k z (buildS g) = g k z #-}++{-# RULES "foldrS/cons/buildS"+      forall k z x+        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+      foldrS k z (x `cons` buildS g) = k x (g k z) #-}++{-# RULES "foldrSShared/buildS"+      forall k z+        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+      foldrSShared k z (buildS g) = g k z #-}++{-# RULES "foldrSShared/cons/buildS"+      forall k z x+        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+      foldrSShared k z (x `cons` buildS g) = k x (g k z) #-}++-- build a stream by applying consM and nil to a build function+{-# INLINE_NORMAL buildSM #-}+buildSM :: Monad m+    => ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+    -> StreamK m a+buildSM g = g consM nil++{-# RULES "foldrSM/buildSM"+     forall k z+        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+     foldrSM k z (buildSM g) = g k z #-}++{-# RULES "foldrSMShared/buildSM"+     forall k z+        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+     foldrSMShared k z (buildSM g) = g k z #-}++-- Disabled because this may not fire as consM is a class Op+{-+{-# RULES "foldrS/consM/buildSM"+      forall k z x (g :: (m a -> t m a -> t m a) -> t m a -> t m a)+    . foldrSM k z (x `consM` buildSM g)+    = k x (g k z)+#-}+-}++-- Build using monadic build functions (continuations) instead of+-- reconstructing a stream.+{-# INLINE_NORMAL buildM #-}+buildM :: Monad m+    => (forall r. (a -> StreamK m a -> m r)+        -> (a -> m r)+        -> m r+        -> m r+       )+    -> StreamK m a+buildM g = mkStream $ \st yld sng stp ->+    g (\a r -> foldStream st yld sng stp (return a `consM` r)) sng stp++-- | Like 'buildM' but shares the SVar state across computations.+{-# INLINE_NORMAL sharedMWith #-}+sharedMWith :: Monad m+    => (m a -> StreamK m a -> StreamK m a)+    -> (forall r. (a -> StreamK m a -> m r)+        -> (a -> m r)+        -> m r+        -> m r+       )+    -> StreamK m a+sharedMWith cns g = mkStream $ \st yld sng stp ->+    g (\a r -> foldStreamShared st yld sng stp (return a `cns` r)) sng stp++-------------------------------------------------------------------------------+-- augment+-------------------------------------------------------------------------------++{-# INLINE_NORMAL augmentS #-}+augmentS ::+       ((a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+    -> StreamK m a+    -> StreamK m a+augmentS g xs = g cons xs++{-# RULES "augmentS/nil"+    forall (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    augmentS g nil = buildS g+    #-}++{-# RULES "foldrS/augmentS"+    forall k z xs+        (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    foldrS k z (augmentS g xs) = g k (foldrS k z xs)+    #-}++{-# RULES "augmentS/buildS"+    forall (g :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+           (h :: (a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    augmentS g (buildS h) = buildS (\c n -> g c (h c n))+    #-}++{-# INLINE_NORMAL augmentSM #-}+augmentSM :: Monad m =>+       ((m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+    -> StreamK m a -> StreamK m a+augmentSM g xs = g consM xs++{-# RULES "augmentSM/nil"+    forall+        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    augmentSM g nil = buildSM g+    #-}++{-# RULES "foldrSM/augmentSM"+    forall k z xs+        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    foldrSM k z (augmentSM g xs) = g k (foldrSM k z xs)+    #-}++{-# RULES "augmentSM/buildSM"+    forall+        (g :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a)+        (h :: (m a -> StreamK m a -> StreamK m a) -> StreamK m a -> StreamK m a).+    augmentSM g (buildSM h) = buildSM (\c n -> g c (h c n))+    #-}++-------------------------------------------------------------------------------+-- Experimental foldrM/buildM+-------------------------------------------------------------------------------++-- | Lazy right fold with a monadic step function.+{-# INLINE_NORMAL foldrM #-}+foldrM :: (a -> m b -> m b) -> m b -> StreamK m a -> m b+foldrM step acc m = go m+    where+    go m1 =+        let stop = acc+            single a = step a acc+            yieldk a r = step a (go r)+        in foldStream defState yieldk single stop m1++{-# INLINE_NORMAL foldrMKWith #-}+foldrMKWith+    :: (State StreamK m a+        -> (a -> StreamK m a -> m b)+        -> (a -> m b)+        -> m b+        -> StreamK m a+        -> m b)+    -> (a -> m b -> m b)+    -> m b+    -> ((a -> StreamK m a -> m b) -> (a -> m b) -> m b -> m b)+    -> m b+foldrMKWith f step acc = go+    where+    go k =+        let stop = acc+            single a = step a acc+            yieldk a r = step a (go (\yld sng stp -> f defState yld sng stp r))+        in k yieldk single stop++{-+{-# RULES "foldrM/buildS"+      forall k z (g :: (a -> t m a -> t m a) -> t m a -> t m a)+    . foldrM k z (buildS g)+    = g k z+#-}+-}+-- XXX in which case will foldrM/buildM fusion be useful?+{-# RULES "foldrM/buildM"+    forall step acc (g :: (forall r.+           (a -> StreamK m a -> m r)+        -> (a -> m r)+        -> m r+        -> m r+       )).+    foldrM step acc (buildM g) = foldrMKWith foldStream step acc g+    #-}++{-+{-# RULES "foldrM/sharedM"+    forall step acc (g :: (forall r.+           (a -> StreamK m a -> m r)+        -> (a -> m r)+        -> m r+        -> m r+       )).+    foldrM step acc (sharedM g) = foldrMKWith foldStreamShared step acc g+    #-}+-}++------------------------------------------------------------------------------+-- Left fold+------------------------------------------------------------------------------++-- | Strict left fold with an extraction function. Like the standard strict+-- left fold, but applies a user supplied extraction function (the third+-- argument) to the folded value at the end. This is designed to work with the+-- @foldl@ library. The suffix @x@ is a mnemonic for extraction.+--+-- Note that the accumulator is always evaluated including the initial value.+{-# INLINE foldlx' #-}+foldlx' :: forall m a b x. Monad m+    => (x -> a -> x) -> x -> (x -> b) -> StreamK m a -> m b+foldlx' step begin done m = get $ go m begin+    where+    {-# NOINLINE get #-}+    get :: StreamK m x -> m b+    get m1 =+        -- XXX we are not strictly evaluating the accumulator here. Is this+        -- okay?+        let single = return . done+        -- XXX this is foldSingleton. why foldStreamShared?+         in foldStreamShared undefined undefined single undefined m1++    -- Note, this can be implemented by making a recursive call to "go",+    -- however that is more expensive because of unnecessary recursion+    -- that cannot be tail call optimized. Unfolding recursion explicitly via+    -- continuations is much more efficient.+    go :: StreamK m a -> x -> StreamK m x+    go m1 !acc = mkStream $ \_ yld sng _ ->+        let stop = sng acc+            single a = sng $ step acc a+            -- XXX this is foldNonEmptyStream+            yieldk a r = foldStream defState yld sng undefined $+                go r (step acc a)+        in foldStream defState yieldk single stop m1++-- | Strict left associative fold.+{-# INLINE foldl' #-}+foldl' :: Monad m => (b -> a -> b) -> b -> StreamK m a -> m b+foldl' step begin = foldlx' step begin id++------------------------------------------------------------------------------+-- Specialized folds+------------------------------------------------------------------------------++-- XXX use foldrM to implement folds where possible+-- XXX This (commented) definition of drain and mapM_ perform much better on+-- some benchmarks but worse on others. Need to investigate why, may there is+-- an optimization opportunity that we can exploit.+-- drain = foldrM (\_ xs -> return () >> xs) (return ())++--+-- > drain = foldl' (\_ _ -> ()) ()+-- > drain = mapM_ (\_ -> return ())+{-# INLINE drain #-}+drain :: Monad m => StreamK m a -> m ()+drain = foldrM (\_ xs -> xs) (return ())+{-+drain = go+    where+    go m1 =+        let stop = return ()+            single _ = return ()+            yieldk _ r = go r+         in foldStream defState yieldk single stop m1+-}++{-# INLINE null #-}+null :: Monad m => StreamK m a -> m Bool+-- null = foldrM (\_ _ -> return True) (return False)+null m =+    let stop      = return True+        single _  = return False+        yieldk _ _ = return False+    in foldStream defState yieldk single stop m++------------------------------------------------------------------------------+-- Semigroup+------------------------------------------------------------------------------++infixr 6 `append`++{-# INLINE append #-}+append :: StreamK m a -> StreamK m a -> StreamK m a+-- XXX This doubles the time of toNullAp benchmark, may not be fusing properly+-- serial xs ys = augmentS (\c n -> foldrS c n xs) ys+append m1 m2 = go m1+    where+    go m = mkStream $ \st yld sng stp ->+               let stop       = foldStream st yld sng stp m2+                   single a   = yld a m2+                   yieldk a r = yld a (go r)+               in foldStream st yieldk single stop m++-- join/merge/append streams depending on consM+{-# INLINE conjoin #-}+conjoin :: Monad m => StreamK m a -> StreamK m a -> StreamK m a+conjoin xs = augmentSM (\c n -> foldrSM c n xs)++instance Semigroup (StreamK m a) where+    (<>) = append++------------------------------------------------------------------------------+-- Monoid+------------------------------------------------------------------------------++instance Monoid (StreamK m a) where+    mempty = nil+    mappend = (<>)++-------------------------------------------------------------------------------+-- Functor+-------------------------------------------------------------------------------++-- IMPORTANT: This is eta expanded on purpose. This should not be eta+-- reduced. This will cause a lot of regressions, probably because of some+-- rewrite rules. Ideally don't run hlint on this file.+{-# INLINE_LATE mapFB #-}+mapFB :: forall b m a.+       (b -> StreamK m b -> StreamK m b)+    -> (a -> b)+    -> a+    -> StreamK m b+    -> StreamK m b+mapFB c f = \x ys -> c (f x) ys++{-# RULES+"mapFB/mapFB" forall c f g. mapFB (mapFB c f) g = mapFB c (f . g)+"mapFB/id"    forall c.     mapFB c (\x -> x)   = c+    #-}++{-# INLINE map #-}+map :: (a -> b) -> StreamK m a -> StreamK m b+map f xs = buildS (\c n -> foldrS (mapFB c f) n xs)++-- XXX This definition might potentially be more efficient, but the cost in the+-- benchmark is dominated by unfoldrM cost so we cannot correctly determine+-- differences in the mapping cost. We should perhaps deduct the cost of+-- unfoldrM from the benchmarks and then compare.+{-+map f m = go m+    where+        go m1 =+            mkStream $ \st yld sng stp ->+            let single     = sng . f+                yieldk a r = yld (f a) (go r)+            in foldStream (adaptState st) yieldk single stp m1+-}++{-# INLINE_LATE mapMFB #-}+mapMFB :: Monad m => (m b -> t m b -> t m b) -> (a -> m b) -> m a -> t m b -> t m b+mapMFB c f x = c (x >>= f)++{-# RULES+    "mapMFB/mapMFB" forall c f g. mapMFB (mapMFB c f) g = mapMFB c (f >=> g)+    #-}+-- XXX These rules may never fire because pure/return type class rules will+-- fire first.+{-+"mapMFB/pure"    forall c.     mapMFB c (\x -> pure x)   = c+"mapMFB/return"  forall c.     mapMFB c (\x -> return x) = c+-}++-- This is experimental serial version supporting fusion.+--+-- XXX what if we do not want to fuse two concurrent mapMs?+-- XXX we can combine two concurrent mapM only if the SVar is of the same type+-- So for now we use it only for serial streams.+-- XXX fusion would be easier for monomoprhic stream types.+-- {-# RULES "mapM serial" mapM = mapMSerial #-}+{-# INLINE mapMSerial #-}+mapMSerial :: Monad m => (a -> m b) -> StreamK m a -> StreamK m b+mapMSerial f xs = buildSM (\c n -> foldrSMShared (mapMFB c f) n xs)++{-# INLINE mapMWith #-}+mapMWith ::+       (m b -> StreamK m b -> StreamK m b)+    -> (a -> m b)+    -> StreamK m a+    -> StreamK m b+mapMWith cns f = foldrSShared (\x xs -> f x `cns` xs) nil++{-+-- See note under map definition above.+mapMWith cns f = go+    where+    go m1 = mkStream $ \st yld sng stp ->+        let single a  = f a >>= sng+            yieldk a r = foldStreamShared st yld sng stp $ f a `cns` go r+         in foldStream (adaptState st) yieldk single stp m1+-}++-- XXX in fact use the Stream type everywhere and only use polymorphism in the+-- high level modules/prelude.+instance Monad m => Functor (StreamK m) where+    fmap = map++------------------------------------------------------------------------------+-- Lists+------------------------------------------------------------------------------++-- Serial streams can act like regular lists using the Identity monad++-- XXX Show instance is 10x slower compared to read, we can do much better.+-- The list show instance itself is really slow.++-- XXX The default definitions of "<" in the Ord instance etc. do not perform+-- well, because they do not get inlined. Need to add INLINE in Ord class in+-- base?++instance IsList (StreamK Identity a) where+    type (Item (StreamK Identity a)) = a++    {-# INLINE fromList #-}+    fromList = fromFoldable++    {-# INLINE toList #-}+    toList = Data.Foldable.foldr (:) []++-- XXX Fix these+{-+instance Eq a => Eq (StreamK Identity a) where+    {-# INLINE (==) #-}+    (==) xs ys = runIdentity $ eqBy (==) xs ys++instance Ord a => Ord (StreamK Identity a) where+    {-# INLINE compare #-}+    compare xs ys = runIdentity $ cmpBy compare xs ys++    {-# INLINE (<) #-}+    x < y =+        case compare x y of+            LT -> True+            _ -> False++    {-# INLINE (<=) #-}+    x <= y =+        case compare x y of+            GT -> False+            _ -> True++    {-# INLINE (>) #-}+    x > y =+        case compare x y of+            GT -> True+            _ -> False++    {-# INLINE (>=) #-}+    x >= y =+        case compare x y of+            LT -> False+            _ -> True++    {-# INLINE max #-}+    max x y = if x <= y then y else x++    {-# INLINE min #-}+    min x y = if x <= y then x else y+-}++instance Show a => Show (StreamK Identity a) where+    showsPrec p dl = showParen (p > 10) $+        showString "fromList " . shows (toList dl)++instance Read a => Read (StreamK Identity a) where+    readPrec = parens $ prec 10 $ do+        Ident "fromList" <- lexP+        fromList <$> readPrec++    readListPrec = readListPrecDefault++instance (a ~ Char) => IsString (StreamK Identity a) where+    {-# INLINE fromString #-}+    fromString = fromList++-------------------------------------------------------------------------------+-- Foldable+-------------------------------------------------------------------------------++-- | Lazy right associative fold.+{-# INLINE foldr #-}+foldr :: Monad m => (a -> b -> b) -> b -> StreamK m a -> m b+foldr step acc = foldrM (\x xs -> xs >>= \b -> return (step x b)) (return acc)++-- The default Foldable instance has several issues:+-- 1) several definitions do not have INLINE on them, so we provide+--    re-implementations with INLINE pragmas.+-- 2) the definitions of sum/product/maximum/minimum are inefficient as they+--    use right folds, they cannot run in constant memory. We provide+--    implementations using strict left folds here.++instance (Foldable m, Monad m) => Foldable (StreamK m) where++    {-# INLINE foldMap #-}+    foldMap f =+          fold+        . Streamly.Internal.Data.StreamK.Type.foldr (mappend . f) mempty++    {-# INLINE foldr #-}+    foldr f z t = appEndo (foldMap (Endo #. f) t) z++    {-# INLINE foldl' #-}+    foldl' f z0 xs = Data.Foldable.foldr f' id xs z0+        where f' x k = oneShot $ \z -> k $! f z x++    {-# INLINE length #-}+    length = Data.Foldable.foldl' (\n _ -> n + 1) 0++    {-# INLINE elem #-}+    elem = any . (==)++    {-# INLINE maximum #-}+    maximum =+          fromMaybe (errorWithoutStackTrace "maximum: empty stream")+        . toMaybe+        . Data.Foldable.foldl' getMax Nothing'++        where++        getMax Nothing' x = Just' x+        getMax (Just' mx) x = Just' $! max mx x++    {-# INLINE minimum #-}+    minimum =+          fromMaybe (errorWithoutStackTrace "minimum: empty stream")+        . toMaybe+        . Data.Foldable.foldl' getMin Nothing'++        where++        getMin Nothing' x = Just' x+        getMin (Just' mn) x = Just' $! min mn x++    {-# INLINE sum #-}+    sum = Data.Foldable.foldl' (+) 0++    {-# INLINE product #-}+    product = Data.Foldable.foldl' (*) 1++-------------------------------------------------------------------------------+-- Traversable+-------------------------------------------------------------------------------++instance Traversable (StreamK Identity) where+    {-# INLINE traverse #-}+    traverse f xs =+        runIdentity+            $ Streamly.Internal.Data.StreamK.Type.foldr+                consA (pure mempty) xs++        where++        consA x ys = liftA2 cons (f x) ys++-------------------------------------------------------------------------------+-- Nesting+-------------------------------------------------------------------------------++-- | Detach a stream from an SVar+{-# INLINE unShare #-}+unShare :: StreamK m a -> StreamK m a+unShare x = mkStream $ \st yld sng stp ->+    foldStream st yld sng stp x++-- XXX the function stream and value stream can run in parallel+{-# INLINE crossApplyWith #-}+crossApplyWith ::+       (StreamK m b -> StreamK m b -> StreamK m b)+    -> StreamK m (a -> b)+    -> StreamK m a+    -> StreamK m b+crossApplyWith par fstream stream = go1 fstream++    where++    go1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single f   = foldShared $ unShare (go2 f stream)+                yieldk f r = foldShared $ unShare (go2 f stream) `par` go1 r+            in foldStream (adaptState st) yieldk single stp m++    go2 f m =+        mkStream $ \st yld sng stp ->+            let single a   = sng (f a)+                yieldk a r = yld (f a) (go2 f r)+            in foldStream (adaptState st) yieldk single stp m++-- | Apply a stream of functions to a stream of values and flatten the results.+--+-- Note that the second stream is evaluated multiple times.+--+-- Definition:+--+-- >>> crossApply = StreamK.crossApplyWith StreamK.append+-- >>> crossApply = Stream.crossWith id+--+{-# INLINE crossApply #-}+crossApply ::+       StreamK m (a -> b)+    -> StreamK m a+    -> StreamK m b+crossApply fstream stream = go1 fstream++    where++    go1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single f   = foldShared $ go3 f stream+                yieldk f r = foldShared $ go2 f r stream+            in foldStream (adaptState st) yieldk single stp m++    go2 f r1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                stop = foldShared $ go1 r1+                single a   = yld (f a) (go1 r1)+                yieldk a r = yld (f a) (go2 f r1 r)+            in foldStream (adaptState st) yieldk single stop m++    go3 f m =+        mkStream $ \st yld sng stp ->+            let single a   = sng (f a)+                yieldk a r = yld (f a) (go3 f r)+            in foldStream (adaptState st) yieldk single stp m++{-# INLINE crossApplySnd #-}+crossApplySnd ::+       StreamK m a+    -> StreamK m b+    -> StreamK m b+crossApplySnd fstream stream = go1 fstream++    where++    go1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single _   = foldShared stream+                yieldk _ r = foldShared $ go2 r stream+            in foldStream (adaptState st) yieldk single stp m++    go2 r1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                stop = foldShared $ go1 r1+                single a   = yld a (go1 r1)+                yieldk a r = yld a (go2 r1 r)+            in foldStream st yieldk single stop m++{-# INLINE crossApplyFst #-}+crossApplyFst ::+       StreamK m a+    -> StreamK m b+    -> StreamK m a+crossApplyFst fstream stream = go1 fstream++    where++    go1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single f   = foldShared $ go3 f stream+                yieldk f r = foldShared $ go2 f r stream+            in foldStream st yieldk single stp m++    go2 f r1 m =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                stop = foldShared $ go1 r1+                single _   = yld f (go1 r1)+                yieldk _ r = yld f (go2 f r1 r)+            in foldStream (adaptState st) yieldk single stop m++    go3 f m =+        mkStream $ \st yld sng stp ->+            let single _   = sng f+                yieldk _ r = yld f (go3 f r)+            in foldStream (adaptState st) yieldk single stp m++-- |+-- Definition:+--+-- >>> crossWith f m1 m2 = fmap f m1 `StreamK.crossApply` m2+--+-- Note that the second stream is evaluated multiple times.+--+{-# INLINE crossWith #-}+crossWith :: Monad m => (a -> b -> c) -> StreamK m a -> StreamK m b -> StreamK m c+crossWith f m1 m2 = fmap f m1 `crossApply` m2++-- | Given a @StreamK m a@ and @StreamK m b@ generate a stream with all possible+-- combinations of the tuple @(a, b)@.+--+-- Definition:+--+-- >>> cross = StreamK.crossWith (,)+--+-- The second stream is evaluated multiple times. If that is not desired it can+-- be cached in an 'Data.Array.Array' and then generated from the array before+-- calling this function. Caching may also improve performance if the stream is+-- expensive to evaluate.+--+-- See 'Streamly.Internal.Data.Unfold.cross' for a much faster fused+-- alternative.+--+-- Time: O(m x n)+--+-- /Pre-release/+{-# INLINE cross #-}+cross :: Monad m => StreamK m a -> StreamK m b -> StreamK m (a, b)+cross = crossWith (,)++-- XXX This is just concatMapWith with arguments flipped. We need to keep this+-- instead of using a concatMap style definition because the bind+-- implementation in Async and WAsync streams show significant perf degradation+-- if the argument order is changed.+{-# INLINE bindWith #-}+bindWith ::+       (StreamK m b -> StreamK m b -> StreamK m b)+    -> StreamK m a+    -> (a -> StreamK m b)+    -> StreamK m b+bindWith par m1 f = go m1+    where+        go m =+            mkStream $ \st yld sng stp ->+                let foldShared = foldStreamShared st yld sng stp+                    single a   = foldShared $ unShare (f a)+                    yieldk a r = foldShared $ unShare (f a) `par` go r+                in foldStream (adaptState st) yieldk single stp m++-- XXX express in terms of foldrS?+-- XXX can we use a different stream type for the generated stream being+-- falttened so that we can combine them differently and keep the resulting+-- stream different?+-- XXX do we need specialize to IO?+-- XXX can we optimize when c and a are same, by removing the forall using+-- rewrite rules with type applications?++-- | Perform a 'concatMap' using a specified concat strategy. The first+-- argument specifies a merge or concat function that is used to merge the+-- streams generated by the map function.+--+{-# INLINE concatMapWith #-}+concatMapWith+    ::+       (StreamK m b -> StreamK m b -> StreamK m b)+    -> (a -> StreamK m b)+    -> StreamK m a+    -> StreamK m b+concatMapWith par f xs = bindWith par xs f++{-# INLINE concatMap #-}+concatMap :: (a -> StreamK m b) -> StreamK m a -> StreamK m b+concatMap = concatMapWith append++{-+-- Fused version.+-- XXX This fuses but when the stream is nil this performs poorly.+-- The filterAllOut benchmark degrades. Need to investigate and fix that.+{-# INLINE concatMap #-}+concatMap :: IsStream t => (a -> t m b) -> t m a -> t m b+concatMap f xs = buildS+    (\c n -> foldrS (\x b -> foldrS c b (f x)) n xs)++-- Stream polymorphic concatMap implementation+-- XXX need to use buildSM/foldrSMShared for parallel behavior+-- XXX unShare seems to degrade the fused performance+{-# INLINE_EARLY concatMap_ #-}+concatMap_ :: IsStream t => (a -> t m b) -> t m a -> t m b+concatMap_ f xs = buildS+     (\c n -> foldrSShared (\x b -> foldrSShared c b (unShare $ f x)) n xs)+-}++-- | Combine streams in pairs using a binary combinator, the resulting streams+-- are then combined again in pairs recursively until we get to a single+-- combined stream. The composition would thus form a binary tree.+--+-- For example, you can sort a stream using merge sort like this:+--+-- >>> s = StreamK.fromStream $ Stream.fromList [5,1,7,9,2]+-- >>> generate = StreamK.fromPure+-- >>> combine = StreamK.mergeBy compare+-- >>> Stream.fold Fold.toList $ StreamK.toStream $ StreamK.mergeMapWith combine generate s+-- [1,2,5,7,9]+--+-- Note that if the stream length is not a power of 2, the binary tree composed+-- by mergeMapWith would not be balanced, which may or may not be important+-- depending on what you are trying to achieve.+--+-- /Caution: the stream of streams must be finite/+--+-- /Pre-release/+--+{-# INLINE mergeMapWith #-}+mergeMapWith+    ::+       (StreamK m b -> StreamK m b -> StreamK m b)+    -> (a -> StreamK m b)+    -> StreamK m a+    -> StreamK m b+mergeMapWith combine f str = go (leafPairs str)++    where++    go stream =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single a   = foldShared $ unShare a+                yieldk a r = foldShared $ go1 a r+            in foldStream (adaptState st) yieldk single stp stream++    go1 a1 stream =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                stop = foldShared $ unShare a1+                single a = foldShared $ unShare a1 `combine` a+                yieldk a r =+                    foldShared $ go $ combine a1 a `cons` nonLeafPairs r+            in foldStream (adaptState st) yieldk single stop stream++    -- Exactly the same as "go" except that stop continuation extracts the+    -- stream.+    leafPairs stream =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single a   = sng (f a)+                yieldk a r = foldShared $ leafPairs1 a r+            in foldStream (adaptState st) yieldk single stp stream++    leafPairs1 a1 stream =+        mkStream $ \st yld sng _ ->+            let stop = sng (f a1)+                single a = sng (f a1 `combine` f a)+                yieldk a r = yld (f a1 `combine` f a) $ leafPairs r+            in foldStream (adaptState st) yieldk single stop stream++    -- Exactly the same as "leafPairs" except that it does not map "f"+    nonLeafPairs stream =+        mkStream $ \st yld sng stp ->+            let foldShared = foldStreamShared st yld sng stp+                single a   = sng a+                yieldk a r = foldShared $ nonLeafPairs1 a r+            in foldStream (adaptState st) yieldk single stp stream++    nonLeafPairs1 a1 stream =+        mkStream $ \st yld sng _ ->+            let stop = sng a1+                single a = sng (a1 `combine` a)+                yieldk a r = yld (a1 `combine` a) $ nonLeafPairs r+            in foldStream (adaptState st) yieldk single stop stream++{-+instance Monad m => Applicative (StreamK m) where+    {-# INLINE pure #-}+    pure = fromPure++    {-# INLINE (<*>) #-}+    (<*>) = crossApply++    {-# INLINE liftA2 #-}+    liftA2 f x = (<*>) (fmap f x)++    {-# INLINE (*>) #-}+    (*>) = crossApplySnd++    {-# INLINE (<*) #-}+    (<*) = crossApplyFst++-- NOTE: even though concatMap for StreamD is 3x faster compared to StreamK,+-- the monad instance of StreamD is slower than StreamK after foldr/build+-- fusion.+instance Monad m => Monad (StreamK m) where+    {-# INLINE return #-}+    return = pure++    {-# INLINE (>>=) #-}+    (>>=) = flip concatMap+-}++{-+-- Like concatMap but generates stream using an unfold function. Similar to+-- unfoldMany but for StreamK.+concatUnfoldr :: IsStream t+    => (b -> t m (Maybe (a, b))) -> t m b -> t m a+concatUnfoldr = undefined+-}++------------------------------------------------------------------------------+-- concatIterate - Map and flatten Trees of Streams+------------------------------------------------------------------------------++-- | Yield an input element in the output stream, map a stream generator on it+-- and repeat the process on the resulting stream. Resulting streams are+-- flattened using the 'concatMapWith' combinator. This can be used for a depth+-- first style (DFS) traversal of a tree like structure.+--+-- Example, list a directory tree using DFS:+--+-- >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)+-- >>> input = StreamK.fromPure (Left ".")+-- >>> ls = StreamK.concatIterateWith StreamK.append f input+--+-- Note that 'iterateM' is a special case of 'concatIterateWith':+--+-- >>> iterateM f = StreamK.concatIterateWith StreamK.append (StreamK.fromEffect . f) . StreamK.fromEffect+--+-- /Pre-release/+--+{-# INLINE concatIterateWith #-}+concatIterateWith ::+       (StreamK m a -> StreamK m a -> StreamK m a)+    -> (a -> StreamK m a)+    -> StreamK m a+    -> StreamK m a+concatIterateWith combine f = iterateStream++    where++    iterateStream = concatMapWith combine generate++    generate x = x `cons` iterateStream (f x)++-- | Like 'concatIterateWith' but uses the pairwise flattening combinator+-- 'mergeMapWith' for flattening the resulting streams. This can be used for a+-- balanced traversal of a tree like structure.+--+-- Example, list a directory tree using balanced traversal:+--+-- >>> f = StreamK.fromStream . either Dir.readEitherPaths (const Stream.nil)+-- >>> input = StreamK.fromPure (Left ".")+-- >>> ls = StreamK.mergeIterateWith StreamK.interleave f input+--+-- /Pre-release/+--+{-# INLINE mergeIterateWith #-}+mergeIterateWith ::+       (StreamK m a -> StreamK m a -> StreamK m a)+    -> (a -> StreamK m a)+    -> StreamK m a+    -> StreamK m a+mergeIterateWith combine f = iterateStream++    where++    iterateStream = mergeMapWith combine generate++    generate x = x `cons` iterateStream (f x)++------------------------------------------------------------------------------+-- Flattening Graphs+------------------------------------------------------------------------------++-- To traverse graphs we need a state to be carried around in the traversal.+-- For example, we can use a hashmap to store the visited status of nodes.++-- | Like 'iterateMap' but carries a state in the stream generation function.+-- This can be used to traverse graph like structures, we can remember the+-- visited nodes in the state to avoid cycles.+--+-- Note that a combination of 'iterateMap' and 'usingState' can also be used to+-- traverse graphs. However, this function provides a more localized state+-- instead of using a global state.+--+-- See also: 'mfix'+--+-- /Pre-release/+--+{-# INLINE concatIterateScanWith #-}+concatIterateScanWith+    :: Monad m+    => (StreamK m a -> StreamK m a -> StreamK m a)+    -> (b -> a -> m (b, StreamK m a))+    -> m b+    -> StreamK m a+    -> StreamK m a+concatIterateScanWith combine f initial stream =+    concatEffect $ do+        b <- initial+        iterateStream (b, stream)++    where++    iterateStream (b, s) = pure $ concatMapWith combine (generate b) s++    generate b a = a `cons` feedback b a++    feedback b a = concatEffect $ f b a >>= iterateStream++------------------------------------------------------------------------------+-- Either streams+------------------------------------------------------------------------------++-- Keep concating either streams as long as rights are generated, stop as soon+-- as a left is generated and concat the left stream.+--+-- See also: 'handle'+--+-- /Unimplemented/+--+{-+concatMapEitherWith+    :: (forall x. t m x -> t m x -> t m x)+    -> (a -> t m (Either (StreamK m b) b))+    -> StreamK m a+    -> StreamK m b+concatMapEitherWith = undefined+-}++-- XXX We should prefer using the Maybe stream returning signatures over this.+-- This API should perhaps be removed in favor of those.++-- | In an 'Either' stream iterate on 'Left's.  This is a special case of+-- 'concatIterateWith':+--+-- >>> concatIterateLeftsWith combine f = StreamK.concatIterateWith combine (either f (const StreamK.nil))+--+-- To traverse a directory tree:+--+-- >>> input = StreamK.fromPure (Left ".")+-- >>> ls = StreamK.concatIterateLeftsWith StreamK.append (StreamK.fromStream . Dir.readEither) input+--+-- /Pre-release/+--+{-# INLINE concatIterateLeftsWith #-}+concatIterateLeftsWith+    :: (b ~ Either a c)+    => (StreamK m b -> StreamK m b -> StreamK m b)+    -> (a -> StreamK m b)+    -> StreamK m b+    -> StreamK m b+concatIterateLeftsWith combine f =+    concatIterateWith combine (either f (const nil))++------------------------------------------------------------------------------+-- Interleaving+------------------------------------------------------------------------------++infixr 6 `interleave`++-- Additionally we can have m elements yield from the first stream and n+-- elements yielding from the second stream. We can also have time slicing+-- variants of positional interleaving, e.g. run first stream for m seconds and+-- run the second stream for n seconds.++-- | Note: When joining many streams in a left associative manner earlier+-- streams will get exponential priority than the ones joining later. Because+-- of exponentially high weighting of left streams it can be used with+-- 'concatMapWith' even on a large number of streams.+--+{-# INLINE interleave #-}+interleave :: StreamK m a -> StreamK m a -> StreamK m a+interleave m1 m2 = mkStream $ \st yld sng stp -> do+    let stop       = foldStream st yld sng stp m2+        single a   = yld a m2+        yieldk a r = yld a (interleave m2 r)+    foldStream st yieldk single stop m1++infixr 6 `interleaveFst`++-- | Like `interleave` but stops interleaving as soon as the first stream stops.+--+{-# INLINE interleaveFst #-}+interleaveFst :: StreamK m a -> StreamK m a -> StreamK m a+interleaveFst m1 m2 = mkStream $ \st yld sng stp -> do+    let yieldFirst a r = yld a (yieldSecond r m2)+     in foldStream st yieldFirst sng stp m1++    where++    yieldSecond s1 s2 = mkStream $ \st yld sng stp -> do+            let stop       = foldStream st yld sng stp s1+                single a   = yld a s1+                yieldk a r = yld a (interleave s1 r)+             in foldStream st yieldk single stop s2++infixr 6 `interleaveMin`++-- | Like `interleave` but stops interleaving as soon as any of the two streams+-- stops.+--+{-# INLINE interleaveMin #-}+interleaveMin :: StreamK m a -> StreamK m a -> StreamK m a+interleaveMin m1 m2 = mkStream $ \st yld _ stp -> do+    let stop       = stp+        -- "single a" is defined as "yld a (interleaveMin m2 nil)" instead of+        -- "sng a" to keep the behaviour consistent with the yield+        -- continuation.+        single a   = yld a (interleaveMin m2 nil)+        yieldk a r = yld a (interleaveMin m2 r)+    foldStream st yieldk single stop m1++-------------------------------------------------------------------------------+-- Generation+-------------------------------------------------------------------------------++-- |+-- >>> :{+-- unfoldr step s =+--     case step s of+--         Nothing -> StreamK.nil+--         Just (a, b) -> a `StreamK.cons` unfoldr step b+-- :}+--+-- Build a stream by unfolding a /pure/ step function @step@ starting from a+-- seed @s@.  The step function returns the next element in the stream and the+-- next seed value. When it is done it returns 'Nothing' and the stream ends.+-- For example,+--+-- >>> :{+-- let f b =+--         if b > 2+--         then Nothing+--         else Just (b, b + 1)+-- in StreamK.toList $ StreamK.unfoldr f 0+-- :}+-- [0,1,2]+--+{-# INLINE unfoldr #-}+unfoldr :: (b -> Maybe (a, b)) -> b -> StreamK m a+unfoldr next s0 = build $ \yld stp ->+    let go s =+            case next s of+                Just (a, b) -> yld a (go b)+                Nothing -> stp+    in go s0++{-# INLINE unfoldrMWith #-}+unfoldrMWith :: Monad m =>+       (m a -> StreamK m a -> StreamK m a)+    -> (b -> m (Maybe (a, b)))+    -> b+    -> StreamK m a+unfoldrMWith cns step = go++    where++    go s = sharedMWith cns $ \yld _ stp -> do+                r <- step s+                case r of+                    Just (a, b) -> yld a (go b)+                    Nothing -> stp++-- | Build a stream by unfolding a /monadic/ step function starting from a+-- seed.  The step function returns the next element in the stream and the next+-- seed value. When it is done it returns 'Nothing' and the stream ends. For+-- example,+--+-- >>> :{+-- let f b =+--         if b > 2+--         then return Nothing+--         else return (Just (b, b + 1))+-- in StreamK.toList $ StreamK.unfoldrM f 0+-- :}+-- [0,1,2]+--+{-# INLINE unfoldrM #-}+unfoldrM :: Monad m => (b -> m (Maybe (a, b))) -> b -> StreamK m a+unfoldrM = unfoldrMWith consM++-- | Generate an infinite stream by repeating a pure value.+--+-- /Pre-release/+{-# INLINE repeat #-}+repeat :: a -> StreamK m a+repeat a = let x = cons a x in x++-- | Like 'repeatM' but takes a stream 'cons' operation to combine the actions+-- in a stream specific manner. A serial cons would repeat the values serially+-- while an async cons would repeat concurrently.+--+-- /Pre-release/+repeatMWith :: (m a -> t m a -> t m a) -> m a -> t m a+repeatMWith cns = go++    where++    go m = m `cns` go m++{-# INLINE replicateMWith #-}+replicateMWith :: (m a -> StreamK m a -> StreamK m a) -> Int -> m a -> StreamK m a+replicateMWith cns n m = go n++    where++    go cnt = if cnt <= 0 then nil else m `cns` go (cnt - 1)++{-# INLINE fromIndicesMWith #-}+fromIndicesMWith ::+    (m a -> StreamK m a -> StreamK m a) -> (Int -> m a) -> StreamK m a+fromIndicesMWith cns gen = go 0++    where++    go i = mkStream $ \st stp sng yld -> do+        foldStreamShared st stp sng yld (gen i `cns` go (i + 1))++{-# INLINE iterateMWith #-}+iterateMWith :: Monad m =>+    (m a -> StreamK m a -> StreamK m a) -> (a -> m a) -> m a -> StreamK m a+iterateMWith cns step = go++    where++    go s = mkStream $ \st stp sng yld -> do+        !next <- s+        foldStreamShared st stp sng yld (return next `cns` go (step next))++{-# INLINE headPartial #-}+headPartial :: Monad m => StreamK m a -> m a+headPartial = foldrM (\x _ -> return x) (error "head of nil")++{-# INLINE tailPartial #-}+tailPartial :: StreamK m a -> StreamK m a+tailPartial m = mkStream $ \st yld sng stp ->+    let stop      = error "tail of nil"+        single _  = stp+        yieldk _ r = foldStream st yld sng stp r+    in foldStream st yieldk single stop m++-- | We can define cyclic structures using @let@:+--+-- >>> let (a, b) = ([1, b], head a) in (a, b)+-- ([1,1],1)+--+-- The function @fix@ defined as:+--+-- >>> fix f = let x = f x in x+--+-- ensures that the argument of a function and its output refer to the same+-- lazy value @x@ i.e.  the same location in memory.  Thus @x@ can be defined+-- in terms of itself, creating structures with cyclic references.+--+-- >>> f ~(a, b) = ([1, b], head a)+-- >>> fix f+-- ([1,1],1)+--+-- 'Control.Monad.mfix' is essentially the same as @fix@ but for monadic+-- values.+--+-- Using 'mfix' for streams we can construct a stream in which each element of+-- the stream is defined in a cyclic fashion. The argument of the function+-- being fixed represents the current element of the stream which is being+-- returned by the stream monad. Thus, we can use the argument to construct+-- itself.+--+-- In the following example, the argument @action@ of the function @f@+-- represents the tuple @(x,y)@ returned by it in a given iteration. We define+-- the first element of the tuple in terms of the second.+--+-- >>> import System.IO.Unsafe (unsafeInterleaveIO)+--+-- >>> :{+-- main = Stream.fold (Fold.drainMapM print) $ StreamK.toStream $ StreamK.mfix f+--     where+--     f action = StreamK.unCross $ do+--         let incr n act = fmap ((+n) . snd) $ unsafeInterleaveIO act+--         x <- StreamK.mkCross $ StreamK.fromStream $ Stream.sequence $ Stream.fromList [incr 1 action, incr 2 action]+--         y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [4,5]+--         return (x, y)+-- :}+--+-- Note: you cannot achieve this by just changing the order of the monad+-- statements because that would change the order in which the stream elements+-- are generated.+--+-- Note that the function @f@ must be lazy in its argument, that's why we use+-- 'unsafeInterleaveIO' on @action@ because IO monad is strict.+--+-- /Pre-release/+{-# INLINE mfix #-}+mfix :: Monad m => (m a -> StreamK m a) -> StreamK m a+mfix f = mkStream $ \st yld sng stp ->+    let single a  = foldStream st yld sng stp $ a `cons` ys+        yieldk a _ = foldStream st yld sng stp $ a `cons` ys+    in foldStream st yieldk single stp xs++    where++    -- fix the head element of the stream+    xs = fix  (f . headPartial)++    -- now fix the tail recursively+    ys = mfix (tailPartial . f)++-------------------------------------------------------------------------------+-- Conversions+-------------------------------------------------------------------------------++-- |+-- >>> fromFoldable = Prelude.foldr StreamK.cons StreamK.nil+--+-- Construct a stream from a 'Foldable' containing pure values:+--+{-# INLINE fromFoldable #-}+fromFoldable :: Foldable f => f a -> StreamK m a+fromFoldable = Prelude.foldr cons nil++{-# INLINE fromFoldableM #-}+fromFoldableM :: (Foldable f, Monad m) => f (m a) -> StreamK m a+fromFoldableM = Prelude.foldr consM nil++-------------------------------------------------------------------------------+-- Deconstruction+-------------------------------------------------------------------------------++{-# INLINE uncons #-}+uncons :: Applicative m => StreamK m a -> m (Maybe (a, StreamK m a))+uncons m =+    let stop = pure Nothing+        single a = pure (Just (a, nil))+        yieldk a r = pure (Just (a, r))+    in foldStream defState yieldk single stop m++{-# INLINE tail #-}+tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))+tail =+    let stop      = pure Nothing+        single _  = pure $ Just nil+        yieldk _ r = pure $ Just r+    in foldStream defState yieldk single stop++-- | Extract all but the last element of the stream, if any.+--+-- Note: This will end up buffering the entire stream.+--+-- /Pre-release/+{-# INLINE init #-}+init :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))+init = go1+    where+    go1 m1 = do+        (\case+            Nothing -> Nothing+            Just (h, t) -> Just $ go h t) <$> uncons m1+    go p m1 = mkStream $ \_ yld sng stp ->+        let single _ = sng p+            yieldk a x = yld p $ go a x+         in foldStream defState yieldk single stp m1++------------------------------------------------------------------------------+-- Reordering+------------------------------------------------------------------------------++-- | Lazy left fold to a stream.+{-# INLINE foldlS #-}+foldlS ::+    (StreamK m b -> a -> StreamK m b) -> StreamK m b -> StreamK m a -> StreamK m b+foldlS step = go+    where+    go acc rest = mkStream $ \st yld sng stp ->+        let run x = foldStream st yld sng stp x+            stop = run acc+            single a = run $ step acc a+            yieldk a r = run $ go (step acc a) r+         in foldStream (adaptState st) yieldk single stop rest++{-# INLINE reverse #-}+reverse :: StreamK m a -> StreamK m a+reverse = foldlS (flip cons) nil++------------------------------------------------------------------------------+-- Running effects+------------------------------------------------------------------------------++-- | Run an action before evaluating the stream.+{-# INLINE before #-}+before :: Monad m => m b -> StreamK m a -> StreamK m a+before action stream =+    mkStream $ \st yld sng stp ->+        action >> foldStreamShared st yld sng stp stream++{-# INLINE concatEffect #-}+concatEffect :: Monad m => m (StreamK m a) -> StreamK m a+concatEffect action =+    mkStream $ \st yld sng stp ->+        action >>= foldStreamShared st yld sng stp++{-# INLINE concatMapEffect #-}+concatMapEffect :: Monad m => (b -> StreamK m a) -> m b -> StreamK m a+concatMapEffect f action =+    mkStream $ \st yld sng stp ->+        action >>= foldStreamShared st yld sng stp . f++------------------------------------------------------------------------------+-- Stream with a cross product style monad instance+------------------------------------------------------------------------------++-- | A newtype wrapper for the 'StreamK' type adding a cross product style+-- monad instance.+--+-- A 'Monad' bind behaves like a @for@ loop:+--+-- >>> :{+-- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do+--     x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]+--     -- Perform the following actions for each x in the stream+--     return x+-- :}+-- [1,2]+--+-- Nested monad binds behave like nested @for@ loops:+--+-- >>> :{+-- Stream.fold Fold.toList $ StreamK.toStream $ StreamK.unCross $ do+--     x <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [1,2]+--     y <- StreamK.mkCross $ StreamK.fromStream $ Stream.fromList [3,4]+--     -- Perform the following actions for each x, for each y+--     return (x, y)+-- :}+-- [(1,3),(1,4),(2,3),(2,4)]+--+newtype CrossStreamK m a = CrossStreamK {unCrossStreamK :: StreamK m a}+        deriving (Functor, Semigroup, Monoid, Foldable)++-- | Wrap the 'StreamK' type in a 'CrossStreamK' newtype to enable cross+-- product style applicative and monad instances.+--+-- This is a type level operation with no runtime overhead.+{-# INLINE mkCross #-}+mkCross :: StreamK m a -> CrossStreamK m a+mkCross = CrossStreamK++-- | Unwrap the 'StreamK' type from 'CrossStreamK' newtype.+--+-- This is a type level operation with no runtime overhead.+{-# INLINE unCross #-}+unCross :: CrossStreamK m a -> StreamK m a+unCross = unCrossStreamK++-- Pure (Identity monad) stream instances+deriving instance Traversable (CrossStreamK Identity)+deriving instance IsList (CrossStreamK Identity a)+deriving instance (a ~ Char) => IsString (CrossStreamK Identity a)+-- deriving instance Eq a => Eq (CrossStreamK Identity a)+-- deriving instance Ord a => Ord (CrossStreamK Identity a)++-- Do not use automatic derivation for this to show as "fromList" rather than+-- "fromList Identity".+instance Show a => Show (CrossStreamK Identity a) where+    {-# INLINE show #-}+    show (CrossStreamK xs) = show xs++instance Read a => Read (CrossStreamK Identity a) where+    {-# INLINE readPrec #-}+    readPrec = fmap CrossStreamK readPrec++------------------------------------------------------------------------------+-- Applicative+------------------------------------------------------------------------------++-- Note: we need to define all the typeclass operations because we want to+-- INLINE them.+instance Monad m => Applicative (CrossStreamK m) where+    {-# INLINE pure #-}+    pure x = CrossStreamK (fromPure x)++    {-# INLINE (<*>) #-}+    (CrossStreamK s1) <*> (CrossStreamK s2) =+        CrossStreamK (crossApply s1 s2)++    {-# INLINE liftA2 #-}+    liftA2 f x = (<*>) (fmap f x)++    {-# INLINE (*>) #-}+    (CrossStreamK s1) *> (CrossStreamK s2) =+        CrossStreamK (crossApplySnd s1 s2)++    {-# INLINE (<*) #-}+    (CrossStreamK s1) <* (CrossStreamK s2) =+        CrossStreamK (crossApplyFst s1 s2)++------------------------------------------------------------------------------+-- Monad+------------------------------------------------------------------------------++instance Monad m => Monad (CrossStreamK m) where+    return = pure++    -- Benchmarks better with CPS bind and pure:+    -- Prime sieve (25x)+    -- n binds, breakAfterSome, filterAllIn, state transformer (~2x)+    --+    {-# INLINE (>>=) #-}+    (>>=) (CrossStreamK m) f =+        CrossStreamK (bindWith append m (unCrossStreamK . f))++    {-# INLINE (>>) #-}+    (>>) = (*>)++------------------------------------------------------------------------------+-- Transformers+------------------------------------------------------------------------------++instance (MonadIO m) => MonadIO (CrossStreamK m) where+    liftIO x = CrossStreamK (fromEffect $ liftIO x)++instance MonadTrans CrossStreamK where+    {-# INLINE lift #-}+    lift x = CrossStreamK (fromEffect x)++instance (MonadThrow m) => MonadThrow (CrossStreamK m) where+    throwM = lift . throwM
src/Streamly/Internal/Data/Time/Clock.hs view
@@ -9,8 +9,7 @@ module Streamly.Internal.Data.Time.Clock     (     -- * System clock-      Clock(..)-    , getTime+      module Streamly.Internal.Data.Time.Clock.Type      -- * Async clock     , asyncClock@@ -30,12 +29,13 @@ import Control.Concurrent (threadDelay, ThreadId) import Control.Concurrent.MVar (MVar, newEmptyMVar, takeMVar, tryPutMVar) import Control.Monad (forever, when, void)-import Streamly.Internal.Data.Time.Clock.Type (Clock(..), getTime) import Streamly.Internal.Data.Time.Units     (MicroSecond64(..), fromAbsTime, addToAbsTime, toRelTime) import Streamly.Internal.Control.ForkIO (forkIOManaged)  import qualified Streamly.Internal.Data.IORef.Unboxed as Unboxed++import Streamly.Internal.Data.Time.Clock.Type  ------------------------------------------------------------------------------ -- Async clock
src/Streamly/Internal/Data/Time/Units.hs view
@@ -1,4 +1,5 @@-{-# LANGUAGE TypeInType #-}+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE PolyKinds #-} {-# LANGUAGE UnboxedTuples #-}  -- |@@ -52,7 +53,7 @@  import Data.Int import Foreign.Storable (Storable)-import Streamly.Internal.Data.Unboxed (Unbox)+import Streamly.Internal.Data.Unbox (Unbox) import Streamly.Internal.Data.Time.TimeSpec  -------------------------------------------------------------------------------
+ src/Streamly/Internal/Data/Unbox.hs view
@@ -0,0 +1,905 @@+{-# LANGUAGE DataKinds #-}+{-# LANGUAGE DefaultSignatures #-}+{-# LANGUAGE UnboxedTuples #-}+{-# LANGUAGE UndecidableInstances #-}++-- |+-- Module      : Streamly.Internal.Data.Unbox+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Unbox+    (+    -- ** Unbox type class+      Unbox(..)++    -- ** Peek and poke utilities+    , BoundedPtr (..)+    -- Peek+    , Peeker (..)+    , read+    , readUnsafe+    , skipByte+    , runPeeker+    -- Poke+    , pokeBoundedPtrUnsafe+    , pokeBoundedPtr++    -- ** Generic Deriving+    , PeekRep(..)+    , PokeRep(..)+    , SizeOfRep(..)+    , genericSizeOf+    , genericPeekByteIndex+    , genericPokeByteIndex+    ) where++#include "MachDeps.h"+#include "ArrayMacros.h"++import Control.Monad (void, when)+import Data.Complex (Complex((:+)))+import Data.Functor ((<&>))+import Data.Functor.Const (Const(..))+import Data.Functor.Identity (Identity(..))+import Data.Kind (Type)+import Data.Proxy (Proxy (..))+import Foreign.Ptr (IntPtr(..), WordPtr(..))+import GHC.Base (IO(..))+import GHC.Fingerprint.Type (Fingerprint(..))+import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))+import GHC.Real (Ratio(..))+import GHC.Stable (StablePtr(..))+import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))+#if MIN_VERSION_base(4,15,0)+import GHC.RTS.Flags (IoSubSystem(..))+#endif+import Streamly.Internal.Data.Builder (Builder (..))++import GHC.Generics+import GHC.Exts+import GHC.TypeLits+import Prelude hiding (read)++import Streamly.Internal.Data.MutByteArray.Type (MutByteArray(..))+#ifdef DEBUG+import Streamly.Internal.Data.MutByteArray.Type (sizeOfMutableByteArray)+#endif++--------------------------------------------------------------------------------+-- The Unbox type class+--------------------------------------------------------------------------------++-- XXX generate error if the size is < 1++-- = Design notes =+--+-- == Fixed length data types ==+--+-- The main goal of the Unbox type class is to be used in arrays. Invariants+-- for the sizeOf value required for use in arrays:+--+-- * size is independent of the value, it is determined by the type only. So+-- that we can store values of the same type in fixed length array cells.+-- * recursive data types cannot be fixed length, therefore, cannot be+-- serialized using this type class.+-- * size cannot be zero. So that the length of an array storing the element+-- and the number of elements can be related.+--+-- Note, for general serializable types the size cannot be fixed e.g. we may+-- want to serialize a list. This type class can be considered a special case+-- of a more general serialization type class.+--+-- == Stream vs Array ==+--+-- In theory we could convert a type to and from a byte stream and use that+-- to implement boxing, unboxing. But composing a stream from parts of the+-- structure is much more inefficient than writing them to a memory location.+-- However, it should be possible to efficiently parse a Haskell type from an+-- array using chunk folds.+--+-- Also, this type class allows each primitive type to have its own specific+-- efficient implementation to read and write the type to the mutable byte+-- array using special GHC byte array operations. For example, see the Unbox+-- instances of Char, Int, Word types.+--+-- == MutableByteArray vs ForeignPtr ==+--+-- The Unbox typeclass uses MutableByteArray but could use ForeignPtr or+-- any other representation of memory. We could make this a multiparameter type+-- class if necessary.+--+-- If the type class would have to support reading and writing to a Ptr as well,+-- basically what Storable does. We will also need to touch the anchoring ptr at+-- the right points which is prone to errors. However, it should be simple to+-- implement unmanaged/read-only memory arrays by allowing a Ptr type in+-- ArrayContents, though it would require all instances to support reading from+-- a Ptr.+--+-- == Byte Offset vs Element Index ==+--+-- There is a reason for using byte offset instead of element index in read and+-- write operations in the type class. If we use element index, slicing of the+-- array becomes rigid. We can only slice the array at addresses that are+-- aligned with the type, therefore, we cannot slice at misaligned location and+-- then cast the slice into another type which does not necessarily align with+-- the original type.+--+-- == Alignment ==+--+-- As a side note, there seem to be no performance advantage of alignment+-- anymore, see+-- https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/+--+-- = Unboxed Records =+--+-- Unboxed types can be treated as unboxed records. We can provide a more+-- convenient API to access different parts from the Unboxed representation+-- without having to unbox the entire type. The type can have nested parts+-- therefore, we will need a general way (some sort of lenses) to address the+-- parts.+--+-- = Lazy Boxing =+--+-- When converting an unboxed representation to a boxed representation we can+-- use lazy construction. Each constructor of the constructed computation may+-- just hold a lazy computation to actually construct it on demand. This could+-- be useful for larger structures where we may need only small parts of it.+--+-- Same thing can be done for serialize type class as well but it will require+-- size fields at each nesting level, aggregating the size upwards.++-- | The 'Unbox' type class provides operations for serialization (unboxing)+-- and deserialization (boxing) of fixed-length, non-recursive Haskell data+-- types to and from their byte stream representation.+--+-- Unbox uses fixed size encoding, therefore, size is independent of the value,+-- it must be determined solely by the type. This restriction makes types with+-- 'Unbox' instances suitable for storing in arrays. Note that sum types may+-- have multiple constructors of different sizes, the size of a sum type is+-- computed as the maximum required by any constructor.+--+-- The 'peekAt' operation reads as many bytes from the mutable byte+-- array as the @size@ of the data type and builds a Haskell data type from+-- these bytes. 'pokeAt' operation converts a Haskell data type to its+-- binary representation which consists of @size@ bytes and then stores+-- these bytes into the mutable byte array. These operations do not check the+-- bounds of the array, the user of the type class is expected to check the+-- bounds before peeking or poking.+--+-- IMPORTANT: The serialized data's byte ordering remains the same as the host+-- machine's byte order. Therefore, it can not be deserialized from host+-- machines with a different byte ordering.+--+-- Instances can be derived via Generics, Template Haskell, or written+-- manually. Note that the data type must be non-recursive. WARNING! Generic+-- and Template Haskell deriving, both hang for recursive data types. Deriving+-- via Generics is more convenient but Template Haskell should be preferred+-- over Generics for the following reasons:+--+-- 1. Instances derived via Template Haskell provide better and more reliable+-- performance.+-- 2. Generic deriving allows only 256 fields or constructor tags whereas+-- template Haskell has no limit.+--+-- Here is an example, for deriving an instance of this type class using+-- generics:+--+-- >>> import GHC.Generics (Generic)+-- >>> :{+-- data Object = Object+--     { _int0 :: Int+--     , _int1 :: Int+--     } deriving Generic+-- :}+--+-- >>> import Streamly.Data.MutByteArray (Unbox(..))+-- >>> instance Unbox Object+--+-- To derive the instance via Template Haskell:+--+-- @+-- import Streamly.Data.MutByteArray (deriveUnbox)+-- \$(deriveUnbox [d|instance Unbox Object|])+-- @+--+-- See 'Streamly.Data.MutByteArray.deriveUnbox' for more information on deriving+-- using Template Haskell.+--+-- If you want to write the instance manually:+--+-- >>> :{+-- instance Unbox Object where+--     sizeOf _ = 16+--     peekAt i arr = do+--        -- Check the array bounds+--         x0 <- peekAt i arr+--         x1 <- peekAt (i + 8) arr+--         return $ Object x0 x1+--     pokeAt i arr (Object x0 x1) = do+--        -- Check the array bounds+--         pokeAt i arr x0+--         pokeAt (i + 8) arr x1+-- :}+--+class Unbox a where+    -- | Get the size. Size cannot be zero, should be at least 1 byte.+    sizeOf :: Proxy a -> Int++    {-# INLINE sizeOf #-}+    default sizeOf :: (SizeOfRep (Rep a)) => Proxy a -> Int+    sizeOf = genericSizeOf++    -- | @peekAt byte-offset array@ reads an element of type @a@ from the+    -- the given the byte offset in the array.+    --+    -- IMPORTANT: The implementation of this interface may not check the bounds+    -- of the array, the caller must not assume that.+    peekAt :: Int -> MutByteArray -> IO a++    {-# INLINE peekAt #-}+    default peekAt :: (Generic a, PeekRep (Rep a)) =>+         Int -> MutByteArray -> IO a+    peekAt i arr = genericPeekByteIndex arr i++    peekByteIndex :: Int -> MutByteArray -> IO a+    peekByteIndex = peekAt++    -- | @pokeAt byte-offset array@ writes an element of type @a@ to the+    -- the given the byte offset in the array.+    --+    -- IMPORTANT: The implementation of this interface may not check the bounds+    -- of the array, the caller must not assume that.+    pokeAt :: Int -> MutByteArray -> a -> IO ()++    pokeByteIndex :: Int -> MutByteArray -> a -> IO ()+    pokeByteIndex = pokeAt++    {-# INLINE pokeAt #-}+    default pokeAt :: (Generic a, PokeRep (Rep a)) =>+        Int -> MutByteArray -> a -> IO ()+    pokeAt i arr = genericPokeByteIndex arr i++{-# DEPRECATED peekByteIndex "Use peekAt." #-}+{-# DEPRECATED pokeByteIndex "Use pokeAt." #-}++-- _size is the length from array start to the last accessed byte.+{-# INLINE checkBounds #-}+checkBounds :: String -> Int -> MutByteArray -> IO ()+checkBounds _label _size _arr = do+#ifdef DEBUG+    sz <- sizeOfMutableByteArray _arr+    if (_size > sz)+    then error+        $ _label+            ++ ": accessing array at offset = "+            ++ show (_size - 1)+            ++ " max valid offset = " ++ show (sz - 1)+    else return ()+#else+    return ()+#endif++#define DERIVE_UNBOXED(_type, _constructor, _readArray, _writeArray, _sizeOf) \+instance Unbox _type where {                                                  \+; {-# INLINE peekAt #-}                                                       \+; peekAt off@(I# n) arr@(MutByteArray mbarr) =                                \+    checkBounds "peek" (off + sizeOf (Proxy :: Proxy _type)) arr              \+    >> (IO $ \s ->                                                            \+      case _readArray mbarr n s of                                            \+          { (# s1, i #) -> (# s1, _constructor i #) })                        \+; {-# INLINE pokeAt #-}                                                       \+; pokeAt off@(I# n) arr@(MutByteArray mbarr) (_constructor val) =             \+    checkBounds "poke" (off + sizeOf (Proxy :: Proxy _type)) arr              \+    >> (IO $ \s -> (# _writeArray mbarr n val s, () #))                       \+; {-# INLINE sizeOf #-}                                                       \+; sizeOf _ = _sizeOf                                                          \+}++#define DERIVE_WRAPPED_UNBOX(_constraint, _type, _constructor, _innerType)    \+instance _constraint Unbox _type where                                        \+; {-# INLINE peekAt #-}                                                       \+; peekAt i arr =                                                              \+    checkBounds "peek" (i + sizeOf (Proxy :: Proxy _type)) arr                \+    >> _constructor <$> peekAt i arr                                          \+; {-# INLINE pokeAt #-}                                                       \+; pokeAt i arr (_constructor a) =                                             \+    checkBounds "poke" (i + sizeOf (Proxy :: Proxy _type)) arr                \+    >> pokeAt i arr a                                                         \+; {-# INLINE sizeOf #-}                                                       \+; sizeOf _ = SIZE_OF(_innerType)++#define DERIVE_BINARY_UNBOX(_constraint, _type, _constructor, _innerType)     \+instance _constraint Unbox _type where {                                      \+; {-# INLINE peekAt #-}                                                       \+; peekAt i arr =                                                              \+      checkBounds "peek" (i + sizeOf (Proxy :: Proxy _type)) arr >>           \+      peekAt i arr >>=                                                        \+        (\p1 -> peekAt (i + SIZE_OF(_innerType)) arr <&> _constructor p1)     \+; {-# INLINE pokeAt #-}                                                       \+; pokeAt i arr (_constructor p1 p2) =                                         \+      checkBounds "poke" (i + sizeOf (Proxy :: Proxy _type)) arr >>           \+      pokeAt i arr p1 >>                                                      \+        pokeAt (i + SIZE_OF(_innerType)) arr p2                               \+; {-# INLINE sizeOf #-}                                                       \+; sizeOf _ = 2 * SIZE_OF(_innerType)                                          \+}++-------------------------------------------------------------------------------+-- Unbox instances for primitive types+-------------------------------------------------------------------------------++DERIVE_UNBOXED( Char+              , C#+              , readWord8ArrayAsWideChar#+              , writeWord8ArrayAsWideChar#+              , SIZEOF_HSCHAR)++DERIVE_UNBOXED( Int8+              , I8#+              , readInt8Array#+              , writeInt8Array#+              , 1)++DERIVE_UNBOXED( Int16+              , I16#+              , readWord8ArrayAsInt16#+              , writeWord8ArrayAsInt16#+              , 2)++DERIVE_UNBOXED( Int32+              , I32#+              , readWord8ArrayAsInt32#+              , writeWord8ArrayAsInt32#+              , 4)++DERIVE_UNBOXED( Int+              , I#+              , readWord8ArrayAsInt#+              , writeWord8ArrayAsInt#+              , SIZEOF_HSINT)++DERIVE_UNBOXED( Int64+              , I64#+              , readWord8ArrayAsInt64#+              , writeWord8ArrayAsInt64#+              , 8)++DERIVE_UNBOXED( Word+              , W#+              , readWord8ArrayAsWord#+              , writeWord8ArrayAsWord#+              , SIZEOF_HSWORD)++DERIVE_UNBOXED( Word8+              , W8#+              , readWord8Array#+              , writeWord8Array#+              , 1)++DERIVE_UNBOXED( Word16+              , W16#+              , readWord8ArrayAsWord16#+              , writeWord8ArrayAsWord16#+              , 2)++DERIVE_UNBOXED( Word32+              , W32#+              , readWord8ArrayAsWord32#+              , writeWord8ArrayAsWord32#+              , 4)++DERIVE_UNBOXED( Word64+              , W64#+              , readWord8ArrayAsWord64#+              , writeWord8ArrayAsWord64#+              , 8)++DERIVE_UNBOXED( Double+              , D#+              , readWord8ArrayAsDouble#+              , writeWord8ArrayAsDouble#+              , SIZEOF_HSDOUBLE)++DERIVE_UNBOXED( Float+              , F#+              , readWord8ArrayAsFloat#+              , writeWord8ArrayAsFloat#+              , SIZEOF_HSFLOAT)++-------------------------------------------------------------------------------+-- Unbox instances for derived types+-------------------------------------------------------------------------------++DERIVE_UNBOXED( (StablePtr a)+              , StablePtr+              , readWord8ArrayAsStablePtr#+              , writeWord8ArrayAsStablePtr#+              , SIZEOF_HSSTABLEPTR)++DERIVE_UNBOXED( (Ptr a)+              , Ptr+              , readWord8ArrayAsAddr#+              , writeWord8ArrayAsAddr#+              , SIZEOF_HSPTR)++DERIVE_UNBOXED( (FunPtr a)+              , FunPtr+              , readWord8ArrayAsAddr#+              , writeWord8ArrayAsAddr#+              , SIZEOF_HSFUNPTR)++DERIVE_WRAPPED_UNBOX(,IntPtr,IntPtr,Int)+DERIVE_WRAPPED_UNBOX(,WordPtr,WordPtr,Word)+DERIVE_WRAPPED_UNBOX(Unbox a =>,(Identity a),Identity,a)+#if MIN_VERSION_base(4,14,0)+DERIVE_WRAPPED_UNBOX(Unbox a =>,(Down a),Down,a)+#endif+DERIVE_WRAPPED_UNBOX(Unbox a =>,(Const a b),Const,a)+DERIVE_BINARY_UNBOX(forall a. Unbox a =>,(Complex a),(:+),a)+DERIVE_BINARY_UNBOX(forall a. Unbox a =>,(Ratio a),(:%),a)+DERIVE_BINARY_UNBOX(,Fingerprint,Fingerprint,Word64)++instance Unbox () where++    {-# INLINE peekAt #-}+    peekAt i arr =+      checkBounds "peek ()" (i + sizeOf (Proxy :: Proxy ())) arr >> return ()++    {-# INLINE pokeAt #-}+    pokeAt i arr _ =+      checkBounds "poke ()" (i + sizeOf (Proxy :: Proxy ())) arr >> return ()++    {-# INLINE sizeOf #-}+    sizeOf _ = 1++#if MIN_VERSION_base(4,15,0)+instance Unbox IoSubSystem where++    {-# INLINE peekAt #-}+    peekAt i arr =+        checkBounds+            "peek IoSubSystem" (i + sizeOf (Proxy :: Proxy IoSubSystem)) arr+        >> toEnum <$> peekAt i arr++    {-# INLINE pokeAt #-}+    pokeAt i arr a =+        checkBounds+            "poke IoSubSystem" (i + sizeOf (Proxy :: Proxy IoSubSystem)) arr+        >> pokeAt i arr (fromEnum a)++    {-# INLINE sizeOf #-}+    sizeOf _ = sizeOf (Proxy :: Proxy Int)+#endif++instance Unbox Bool where++    {-# INLINE peekAt #-}+    peekAt i arr = do+        checkBounds "peek Bool" (i + sizeOf (Proxy :: Proxy Bool)) arr+        res <- peekAt i arr+        return $ res /= (0 :: Int8)++    {-# INLINE pokeAt #-}+    pokeAt i arr a =+        checkBounds "poke Bool" (i + sizeOf (Proxy :: Proxy Bool)) arr+        >> if a+           then pokeAt i arr (1 :: Int8)+           else pokeAt i arr (0 :: Int8)++    {-# INLINE sizeOf #-}+    sizeOf _ = 1++--------------------------------------------------------------------------------+-- Generic deriving+--------------------------------------------------------------------------------++-- Utilities to build or parse a type safely and easily.++-- | A location inside a mutable byte array with the bound of the array. Is it+-- cheaper to just get the bound using the size of the array whenever needed?+data BoundedPtr =+    BoundedPtr+        MutByteArray          -- byte array+        Int                       -- current pos+        Int                       -- position after end++--------------------------------------------------------------------------------+-- Peeker monad+--------------------------------------------------------------------------------++-- | Chains peek functions that pass the current position to the next function+newtype Peeker a = Peeker (Builder BoundedPtr IO a)+    deriving (Functor, Applicative, Monad)++{-# INLINE readUnsafe #-}+readUnsafe :: Unbox a => Peeker a+readUnsafe = Peeker (Builder step)++    where++    {-# INLINE step #-}+    step :: forall a. Unbox a => BoundedPtr -> IO (a, BoundedPtr)+    step (BoundedPtr arr pos end) = do+        let next = pos + sizeOf (Proxy :: Proxy a)+#ifdef DEBUG+        when (next > end)+            $ error $ "readUnsafe: reading beyond limit. next = "+                ++ show next+                ++ " end = " ++ show end+#endif+        r <- peekAt pos arr+        return (r, BoundedPtr arr next end)++{-# INLINE read #-}+read :: Unbox a => Peeker a+read = Peeker (Builder step)++    where++    {-# INLINE step #-}+    step :: forall a. Unbox a => BoundedPtr -> IO (a, BoundedPtr)+    step (BoundedPtr arr pos end) = do+        let next = pos + sizeOf (Proxy :: Proxy a)+        when (next > end)+            $ error $ "read: reading beyond limit. next = "+                ++ show next+                ++ " end = " ++ show end+        r <- peekAt pos arr+        return (r, BoundedPtr arr next end)++{-# INLINE skipByte #-}+skipByte :: Peeker ()+skipByte = Peeker (Builder step)++    where++    {-# INLINE step #-}+    step :: BoundedPtr -> IO ((), BoundedPtr)+    step (BoundedPtr arr pos end) = do+        let next = pos + 1+#ifdef DEBUG+        when (next > end)+            $ error $ "skipByte: reading beyond limit. next = "+                ++ show next+                ++ " end = " ++ show end+#endif+        return ((), BoundedPtr arr next end)++{-# INLINE runPeeker #-}+runPeeker :: Peeker a -> BoundedPtr -> IO a+runPeeker (Peeker (Builder f)) ptr = fmap fst (f ptr)++--------------------------------------------------------------------------------+-- Poke utilities+--------------------------------------------------------------------------------++-- XXX Using a Poker monad may be useful when we have to compute the size to be+-- poked as we go and then poke the size at a previous location. For variable+-- sized object serialization we may also want to reallocate the array and+-- return the newly allocated array in the output.++-- Does not check writing beyond bound.+{-# INLINE pokeBoundedPtrUnsafe #-}+pokeBoundedPtrUnsafe :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr+pokeBoundedPtrUnsafe a (BoundedPtr arr pos end) = do+    let next = pos + sizeOf (Proxy :: Proxy a)+#ifdef DEBUG+    when (next > end)+        $ error $ "pokeBoundedPtrUnsafe: reading beyond limit. next = "+            ++ show next+            ++ " end = " ++ show end+#endif+    pokeAt pos arr a+    return (BoundedPtr arr next end)++{-# INLINE pokeBoundedPtr #-}+pokeBoundedPtr :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr+pokeBoundedPtr a (BoundedPtr arr pos end) = do+    let next = pos + sizeOf (Proxy :: Proxy a)+    when (next > end) $ error "pokeBoundedPtr writing beyond limit"+    pokeAt pos arr a+    return (BoundedPtr arr next end)++--------------------------------------------------------------------------------+-- Check the number of constructors in a sum type+--------------------------------------------------------------------------------++-- Count the constructors of a sum type.+type family SumArity (a :: Type -> Type) :: Nat where+    SumArity (C1 _ _) = 1+    -- Requires UndecidableInstances+    SumArity (f :+: g) = SumArity f + SumArity g++type family TypeErrorMessage (a :: Symbol) :: Constraint where+    TypeErrorMessage a = TypeError ('Text a)++type family ArityCheck (b :: Bool) :: Constraint where+    ArityCheck 'True = ()+    ArityCheck 'False = TypeErrorMessage+        "Generic Unbox deriving does not support > 256 constructors."++-- Type constraint to restrict the sum type arity so that the constructor tag+-- can fit in a single byte.+-- Note that Arity starts from 1 and constructor tags start from 0. So if max+-- arity is 256 then max constructor tag would be 255.+-- XXX Use variable length encoding to support more than 256 constructors.+type MaxArity256 n = ArityCheck (n <=? 256)++--------------------------------------------------------------------------------+-- Generic Deriving of Unbox instance+--------------------------------------------------------------------------------++-- Unbox uses fixed size encoding, therefore, when a (sum) type has multiple+-- constructors, the size of the type is computed as the maximum required by+-- any constructor. Therefore, size is independent of the value, it can be+-- determined solely by the type.++-- | Implementation of sizeOf that works on the generic representation of an+-- ADT.+class SizeOfRep (f :: Type -> Type) where+    sizeOfRep :: f x -> Int++-- Meta information wrapper, go inside+instance SizeOfRep f => SizeOfRep (M1 i c f) where+    {-# INLINE sizeOfRep #-}+    sizeOfRep _ = sizeOfRep (undefined :: f x)++-- Primitive type "a".+instance Unbox a => SizeOfRep (K1 i a) where+    {-# INLINE sizeOfRep #-}+    sizeOfRep _ = sizeOf (Proxy :: Proxy a)++-- Void: data type without constructors. Values of this type cannot exist,+-- therefore the size is undefined. We should never be serializing structures+-- with elements of this type.+instance SizeOfRep V1 where+    {-# INLINE sizeOfRep #-}+    sizeOfRep = error "sizeOfRep: a value of a Void type must not exist"++-- Note that when a sum type has many unit constructors only a single byte is+-- required to encode the type as only the constructor tag is stored.+instance SizeOfRep U1 where+    {-# INLINE sizeOfRep #-}+    sizeOfRep _ = 0++-- Product type+instance (SizeOfRep f, SizeOfRep g) => SizeOfRep (f :*: g) where+    {-# INLINE sizeOfRep #-}+    sizeOfRep _ = sizeOfRep (undefined :: f x) + sizeOfRep (undefined :: g x)++-------------------------------------------------------------------------------++class SizeOfRepSum (f :: Type -> Type) where+    sizeOfRepSum :: f x -> Int++-- Constructor+instance SizeOfRep a => SizeOfRepSum (C1 c a) where+    {-# INLINE sizeOfRepSum #-}+    sizeOfRepSum = sizeOfRep++instance (SizeOfRepSum f, SizeOfRepSum g) => SizeOfRepSum (f :+: g) where+    {-# INLINE sizeOfRepSum #-}+    sizeOfRepSum _ =+        max (sizeOfRepSum (undefined :: f x)) (sizeOfRepSum (undefined :: g x))++-------------------------------------------------------------------------------++instance (MaxArity256 (SumArity (f :+: g)), SizeOfRepSum f, SizeOfRepSum g) =>+    SizeOfRep (f :+: g) where++    -- The size of a sum type is the max of any of the constructor size.+    -- sizeOfRepSum type class operation is used here instead of sizeOfRep so+    -- that we account the constructor index byte only for the first time and+    -- avoid including it for the subsequent sum constructors.+    {-# INLINE sizeOfRep #-}+    sizeOfRep _ =+        -- One byte for the constructor id and then the constructor value.+        sizeOf (Proxy :: Proxy Word8) ++            max (sizeOfRepSum (undefined :: f x))+                (sizeOfRepSum (undefined :: g x))++-- Unit: constructors without arguments.+-- Theoretically the size can be 0, but we use 1 to simplify the implementation+-- of an array of unit type elements. With a non-zero size we can count the number+-- of elements in the array based on the size of the array. Otherwise we will+-- have to store a virtual length in the array, but keep the physical size of+-- the array as 0. Or we will have to make a special handling for zero sized+-- elements to make the size as 1. Or we can disallow arrays with elements+-- having size 0.+--+-- Some examples:+--+-- data B = B -- one byte+-- data A = A B -- one byte+-- data X = X1 | X2 -- one byte (constructor tag only)+--+{-# INLINE genericSizeOf #-}+genericSizeOf :: forall a. (SizeOfRep (Rep a)) => Proxy a -> Int+genericSizeOf _ =+    let s = sizeOfRep (undefined :: Rep a x)+      in if s == 0 then 1 else s++--------------------------------------------------------------------------------+-- Generic poke+--------------------------------------------------------------------------------++class PokeRep (f :: Type -> Type) where+    pokeRep :: f a -> BoundedPtr -> IO BoundedPtr++instance PokeRep f => PokeRep (M1 i c f) where+    {-# INLINE pokeRep #-}+    pokeRep f = pokeRep (unM1 f)++instance Unbox a => PokeRep (K1 i a) where+    {-# INLINE pokeRep #-}+    pokeRep a = pokeBoundedPtrUnsafe (unK1 a)++instance PokeRep V1 where+    {-# INLINE pokeRep #-}+    pokeRep = error "pokeRep: a value of a Void type should not exist"++instance PokeRep U1 where+    {-# INLINE pokeRep #-}+    pokeRep _ x = pure x++instance (PokeRep f, PokeRep g) => PokeRep (f :*: g) where+    {-# INLINE pokeRep #-}+    pokeRep (f :*: g) ptr = pokeRep f ptr >>= pokeRep g++-------------------------------------------------------------------------------++class KnownNat n => PokeRepSum (n :: Nat) (f :: Type -> Type) where+    -- "n" is the constructor tag to be poked.+    pokeRepSum :: Proxy n -> f a -> BoundedPtr -> IO BoundedPtr++instance (KnownNat n, PokeRep a) => PokeRepSum n (C1 c a) where+    {-# INLINE pokeRepSum #-}+    pokeRepSum _ x ptr = do+        let tag = fromInteger (natVal (Proxy :: Proxy n)) :: Word8+        pokeBoundedPtrUnsafe tag ptr >>= pokeRep x++instance (PokeRepSum n f, PokeRepSum (n + SumArity f) g)+         => PokeRepSum n (f :+: g) where+    {-# INLINE pokeRepSum #-}+    pokeRepSum _ (L1 x) ptr =+        pokeRepSum (Proxy :: Proxy n) x ptr+    pokeRepSum _ (R1 x) ptr =+        pokeRepSum (Proxy :: Proxy (n + SumArity f)) x ptr++-------------------------------------------------------------------------------++instance (MaxArity256 (SumArity (f :+: g)), PokeRepSum 0 (f :+: g)) =>+    PokeRep (f :+: g) where++    {-# INLINE pokeRep #-}+    pokeRep = pokeRepSum (Proxy :: Proxy 0)++{-# INLINE genericPokeObject #-}+genericPokeObject :: (Generic a, PokeRep (Rep a)) =>+    a -> BoundedPtr -> IO BoundedPtr+genericPokeObject a = pokeRep (from a)++genericPokeObj :: (Generic a, PokeRep (Rep a)) => a -> BoundedPtr -> IO ()+genericPokeObj a ptr = void $ genericPokeObject a ptr++{-# INLINE genericPokeByteIndex #-}+genericPokeByteIndex :: (Generic a, PokeRep (Rep a)) =>+    MutByteArray -> Int -> a -> IO ()+genericPokeByteIndex arr index x = do+    -- XXX Should we use unsafe poke?+#ifdef DEBUG+    end <- sizeOfMutableByteArray arr+    genericPokeObj x (BoundedPtr arr index end)+#else+    genericPokeObj x (BoundedPtr arr index undefined)+#endif++--------------------------------------------------------------------------------+-- Generic peek+--------------------------------------------------------------------------------++class PeekRep (f :: Type -> Type) where+    -- Like pokeRep, we can use the following signature instead of using Peeker+    -- peekRep :: BoundedPtr -> IO (BoundedPtr, f a)+    peekRep :: Peeker (f x)++instance PeekRep f => PeekRep (M1 i c f) where+    {-# INLINE peekRep #-}+    peekRep = fmap M1 peekRep++instance Unbox a => PeekRep (K1 i a) where+    {-# INLINE peekRep #-}+    peekRep = fmap K1 readUnsafe++instance PeekRep V1 where+    {-# INLINE peekRep #-}+    peekRep = error "peekRep: a value of a Void type should not exist"++instance PeekRep U1 where+    {-# INLINE peekRep #-}+    peekRep = pure U1++instance (PeekRep f, PeekRep g) => PeekRep (f :*: g) where+    {-# INLINE peekRep #-}+    peekRep = (:*:) <$> peekRep <*> peekRep++-------------------------------------------------------------------------------++class KnownNat n => PeekRepSum (n :: Nat) (f :: Type -> Type) where+    -- "n" is the constructor tag to be matched.+    peekRepSum :: Proxy n -> Word8 -> Peeker (f a)++instance (KnownNat n, PeekRep a) => PeekRepSum n (C1 c a) where+    {-# INLINE peekRepSum #-}+    peekRepSum _ _ = peekRep+    {-+    -- These error checks are expensive, to avoid these+    -- we validate the max value of the tag in peekRep.+    -- XXX Add tests to cover all cases+    peekRepSum _ tag+        | tag == curTag = peekRep+        | tag > curTag =+            error $ "Unbox instance peek: Constructor tag index "+                ++ show tag ++ " out of range, max tag index is "+                ++ show curTag+        | otherwise = error "peekRepSum: bug"++        where++        curTag = fromInteger (natVal (Proxy :: Proxy n))+    -}++instance (PeekRepSum n f, PeekRepSum (n + SumArity f) g)+         => PeekRepSum n (f :+: g) where+    {-# INLINE peekRepSum #-}+    peekRepSum curProxy tag+        | tag < firstRightTag =+            L1 <$> peekRepSum curProxy tag+        | otherwise =+            R1 <$> peekRepSum (Proxy :: Proxy (n + SumArity f)) tag++        where++        firstRightTag = fromInteger (natVal (Proxy :: Proxy (n + SumArity f)))++-------------------------------------------------------------------------------++instance ( MaxArity256 (SumArity (f :+: g))+         , KnownNat (SumArity (f :+: g))+         , PeekRepSum 0 (f :+: g))+         => PeekRep (f :+: g) where+    {-# INLINE peekRep #-}+    peekRep = do+        tag :: Word8 <- readUnsafe+        -- XXX test with 256 and more constructors+        let arity :: Int =+                fromInteger (natVal (Proxy :: Proxy (SumArity (f :+: g))))+        when (fromIntegral tag >= arity)+            $ error $ "peek: Tag " ++ show tag+                ++ " is greater than the max tag " ++ show (arity - 1)+                ++ " for the data type"+        peekRepSum (Proxy :: Proxy 0) tag -- DataKinds++{-# INLINE genericPeeker #-}+genericPeeker :: (Generic a, PeekRep (Rep a)) => Peeker a+genericPeeker = to <$> peekRep++{-# INLINE genericPeekBoundedPtr #-}+genericPeekBoundedPtr :: (Generic a, PeekRep (Rep a)) => BoundedPtr -> IO a+genericPeekBoundedPtr = runPeeker genericPeeker++{-# INLINE genericPeekByteIndex #-}+genericPeekByteIndex :: (Generic a, PeekRep (Rep a)) =>+    MutByteArray -> Int -> IO a+genericPeekByteIndex arr index = do+    -- XXX Should we use unsafe peek?+#ifdef DEBUG+    end <- sizeOfMutableByteArray arr+    genericPeekBoundedPtr (BoundedPtr arr index end)+#else+    genericPeekBoundedPtr (BoundedPtr arr index undefined)+#endif
+ src/Streamly/Internal/Data/Unbox/TH.hs view
@@ -0,0 +1,495 @@+{-# LANGUAGE TemplateHaskell #-}++-- |+-- Module      : Streamly.Internal.Data.Unbox.TH+-- Copyright   : (c) 2023 Composewell Technologies+-- License     : BSD3-3-Clause+-- Maintainer  : streamly@composewell.com+-- Stability   : experimental+-- Portability : GHC+--+module Streamly.Internal.Data.Unbox.TH+    ( deriveUnbox++    -- th-helpers+    , DataCon(..)+    , DataType(..)+    , reifyDataType+    ) where++--------------------------------------------------------------------------------+-- Imports+--------------------------------------------------------------------------------++import Data.Word (Word16, Word32, Word64, Word8)+import Data.Proxy (Proxy(..))+import Data.List (elemIndex)++import Language.Haskell.TH+import Language.Haskell.TH.Syntax+import Streamly.Internal.Data.Unbox++--------------------------------------------------------------------------------+-- th-utilities+--------------------------------------------------------------------------------++-- Note: We don't support template-haskell < 2.14 (GHC < 8.6)++-- The following are copied to remove the dependency on th-utilities.+-- The code has been copied from th-abstraction and th-utilities.++-- Some CPP macros in the following code are not required but are kept+-- anyway. They can be removed if deemed as a maintainance burden.++#if MIN_VERSION_template_haskell(2,17,0)+type TyVarBndr_ flag = TyVarBndr flag+#else+type TyVarBndr_ flag = TyVarBndr+#endif++-- | Case analysis for a 'TyVarBndr'. If the value is a @'PlainTV' n _@, apply+-- the first function to @n@; if it is @'KindedTV' n _ k@, apply the second+-- function to @n@ and @k@.+elimTV :: (Name -> r) -> (Name -> Kind -> r) -> TyVarBndr_ flag -> r+#if MIN_VERSION_template_haskell(2,17,0)+elimTV ptv _ktv (PlainTV n _)    = ptv n+elimTV _ptv ktv (KindedTV n _ k) = ktv n k+#else+elimTV ptv _ktv (PlainTV n)    = ptv n+elimTV _ptv ktv (KindedTV n k) = ktv n k+#endif++-- | Extract the type variable name from a 'TyVarBndr', ignoring the+-- kind signature if one exists.+tvName :: TyVarBndr_ flag -> Name+tvName = elimTV id (\n _ -> n)++-- | Get the 'Name' of a 'TyVarBndr'+tyVarBndrName :: TyVarBndr_ flag -> Name+tyVarBndrName = tvName++-- | Simplified info about a 'DataD'. Omits deriving, strictness,+-- kind info, and whether it's @data@ or @newtype@.+data DataType = DataType+    { dtName :: Name+    , dtTvs :: [Name]+    , dtCxt :: Cxt+    , dtCons :: [DataCon]+    } deriving (Eq, Show, Ord) --, Data, Typeable, Generic)++-- | Simplified info about a 'Con'. Omits deriving, strictness, and kind+-- info. This is much nicer than consuming 'Con' directly, because it+-- unifies all the constructors into one.+data DataCon = DataCon+    { dcName :: Name+    , dcTvs :: [Name]+    , dcCxt :: Cxt+    , dcFields :: [(Maybe Name, Type)]+    } deriving (Eq, Show, Ord) --, Data, Typeable, Generic)+++-- | Convert a 'Con' to a list of 'DataCon'. The result is a list+-- because 'GadtC' and 'RecGadtC' can define multiple constructors.+conToDataCons :: Con -> [DataCon]+conToDataCons = \case+    NormalC name slots ->+        [DataCon name [] [] (map (\(_, ty) -> (Nothing, ty)) slots)]+    RecC name fields ->+        [DataCon name [] [] (map (\(n, _, ty) -> (Just n, ty)) fields)]+    InfixC (_, ty1) name (_, ty2) ->+        [DataCon name [] [] [(Nothing, ty1), (Nothing, ty2)]]+    ForallC tvs preds con ->+        map (\(DataCon name tvs0 preds0 fields) ->+            DataCon name (tvs0 ++ map tyVarBndrName tvs) (preds0 ++ preds) fields) (conToDataCons con)+#if MIN_VERSION_template_haskell(2,11,0)+    GadtC ns slots _ ->+        map (\dn -> DataCon dn [] [] (map (\(_, ty) -> (Nothing, ty)) slots)) ns+    RecGadtC ns fields _ ->+        map (\dn -> DataCon dn [] [] (map (\(fn, _, ty) -> (Just fn, ty)) fields)) ns+#endif++-- | Reify the given data or newtype declaration, and yields its+-- 'DataType' representation.+reifyDataType :: Name -> Q DataType+reifyDataType name = do+    info <- reify name+    case infoToDataType info of+        Nothing -> fail $ "Expected to reify a datatype. Instead got:\n" ++ pprint info+        Just x -> return x++infoToDataType :: Info -> Maybe DataType+infoToDataType info = case info of+#if MIN_VERSION_template_haskell(2,11,0)+    TyConI (DataD preds name tvs _kind cons _deriving) ->+#else+    TyConI (DataD preds name tvs cons _deriving) ->+#endif+        Just $ DataType name (map tyVarBndrName tvs) preds (concatMap conToDataCons cons)+#if MIN_VERSION_template_haskell(2,11,0)+    TyConI (NewtypeD preds name tvs _kind con _deriving) ->+#else+    TyConI (NewtypeD preds name tvs con _deriving) ->+#endif+        Just $ DataType name (map tyVarBndrName tvs) preds (conToDataCons con)+    _ -> Nothing++--------------------------------------------------------------------------------+-- Helpers+--------------------------------------------------------------------------------++type Field = (Maybe Name, Type)++_arr :: Name+_arr = mkName "arr"++_tag :: Name+_tag = mkName "tag"++_initialOffset :: Name+_initialOffset = mkName "initialOffset"++_val :: Name+_val = mkName "val"++mkOffsetName :: Int -> Name+mkOffsetName i = mkName ("offset" ++ show i)++mkFieldName :: Int -> Name+mkFieldName i = mkName ("field" ++ show i)++--------------------------------------------------------------------------------+-- Domain specific helpers+--------------------------------------------------------------------------------++exprGetSize :: Type -> Q Exp+exprGetSize ty = appE (varE 'sizeOf) [|Proxy :: Proxy $(pure ty)|]++getTagSize :: Int -> Int+getTagSize numConstructors+    | numConstructors == 1 = 0+    | fromIntegral (maxBound :: Word8) >= numConstructors = 1+    | fromIntegral (maxBound :: Word16) >= numConstructors = 2+    | fromIntegral (maxBound :: Word32) >= numConstructors = 4+    | fromIntegral (maxBound :: Word64) >= numConstructors = 8+    | otherwise = error "Too many constructors"++getTagType :: Int -> Name+getTagType numConstructors+    | numConstructors == 1 = error "No tag for 1 constructor"+    | fromIntegral (maxBound :: Word8) >= numConstructors = ''Word8+    | fromIntegral (maxBound :: Word16) >= numConstructors = ''Word16+    | fromIntegral (maxBound :: Word32) >= numConstructors = ''Word32+    | fromIntegral (maxBound :: Word64) >= numConstructors = ''Word64+    | otherwise = error "Too many constructors"++mkOffsetDecls :: Int -> [Field] -> [Q Dec]+mkOffsetDecls tagSize fields =+    init+        ((:) (valD+                  (varP (mkOffsetName 0))+                  (normalB+                       [|$(litE (IntegerL (fromIntegral tagSize))) ++                         $(varE _initialOffset)|])+                  [])+             (map mkOffsetExpr (zip [1 ..] fields)))++    where++    mkOffsetExpr (i, (_, ty)) =+        valD+            (varP (mkOffsetName i))+            (normalB [|$(varE (mkOffsetName (i - 1))) + $(exprGetSize ty)|])+            []++--------------------------------------------------------------------------------+-- Size+--------------------------------------------------------------------------------++isUnitType :: [DataCon] -> Bool+isUnitType [DataCon _ _ _ []] = True+isUnitType _ = False++mkSizeOfExpr :: Type -> [DataCon] -> Q Exp+mkSizeOfExpr headTy constructors =+    case constructors of+        [] ->+            [|error+                  ("Attempting to get size with no constructors (" +++                   $(lift (pprint headTy)) ++ ")")|]+        -- One constructor with no fields is a unit type. Size of a unit type is+        -- 1.+        [con@(DataCon _ _ _ fields)] ->+            case fields of+                [] -> litE (IntegerL 1)+                _ -> [|$(sizeOfConstructor con)|]+        _ -> [|$(litE (IntegerL (fromIntegral tagSize))) + $(sizeOfHeadDt)|]++    where++    tagSize = getTagSize (length constructors)+    sizeOfField (_, ty) = exprGetSize ty+    sizeOfConstructor (DataCon _ _ _ fields) =+        appE (varE 'sum) (listE (map sizeOfField fields))+    -- The size of any Unbox type is atleast 1+    sizeOfHeadDt =+        appE (varE 'maximum) (listE (map sizeOfConstructor constructors))++--------------------------------------------------------------------------------+-- Peek+--------------------------------------------------------------------------------++mkPeekExprOne :: Int -> DataCon -> Q Exp+mkPeekExprOne tagSize (DataCon cname _ _ fields) =+    case fields of+        [] -> [|pure $(conE cname)|]+        _ ->+            letE+                (mkOffsetDecls tagSize fields)+                (foldl+                     (\acc i -> [|$(acc) <*> $(peekField i)|])+                     [|$(conE cname) <$> $(peekField 0)|]+                     [1 .. (length fields - 1)])++    where++    peekField i = [|peekAt $(varE (mkOffsetName i)) $(varE _arr)|]++mkPeekExpr :: Type -> [DataCon] -> Q Exp+mkPeekExpr headTy cons =+    case cons of+        [] ->+            [|error+                  ("Attempting to peek type with no constructors (" +++                   $(lift (pprint headTy)) ++ ")")|]+        [con] -> mkPeekExprOne 0 con+        _ ->+            doE+                [ bindS+                      (varP _tag)+                      [|peekAt $(varE _initialOffset) $(varE _arr)|]+                , noBindS+                      (caseE+                           (sigE (varE _tag) (conT tagType))+                           (map peekMatch (zip [0 ..] cons) ++ [peekErr]))+                ]++    where++    lenCons = length cons+    tagType = getTagType lenCons+    tagSize = getTagSize lenCons+    peekMatch (i, con) =+        match (litP (IntegerL i)) (normalB (mkPeekExprOne tagSize con)) []+    peekErr =+        match+            wildP+            (normalB+                 [|error+                       ("Found invalid tag while peeking (" +++                        $(lift (pprint headTy)) ++ ")")|])+            []++--------------------------------------------------------------------------------+-- Poke+--------------------------------------------------------------------------------++mkPokeExprTag :: Name -> Int -> Q Exp+mkPokeExprTag tagType tagVal = pokeTag++    where++    pokeTag =+        [|pokeAt+              $(varE _initialOffset)+              $(varE _arr)+              $((sigE (litE (IntegerL (fromIntegral tagVal))) (conT tagType)))|]++mkPokeExprFields :: Int -> [Field] -> Q Exp+mkPokeExprFields tagSize fields = do+    case fields of+        [] -> [|pure ()|]+        _ ->+            letE+                (mkOffsetDecls tagSize fields)+                (doE $ map (noBindS . pokeField) [0 .. (numFields - 1)])++    where++    numFields = length fields+    pokeField i =+        [|pokeAt+              $(varE (mkOffsetName i))+              $(varE _arr)+              $(varE (mkFieldName i))|]++mkPokeMatch :: Name -> Int -> Q Exp -> Q Match+mkPokeMatch cname numFields exp0 =+    match+        (conP cname (map varP (map mkFieldName [0 .. (numFields - 1)])))+        (normalB exp0)+        []++mkPokeExpr :: Type -> [DataCon] -> Q Exp+mkPokeExpr headTy cons =+    case cons of+        [] ->+            [|error+                  ("Attempting to poke type with no constructors (" +++                   $(lift (pprint headTy)) ++ ")")|]+        [(DataCon _ _ _ [])] -> [|pure ()|]+        [(DataCon cname _ _ fields)] ->+            caseE+                (varE _val)+                [mkPokeMatch cname (length fields) (mkPokeExprFields 0 fields)]+        _ ->+            caseE+                (varE _val)+                (map (\(tagVal, (DataCon cname _ _ fields)) ->+                          mkPokeMatch+                              cname+                              (length fields)+                              (doE [ noBindS $ mkPokeExprTag tagType tagVal+                                   , noBindS $ mkPokeExprFields tagSize fields+                                   ]))+                     (zip [0 ..] cons))++    where++    lenCons = length cons+    tagType = getTagType lenCons+    tagSize = getTagSize lenCons++--------------------------------------------------------------------------------+-- Main+--------------------------------------------------------------------------------++-- | A general function to derive Unbox instances where you can control which+-- Constructors of the datatype to consider and what the Context for the 'Unbox'+-- instance would be.+--+-- Consider the datatype:+-- @+-- data CustomDataType a b+--     = CDTConstructor1+--     | CDTConstructor2 Bool+--     | CDTConstructor3 Bool b+--     deriving (Show, Eq)+-- @+--+-- Usage:+-- @+-- $(deriveUnboxInternal+--       [AppT (ConT ''Unbox) (VarT (mkName "b"))]+--       (AppT+--            (AppT (ConT ''CustomDataType) (VarT (mkName "a")))+--            (VarT (mkName "b")))+--       [ DataCon 'CDTConstructor1 [] [] []+--       , DataCon 'CDTConstructor2 [] [] [(Nothing, (ConT ''Bool))]+--       , DataCon+--             'CDTConstructor3+--             []+--             []+--             [(Nothing, (ConT ''Bool)), (Nothing, (VarT (mkName "b")))]+--       ])+-- @+deriveUnboxInternal :: Type -> [DataCon] -> ([Dec] -> Q [Dec]) -> Q [Dec]+deriveUnboxInternal headTy cons mkDec = do+    sizeOfMethod <- mkSizeOfExpr headTy cons+    peekMethod <- mkPeekExpr headTy cons+    pokeMethod <- mkPokeExpr headTy cons+    let methods =+            -- INLINE on sizeOf actually worsens some benchmarks, and improves+            -- none+            [ -- PragmaD (InlineP 'sizeOf Inline FunLike AllPhases)+              FunD 'sizeOf [Clause [WildP] (NormalB sizeOfMethod) []]+            , PragmaD (InlineP 'peekAt Inline FunLike AllPhases)+            , FunD+                  'peekAt+                  [ Clause+                        (if isUnitType cons+                             then [WildP, WildP]+                             else [VarP _initialOffset, VarP _arr])+                        (NormalB peekMethod)+                        []+                  ]+            , PragmaD (InlineP 'pokeAt Inline FunLike AllPhases)+            , FunD+                  'pokeAt+                  [ Clause+                        (if isUnitType cons+                             then [WildP, WildP, WildP]+                             else [VarP _initialOffset, VarP _arr, VarP _val])+                        (NormalB pokeMethod)+                        []+                  ]+            ]+    mkDec methods++-- | Given an 'Unbox' instance declaration splice without the methods (e.g.+-- @[d|instance Unbox a => Unbox (Maybe a)|]@), generate an instance+-- declaration including all the type class method implementations.+--+-- Usage:+--+-- @+-- \$(deriveUnbox [d|instance Unbox a => Unbox (Maybe a)|])+-- @+deriveUnbox :: Q [Dec] -> Q [Dec]+deriveUnbox mDecs = do+    dec <- mDecs+    case dec of+        [InstanceD mo preds headTyWC []] -> do+            let headTy = unwrap dec headTyWC+                (mainTyName, subs) = getMainTypeName dec headTy+            dt <- reifyDataType mainTyName+            let tyVars = dtTvs dt+                mapper = mapperWith (VarT <$> tyVars) subs+                cons = map (modifyConVariables mapper) (dtCons dt)+            deriveUnboxInternal headTy cons (mkInst mo preds headTyWC)+        _ -> errorMessage dec++    where++    mapperWith l1 l2 a =+        case elemIndex a l1 of+            Nothing -> a+            -- XXX Capture this case and give a relavant error.+            Just i -> l2 !! i++    mapType f (AppT t1 t2) = AppT (mapType f t1) (mapType f t2)+    mapType f (InfixT t1 n t2) = InfixT (mapType f t1) n (mapType f t2)+    mapType f (UInfixT t1 n t2) = UInfixT (mapType f t1) n (mapType f t2)+    mapType f (ParensT t) = ParensT (mapType f t)+    mapType f v = f v++    modifyConVariables f con =+        con { dcFields = map (\(a, b) -> (a, mapType f b)) (dcFields con) }++    mkInst mo preds headTyWC methods =+        pure [InstanceD mo preds headTyWC methods]++    errorMessage dec =+        error $ unlines+            [ "Error: deriveUnbox:"+            , ""+            , ">> " ++ pprint dec+            , ""+            , "The supplied declaration not a valid instance declaration."+            , "Provide a valid Haskell instance declaration without a body."+            , ""+            , "Examples:"+            , "instance Unbox (Proxy a)"+            , "instance Unbox a => Unbox (Identity a)"+            , "instance Unbox (TableT Identity)"+            ]++    unwrap _ (AppT (ConT _) r) = r+    unwrap dec _ = errorMessage dec++    getMainTypeName dec = go []++        where++        go xs (ConT nm) = (nm, xs)+        go xs (AppT l r) = go (r:xs) l+        go _ _ = errorMessage dec
− src/Streamly/Internal/Data/Unboxed.hs
@@ -1,855 +0,0 @@-{-# LANGUAGE DataKinds #-}-{-# LANGUAGE DefaultSignatures #-}-{-# LANGUAGE UnboxedTuples #-}-{-# LANGUAGE UndecidableInstances #-}---- | TODO: Implement TH based instance derivation for better performance.--module Streamly.Internal.Data.Unboxed-    ( Unbox(..)-    , peekWith-    , pokeWith-    , MutableByteArray(..)-    , touch-    , getMutableByteArray#-    , pin-    , unpin-    , newUnpinnedBytes-    , newPinnedBytes-    , newAlignedPinnedBytes-    , nil--    -- * Type Parser and Builder-    , BoundedPtr (..)--    , Peeker (..)-    , read-    , readUnsafe-    , skipByte-    , runPeeker--    , pokeBoundedPtrUnsafe-    , pokeBoundedPtr--    -- * Generic Unbox instances-    , genericSizeOf-    , genericPeekByteIndex-    , genericPokeByteIndex--    -- Classess used for generic deriving.-    , PeekRep(..)-    , PokeRep(..)-    , SizeOfRep(..)-    ) where--#include "MachDeps.h"-#include "ArrayMacros.h"--import Control.Monad (void, when)-import Data.Complex (Complex((:+)))-import Data.Functor ((<&>))-import Data.Functor.Const (Const(..))-import Data.Functor.Identity (Identity(..))-import Data.Kind (Type)-import Data.Proxy (Proxy (..))-import Foreign.Ptr (IntPtr(..), WordPtr(..))-import GHC.Base (IO(..))-import GHC.Fingerprint.Type (Fingerprint(..))-import GHC.Int (Int16(..), Int32(..), Int64(..), Int8(..))-import GHC.Real (Ratio(..))-import GHC.Stable (StablePtr(..))-import GHC.Word (Word16(..), Word32(..), Word64(..), Word8(..))-#if MIN_VERSION_base(4,15,0)-import GHC.RTS.Flags (IoSubSystem(..))-#endif-import Streamly.Internal.Data.Builder (Builder (..))-import System.IO.Unsafe (unsafePerformIO)--import GHC.Generics-import GHC.Exts-import GHC.TypeLits-import Prelude hiding (read)------------------------------------------------------------------------------------- The ArrayContents type------------------------------------------------------------------------------------- XXX can use UnliftedNewtypes-data MutableByteArray = MutableByteArray (MutableByteArray# RealWorld)--{-# INLINE getMutableByteArray# #-}-getMutableByteArray# :: MutableByteArray -> MutableByteArray# RealWorld-getMutableByteArray# (MutableByteArray mbarr) = mbarr--{-# INLINE touch #-}-touch :: MutableByteArray -> IO ()-touch (MutableByteArray contents) =-    IO $ \s -> case touch# contents s of s' -> (# s', () #)---- | Return the size of the array in bytes.-{-# INLINE sizeOfMutableByteArray #-}-sizeOfMutableByteArray :: MutableByteArray -> IO Int-sizeOfMutableByteArray (MutableByteArray arr) =-    IO $ \s ->-        case getSizeofMutableByteArray# arr s of-            (# s1, i #) -> (# s1, I# i #)------------------------------------------------------------------------------------- Creation-----------------------------------------------------------------------------------{-# NOINLINE nil #-}-nil :: MutableByteArray-nil = unsafePerformIO $ newUnpinnedBytes 0--{-# INLINE newUnpinnedBytes #-}-newUnpinnedBytes :: Int -> IO MutableByteArray-newUnpinnedBytes nbytes | nbytes < 0 =-  errorWithoutStackTrace "newUnpinnedBytes: size must be >= 0"-newUnpinnedBytes (I# nbytes) = IO $ \s ->-    case newByteArray# nbytes s of-        (# s', mbarr# #) ->-           let c = MutableByteArray mbarr#-            in (# s', c #)--{-# INLINE newPinnedBytes #-}-newPinnedBytes :: Int -> IO MutableByteArray-newPinnedBytes nbytes | nbytes < 0 =-  errorWithoutStackTrace "newPinnedBytes: size must be >= 0"-newPinnedBytes (I# nbytes) = IO $ \s ->-    case newPinnedByteArray# nbytes s of-        (# s', mbarr# #) ->-           let c = MutableByteArray mbarr#-            in (# s', c #)--{-# INLINE newAlignedPinnedBytes #-}-newAlignedPinnedBytes :: Int -> Int -> IO MutableByteArray-newAlignedPinnedBytes nbytes _align | nbytes < 0 =-  errorWithoutStackTrace "newAlignedPinnedBytes: size must be >= 0"-newAlignedPinnedBytes (I# nbytes) (I# align) = IO $ \s ->-    case newAlignedPinnedByteArray# nbytes align s of-        (# s', mbarr# #) ->-           let c = MutableByteArray mbarr#-            in (# s', c #)------------------------------------------------------------------------------------ Pinning & Unpinning----------------------------------------------------------------------------------{-# INLINE isPinned #-}-isPinned :: MutableByteArray -> Bool-isPinned (MutableByteArray arr#) =-    let pinnedInt = I# (isMutableByteArrayPinned# arr#)-     in pinnedInt == 1---{-# INLINE cloneMutableArrayWith# #-}-cloneMutableArrayWith#-    :: (Int# -> State# RealWorld -> (# State# RealWorld-                                     , MutableByteArray# RealWorld #))-    -> MutableByteArray# RealWorld-    -> State# RealWorld-    -> (# State# RealWorld, MutableByteArray# RealWorld #)-cloneMutableArrayWith# alloc# arr# s# =-    case getSizeofMutableByteArray# arr# s# of-        (# s1#, i# #) ->-            case alloc# i# s1# of-                (# s2#, arr1# #) ->-                    case copyMutableByteArray# arr# 0# arr1# 0# i# s2# of-                        s3# -> (# s3#, arr1# #)--{-# INLINE pin #-}-pin :: MutableByteArray -> IO MutableByteArray-pin arr@(MutableByteArray marr#) =-    if isPinned arr-    then return arr-    else IO-             $ \s# ->-                   case cloneMutableArrayWith# newPinnedByteArray# marr# s# of-                       (# s1#, marr1# #) -> (# s1#, MutableByteArray marr1# #)--{-# INLINE unpin #-}-unpin :: MutableByteArray -> IO MutableByteArray-unpin arr@(MutableByteArray marr#) =-    if not (isPinned arr)-    then return arr-    else IO-             $ \s# ->-                   case cloneMutableArrayWith# newByteArray# marr# s# of-                       (# s1#, marr1# #) -> (# s1#, MutableByteArray marr1# #)------------------------------------------------------------------------------------- The Unbox type class------------------------------------------------------------------------------------- XXX generate error if the size is < 1---- In theory we could convert a type to and from a byte stream and use that--- to implement boxing, unboxing. But that would be inefficient. This type--- class allows each primitive type to have its own specific efficient--- implementation to read and write the type to memory.------ This is a typeclass that uses MutableByteArray but could use ForeignPtr or--- any other representation of memory. We could make this a multiparameter type--- class if necessary.------ If the type class would have to support reading and writing to a Ptr as well,--- basically what Storable does. We will also need to touch the anchoring ptr at--- the right points which is prone to errors. However, it should be simple to--- implement unmanaged/read-only memory arrays by allowing a Ptr type in--- ArrayContents, though it would require all instances to support reading from--- a Ptr.------ There is a reason for using byte offset instead of element index in read and--- write operations in the type class. If we use element index slicing of the--- array becomes rigid. We can only slice the array at addresses that are--- aligned with the type, therefore, we cannot slice at misaligned location and--- then cast the slice into another type which does not ncessarily align with--- the original type.------ As a side note, there seem to be no performance advantage of alignment--- anymore, see--- https://lemire.me/blog/2012/05/31/data-alignment-for-speed-myth-or-reality/------- The main goal of the Unbox type class is to be used in arrays. Invariants--- for the sizeOf value required for use in arrays:------ * size is independent of the value, it is determined by the type only. So--- that we can store values of the same type in fixed length array cells.--- * size cannot be zero. So that the length of an array storing the element--- and the number of elements can be related.------ Note, for general serializable types the size cannot be fixed e.g. we may--- want to serialize a list. This type class can be considered a special case--- of a more general serialization type class.---- | A type implementing the 'Unbox' interface supplies operations for reading--- and writing the type from and to a mutable byte array (an unboxed--- representation of the type) in memory. The read operation 'peekByteIndex'--- deserializes the boxed type from the mutable byte array. The write operation--- 'pokeByteIndex' serializes the boxed type to the mutable byte array.------ Instances can be derived via 'Generic'. Note that the data type must be--- non-recursive. Here is an example, for deriving an instance of this type--- class.------ >>> import GHC.Generics (Generic)--- >>> :{--- data Object = Object---     { _int0 :: Int---     , _int1 :: Int---     } deriving Generic--- :}------ WARNING! Generic deriving hangs for recursive data types.------ >>> import Streamly.Data.Array (Unbox(..))--- >>> instance Unbox Object------ If you want to write the instance manually:------ >>> :{--- instance Unbox Object where---     sizeOf _ = 16---     peekByteIndex i arr = do---         x0 <- peekByteIndex i arr---         x1 <- peekByteIndex (i + 8) arr---         return $ Object x0 x1---     pokeByteIndex i arr (Object x0 x1) = do---         pokeByteIndex i arr x0---         pokeByteIndex (i + 8) arr x1--- :}----class Unbox a where-    -- | Get the size. Size cannot be zero.-    sizeOf :: Proxy a -> Int--    default sizeOf :: (SizeOfRep (Rep a)) => Proxy a -> Int-    sizeOf = genericSizeOf--    -- | Read an element of type "a" from a MutableByteArray given the byte-    -- index.-    ---    -- IMPORTANT: The implementation of this interface may not check the bounds-    -- of the array, the caller must not assume that.-    peekByteIndex :: Int -> MutableByteArray -> IO a--    default peekByteIndex :: (Generic a, PeekRep (Rep a)) =>-         Int -> MutableByteArray -> IO a-    peekByteIndex i arr = genericPeekByteIndex arr i--    -- | Write an element of type "a" to a MutableByteArray given the byte-    -- index.-    ---    -- IMPORTANT: The implementation of this interface may not check the bounds-    -- of the array, the caller must not assume that.-    pokeByteIndex :: Int -> MutableByteArray -> a -> IO ()--    default pokeByteIndex :: (Generic a, PokeRep (Rep a)) =>-        Int -> MutableByteArray -> a -> IO ()-    pokeByteIndex i arr = genericPokeByteIndex arr i--#define DERIVE_UNBOXED(_type, _constructor, _readArray, _writeArray, _sizeOf) \-instance Unbox _type where {                                         \-; {-# INLINE peekByteIndex #-}                                       \-; peekByteIndex (I# n) (MutableByteArray mbarr) = IO $ \s ->         \-      case _readArray mbarr n s of                                   \-          { (# s1, i #) -> (# s1, _constructor i #) }                \-; {-# INLINE pokeByteIndex #-}                                       \-; pokeByteIndex (I# n) (MutableByteArray mbarr) (_constructor val) = \-        IO $ \s -> (# _writeArray mbarr n val s, () #)               \-; {-# INLINE sizeOf #-}                                              \-; sizeOf _ = _sizeOf                                                 \-}--#define DERIVE_WRAPPED_UNBOX(_constraint, _type, _constructor, _innerType)    \-instance _constraint Unbox _type where                                        \-; {-# INLINE peekByteIndex #-}                                                \-; peekByteIndex i arr = _constructor <$> peekByteIndex i arr                  \-; {-# INLINE pokeByteIndex #-}                                                \-; pokeByteIndex i arr (_constructor a) = pokeByteIndex i arr a                \-; {-# INLINE sizeOf #-}                                                       \-; sizeOf _ = SIZE_OF(_innerType)--#define DERIVE_BINARY_UNBOX(_constraint, _type, _constructor, _innerType) \-instance _constraint Unbox _type where {                                  \-; {-# INLINE peekByteIndex #-}                                            \-; peekByteIndex i arr =                                                   \-      peekByteIndex i arr >>=                                             \-        (\p1 -> peekByteIndex (i + SIZE_OF(_innerType)) arr               \-            <&> _constructor p1)                                          \-; {-# INLINE pokeByteIndex #-}                                            \-; pokeByteIndex i arr (_constructor p1 p2) =                              \-      pokeByteIndex i arr p1 >>                                           \-        pokeByteIndex (i + SIZE_OF(_innerType)) arr p2                    \-; {-# INLINE sizeOf #-}                                                   \-; sizeOf _ = 2 * SIZE_OF(_innerType)                                      \-}------------------------------------------------------------------------------------ Unbox instances for primitive types----------------------------------------------------------------------------------DERIVE_UNBOXED( Char-              , C#-              , readWord8ArrayAsWideChar#-              , writeWord8ArrayAsWideChar#-              , SIZEOF_HSCHAR)--DERIVE_UNBOXED( Int8-              , I8#-              , readInt8Array#-              , writeInt8Array#-              , 1)--DERIVE_UNBOXED( Int16-              , I16#-              , readWord8ArrayAsInt16#-              , writeWord8ArrayAsInt16#-              , 2)--DERIVE_UNBOXED( Int32-              , I32#-              , readWord8ArrayAsInt32#-              , writeWord8ArrayAsInt32#-              , 4)--DERIVE_UNBOXED( Int-              , I#-              , readWord8ArrayAsInt#-              , writeWord8ArrayAsInt#-              , SIZEOF_HSINT)--DERIVE_UNBOXED( Int64-              , I64#-              , readWord8ArrayAsInt64#-              , writeWord8ArrayAsInt64#-              , 8)--DERIVE_UNBOXED( Word-              , W#-              , readWord8ArrayAsWord#-              , writeWord8ArrayAsWord#-              , SIZEOF_HSWORD)--DERIVE_UNBOXED( Word8-              , W8#-              , readWord8Array#-              , writeWord8Array#-              , 1)--DERIVE_UNBOXED( Word16-              , W16#-              , readWord8ArrayAsWord16#-              , writeWord8ArrayAsWord16#-              , 2)--DERIVE_UNBOXED( Word32-              , W32#-              , readWord8ArrayAsWord32#-              , writeWord8ArrayAsWord32#-              , 4)--DERIVE_UNBOXED( Word64-              , W64#-              , readWord8ArrayAsWord64#-              , writeWord8ArrayAsWord64#-              , 8)--DERIVE_UNBOXED( Double-              , D#-              , readWord8ArrayAsDouble#-              , writeWord8ArrayAsDouble#-              , SIZEOF_HSDOUBLE)--DERIVE_UNBOXED( Float-              , F#-              , readWord8ArrayAsFloat#-              , writeWord8ArrayAsFloat#-              , SIZEOF_HSFLOAT)------------------------------------------------------------------------------------ Unbox instances for derived types----------------------------------------------------------------------------------DERIVE_UNBOXED( (StablePtr a)-              , StablePtr-              , readWord8ArrayAsStablePtr#-              , writeWord8ArrayAsStablePtr#-              , SIZEOF_HSSTABLEPTR)--DERIVE_UNBOXED( (Ptr a)-              , Ptr-              , readWord8ArrayAsAddr#-              , writeWord8ArrayAsAddr#-              , SIZEOF_HSPTR)--DERIVE_UNBOXED( (FunPtr a)-              , FunPtr-              , readWord8ArrayAsAddr#-              , writeWord8ArrayAsAddr#-              , SIZEOF_HSFUNPTR)--DERIVE_WRAPPED_UNBOX(,IntPtr,IntPtr,Int)-DERIVE_WRAPPED_UNBOX(,WordPtr,WordPtr,Word)-DERIVE_WRAPPED_UNBOX(Unbox a =>,(Identity a),Identity,a)-#if MIN_VERSION_base(4,14,0)-DERIVE_WRAPPED_UNBOX(Unbox a =>,(Down a),Down,a)-#endif-DERIVE_WRAPPED_UNBOX(Unbox a =>,(Const a b),Const,a)-DERIVE_BINARY_UNBOX(forall a. Unbox a =>,(Complex a),(:+),a)-DERIVE_BINARY_UNBOX(forall a. Unbox a =>,(Ratio a),(:%),a)-DERIVE_BINARY_UNBOX(,Fingerprint,Fingerprint,Word64)--instance Unbox () where--    {-# INLINE peekByteIndex #-}-    peekByteIndex _ _ = return ()--    {-# INLINE pokeByteIndex #-}-    pokeByteIndex _ _ _ = return ()--    {-# INLINE sizeOf #-}-    sizeOf _ = 1--#if MIN_VERSION_base(4,15,0)-instance Unbox IoSubSystem where--    {-# INLINE peekByteIndex #-}-    peekByteIndex i arr = toEnum <$> peekByteIndex i arr--    {-# INLINE pokeByteIndex #-}-    pokeByteIndex i arr a = pokeByteIndex i arr (fromEnum a)--    {-# INLINE sizeOf #-}-    sizeOf _ = sizeOf (Proxy :: Proxy Int)-#endif--instance Unbox Bool where--    {-# INLINE peekByteIndex #-}-    peekByteIndex i arr = do-        res <- peekByteIndex i arr-        return $ res /= (0 :: Int8)--    {-# INLINE pokeByteIndex #-}-    pokeByteIndex i arr a =-        if a-        then pokeByteIndex i arr (1 :: Int8)-        else pokeByteIndex i arr (0 :: Int8)--    {-# INLINE sizeOf #-}-    sizeOf _ = 1------------------------------------------------------------------------------------- Functions-----------------------------------------------------------------------------------{-# INLINE peekWith #-}-peekWith :: Unbox a => MutableByteArray -> Int -> IO a-peekWith arr i = peekByteIndex i arr--{-# INLINE pokeWith #-}-pokeWith :: Unbox a => MutableByteArray -> Int -> a -> IO ()-pokeWith arr i = pokeByteIndex i arr------------------------------------------------------------------------------------- Generic deriving------------------------------------------------------------------------------------- Utilities to build or parse a type safely and easily.---- | A location inside a mutable byte array with the bound of the array. Is it--- cheaper to just get the bound using the size of the array whenever needed?-data BoundedPtr =-    BoundedPtr-        MutableByteArray          -- byte array-        Int                       -- current pos-        Int                       -- position after end------------------------------------------------------------------------------------- Peeker monad------------------------------------------------------------------------------------- | Chains peek functions that pass the current position to the next function-newtype Peeker a = Peeker (Builder BoundedPtr IO a)-    deriving (Functor, Applicative, Monad)--{-# INLINE readUnsafe #-}-readUnsafe :: Unbox a => Peeker a-readUnsafe = Peeker (Builder step)--    where--    {-# INLINE step #-}-    step :: forall a. Unbox a => BoundedPtr -> IO (BoundedPtr, a)-    step (BoundedPtr arr pos end) = do-        let next = pos + sizeOf (Proxy :: Proxy a)-        r <- peekByteIndex pos arr-        return (BoundedPtr arr next end, r)--{-# INLINE read #-}-read :: Unbox a => Peeker a-read = Peeker (Builder step)--    where--    {-# INLINE step #-}-    step :: forall a. Unbox a => BoundedPtr -> IO (BoundedPtr, a)-    step (BoundedPtr arr pos end) = do-        let next = pos + sizeOf (Proxy :: Proxy a)-        when (next > end) $ error "peekObject reading beyond limit"-        r <- peekByteIndex pos arr-        return (BoundedPtr arr next end, r)--{-# INLINE skipByte #-}-skipByte :: Peeker ()-skipByte = Peeker (Builder step)--    where--    {-# INLINE step #-}-    step :: BoundedPtr -> IO (BoundedPtr, ())-    step (BoundedPtr arr pos end) = do-        let next = pos + 1-        when (next > end)-            $ error $ "skipByte: reading beyond limit. next = "-                ++ show next-                ++ " end = " ++ show end-        return (BoundedPtr arr next end, ())--{-# INLINE runPeeker #-}-runPeeker :: Peeker a -> BoundedPtr -> IO a-runPeeker (Peeker (Builder f)) ptr = fmap snd (f ptr)------------------------------------------------------------------------------------- Poke utilities------------------------------------------------------------------------------------- XXX Using a Poker monad may be useful when we have to compute the size to be--- poked as we go and then poke the size at a previous location. For variable--- sized object serialization we may also want to reallocate the array and--- return the newly allocated array in the output.---- Does not check writing beyond bound.-{-# INLINE pokeBoundedPtrUnsafe #-}-pokeBoundedPtrUnsafe :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr-pokeBoundedPtrUnsafe a (BoundedPtr arr pos end) = do-    let next = pos + sizeOf (Proxy :: Proxy a)-    pokeByteIndex pos arr a-    return (BoundedPtr arr next end)--{-# INLINE pokeBoundedPtr #-}-pokeBoundedPtr :: forall a. Unbox a => a -> BoundedPtr -> IO BoundedPtr-pokeBoundedPtr a (BoundedPtr arr pos end) = do-    let next = pos + sizeOf (Proxy :: Proxy a)-    when (next > end) $ error "pokeBoundedPtr writing beyond limit"-    pokeByteIndex pos arr a-    return (BoundedPtr arr next end)------------------------------------------------------------------------------------- Check the number of constructors in a sum type------------------------------------------------------------------------------------- Count the constructors of a sum type.-type family SumArity (a :: Type -> Type) :: Nat where-    SumArity (C1 _ _) = 1-    -- Requires UndecidableInstances-    SumArity (f :+: g) = SumArity f + SumArity g--type family TypeErrorMessage (a :: Symbol) :: Constraint where-    TypeErrorMessage a = TypeError ('Text a)--type family ArityCheck (b :: Bool) :: Constraint where-    ArityCheck 'True = ()-    ArityCheck 'False = TypeErrorMessage-        "Generic Unbox deriving does not support > 256 constructors."---- Type constraint to restrict the sum type arity so that the constructor tag--- can fit in a single byte.-type MaxArity256 n = ArityCheck (n <=? 255)------------------------------------------------------------------------------------- Generic Deriving of Unbox instance------------------------------------------------------------------------------------- Unbox uses fixed size encoding, therefore, when a (sum) type has multiple--- constructors, the size of the type is computed as the maximum required by--- any constructor. Therefore, size is independent of the value, it can be--- determined solely by the type.---- | Implementation of sizeOf that works on the generic representation of an--- ADT.-class SizeOfRep (f :: Type -> Type) where-    sizeOfRep :: f x -> Int---- Meta information wrapper, go inside-instance SizeOfRep f => SizeOfRep (M1 i c f) where-    {-# INLINE sizeOfRep #-}-    sizeOfRep _ = sizeOfRep (undefined :: f x)---- Primitive type "a".-instance Unbox a => SizeOfRep (K1 i a) where-    {-# INLINE sizeOfRep #-}-    sizeOfRep _ = sizeOf (Proxy :: Proxy a)---- Void: data type without constructors. Values of this type cannot exist,--- therefore the size is undefined. We should never be serializing structures--- with elements of this type.-instance SizeOfRep V1 where-    {-# INLINE sizeOfRep #-}-    sizeOfRep = error "sizeOfRep: a value of a Void type must not exist"---- Note that when a sum type has many unit constructors only a single byte is--- required to encode the type as only the constructor tag is stored.-instance SizeOfRep U1 where-    {-# INLINE sizeOfRep #-}-    sizeOfRep _ = 0---- Product type-instance (SizeOfRep f, SizeOfRep g) => SizeOfRep (f :*: g) where-    {-# INLINE sizeOfRep #-}-    sizeOfRep _ = sizeOfRep (undefined :: f x) + sizeOfRep (undefined :: g x)-----------------------------------------------------------------------------------class SizeOfRepSum (f :: Type -> Type) where-    sizeOfRepSum :: f x -> Int---- Constructor-instance SizeOfRep a => SizeOfRepSum (C1 c a) where-    {-# INLINE sizeOfRepSum #-}-    sizeOfRepSum = sizeOfRep--instance (SizeOfRepSum f, SizeOfRepSum g) => SizeOfRepSum (f :+: g) where-    {-# INLINE sizeOfRepSum #-}-    sizeOfRepSum _ =-        max (sizeOfRepSum (undefined :: f x)) (sizeOfRepSum (undefined :: g x))-----------------------------------------------------------------------------------instance (MaxArity256 (SumArity (f :+: g)), SizeOfRepSum f, SizeOfRepSum g) =>-    SizeOfRep (f :+: g) where--    -- The size of a sum type is the max of any of the constructor size.-    -- sizeOfRepSum type class operation is used here instead of sizeOfRep so-    -- that we add the constructor index byte only for the first time and avoid-    -- including it for the subsequent sum constructors.-    {-# INLINE sizeOfRep #-}-    sizeOfRep _ =-        -- One byte for the constructor id and then the constructor value.-        sizeOf (Proxy :: Proxy Word8) +-            max (sizeOfRepSum (undefined :: f x))-                (sizeOfRepSum (undefined :: g x))---- Unit: constructors without arguments.--- Theoretically the size can be 0, but we use 1 to simplify the implementation--- of an array of unit type elements. With a non-zero size we can count the number--- of elements in the array based on the size of the array. Otherwise we will--- have to store a virtual length in the array, but keep the physical size of--- the array as 0. Or we will have to make a special handling for zero sized--- elements to make the size as 1. Or we can disallow arrays with elements--- having size 0.----{-# INLINE genericSizeOf #-}-genericSizeOf :: forall a. (SizeOfRep (Rep a)) => Proxy a -> Int-genericSizeOf _ =-    let s = sizeOfRep (undefined :: Rep a x)-      in if s == 0 then 1 else s------------------------------------------------------------------------------------- Generic poke-----------------------------------------------------------------------------------class PokeRep (f :: Type -> Type) where-    pokeRep :: f a -> BoundedPtr -> IO BoundedPtr--instance PokeRep f => PokeRep (M1 i c f) where-    {-# INLINE pokeRep #-}-    pokeRep f = pokeRep (unM1 f)--instance Unbox a => PokeRep (K1 i a) where-    {-# INLINE pokeRep #-}-    pokeRep a = pokeBoundedPtr (unK1 a)--instance PokeRep V1 where-    {-# INLINE pokeRep #-}-    pokeRep = error "pokeRep: a value of a Void type should not exist"--instance PokeRep U1 where-    {-# INLINE pokeRep #-}-    pokeRep _ x = pure x--instance (PokeRep f, PokeRep g) => PokeRep (f :*: g) where-    {-# INLINE pokeRep #-}-    pokeRep (f :*: g) ptr = pokeRep f ptr >>= pokeRep g-----------------------------------------------------------------------------------class KnownNat n => PokeRepSum (n :: Nat) (f :: Type -> Type) where-    -- "n" is the constructor tag to be poked.-    pokeRepSum :: Proxy n -> f a -> BoundedPtr -> IO BoundedPtr--instance (KnownNat n, PokeRep a) => PokeRepSum n (C1 c a) where-    {-# INLINE pokeRepSum #-}-    pokeRepSum _ x ptr = do-        pokeBoundedPtr (fromInteger (natVal (Proxy :: Proxy n)) :: Word8) ptr-            >>= pokeRep x--instance (KnownNat n, PokeRepSum n f, PokeRepSum (n + SumArity f) g)-         => PokeRepSum n (f :+: g) where-    {-# INLINE pokeRepSum #-}-    pokeRepSum _ (L1 x) ptr =-        pokeRepSum (Proxy :: Proxy n) x ptr-    pokeRepSum _ (R1 x) ptr =-        pokeRepSum (Proxy :: Proxy (n + SumArity f)) x ptr-----------------------------------------------------------------------------------instance (MaxArity256 (SumArity (f :+: g)), PokeRepSum 0 (f :+: g)) =>-    PokeRep (f :+: g) where--    {-# INLINE pokeRep #-}-    pokeRep = pokeRepSum (Proxy :: Proxy 0)--{-# INLINE genericPokeObject #-}-genericPokeObject :: (Generic a, PokeRep (Rep a)) =>-    a -> BoundedPtr -> IO BoundedPtr-genericPokeObject a = pokeRep (from a)--genericPokeObj :: (Generic a, PokeRep (Rep a)) => a -> BoundedPtr -> IO ()-genericPokeObj a ptr = void $ genericPokeObject a ptr--{-# INLINE genericPokeByteIndex #-}-genericPokeByteIndex :: (Generic a, PokeRep (Rep a)) =>-    MutableByteArray -> Int -> a -> IO ()-genericPokeByteIndex arr index x = do-    -- XXX Should we use unsafe poke?-    end <- sizeOfMutableByteArray arr-    genericPokeObj x (BoundedPtr arr index end)------------------------------------------------------------------------------------- Generic peek-----------------------------------------------------------------------------------class PeekRep (f :: Type -> Type) where-    peekRep :: Peeker (f x)--instance PeekRep f => PeekRep (M1 i c f) where-    {-# INLINE peekRep #-}-    peekRep = fmap M1 peekRep--instance Unbox a => PeekRep (K1 i a) where-    {-# INLINE peekRep #-}-    peekRep = fmap K1 read--instance PeekRep V1 where-    {-# INLINE peekRep #-}-    peekRep = error "peekRep: a value of a Void type should not exist"--instance PeekRep U1 where-    {-# INLINE peekRep #-}-    peekRep = pure U1--instance (PeekRep f, PeekRep g) => PeekRep (f :*: g) where-    {-# INLINE peekRep #-}-    peekRep = (:*:) <$> peekRep <*> peekRep-----------------------------------------------------------------------------------class KnownNat n => PeekRepSum (n :: Nat) (f :: Type -> Type) where-    -- "n" is the constructor tag to be matched.-    peekRepSum :: Proxy n -> Word8 -> Peeker (f a)--instance (KnownNat n, PeekRep a) => PeekRepSum n (C1 c a) where-    {-# INLINE peekRepSum #-}-    peekRepSum _ tag-        | tag == curTag = peekRep-        | tag > curTag =-            error $ "Unbox instance peek: Constructor tag index "-                ++ show tag ++ " out of range, max tag index is "-                ++ show curTag-        | otherwise = error "peekRepSum: bug"--        where--        curTag = fromInteger (natVal (Proxy :: Proxy n))--instance (KnownNat n, PeekRepSum n f, PeekRepSum (n + SumArity f) g)-         => PeekRepSum n (f :+: g) where-    {-# INLINE peekRepSum #-}-    peekRepSum curProxy tag-        | tag < firstRightTag =-            L1 <$> peekRepSum curProxy tag-        | otherwise =-            R1 <$> peekRepSum (Proxy :: Proxy (n + SumArity f)) tag--        where--        firstRightTag = fromInteger (natVal (Proxy :: Proxy (n + SumArity f)))-----------------------------------------------------------------------------------instance (MaxArity256 (SumArity (f :+: g)), PeekRepSum 0 (f :+: g))-         => PeekRep (f :+: g) where-    {-# INLINE peekRep #-}-    peekRep = do-        tag <- read-        peekRepSum (Proxy :: Proxy 0) tag--{-# INLINE genericPeeker #-}-genericPeeker :: (Generic a, PeekRep (Rep a)) => Peeker a-genericPeeker = to <$> peekRep--{-# INLINE genericPeekBoundedPtr #-}-genericPeekBoundedPtr :: (Generic a, PeekRep (Rep a)) => BoundedPtr -> IO a-genericPeekBoundedPtr = runPeeker genericPeeker--{-# INLINE genericPeekByteIndex #-}-genericPeekByteIndex :: (Generic a, PeekRep (Rep a)) =>-    MutableByteArray -> Int -> IO a-genericPeekByteIndex arr index = do-    -- XXX Should we use unsafe peek?-    end <- sizeOfMutableByteArray arr-    genericPeekBoundedPtr (BoundedPtr arr index end)
src/Streamly/Internal/Data/Unfold.hs view
@@ -16,28 +16,16 @@     -- $setup      -- * Unfold Type-      Step(..)-    , Unfold+      module Streamly.Internal.Data.Unfold.Type      -- * Unfolds     -- One to one correspondence with     -- "Streamly.Internal.Data.Stream.Generate"     -- ** Basic Constructors-    , mkUnfoldM-    , mkUnfoldrM-    , unfoldrM-    , unfoldr-    , functionM-    , function-    , identity     , nilM     , nil     , consM -    -- ** From Values-    , fromEffect-    , fromPure-     -- ** Generators     -- | Generate a monadic stream from a seed.     , repeatM@@ -46,39 +34,9 @@     , iterateM      -- ** Enumerations-    , Enumerable (..)--    -- ** Enumerate Num-    , enumerateFromNum-    , enumerateFromThenNum-    , enumerateFromStepNum--    -- ** Enumerating 'Bounded 'Integral' Types-    , enumerateFromIntegralBounded-    , enumerateFromThenIntegralBounded-    , enumerateFromToIntegralBounded-    , enumerateFromThenToIntegralBounded--    -- ** Enumerating 'Unounded Integral' Types-    , enumerateFromIntegral-    , enumerateFromThenIntegral-    , enumerateFromToIntegral-    , enumerateFromThenToIntegral--    -- ** Enumerating 'Small Integral' Types-    , enumerateFromSmallBounded-    , enumerateFromThenSmallBounded-    , enumerateFromToSmall-    , enumerateFromThenToSmall--    -- ** Enumerating 'Fractional' Types-    , enumerateFromFractional-    , enumerateFromThenFractional-    , enumerateFromToFractional-    , enumerateFromThenToFractional+    , module Streamly.Internal.Data.Unfold.Enumeration      -- ** From Containers-    , fromList     , fromListM      -- ** From Memory@@ -91,11 +49,6 @@      -- * Combinators     -- ** Mapping on Input-    , lmap-    , lmapM-    , both-    , first-    , second     , discardFirst     , discardSecond     , swap@@ -110,11 +63,6 @@     -- input, but that might make the common case more inconvenient.      -- ** Mapping on Output-    , map-    , map2-    , mapM-    , mapM2-     , postscanlM'     , postscan     , scan@@ -126,8 +74,6 @@     , either      -- ** Filtering-    , takeWhileM-    , takeWhile     , take     , filter     , filterM@@ -135,24 +81,9 @@     , dropWhile     , dropWhileM -    -- ** Zipping-    , zipWithM-    , zipWith-     -- ** Cross product-    , crossWithM-    , crossWith-    , cross     , joinInnerGeneric-    , crossApply -    -- ** Nesting-    , ConcatState (..)-    , many-    , many2-    , concatMapM-    , bind-     -- ** Resource Management     -- | 'bracket' is the most general resource management operation, all other     -- operations can be expressed using it. These functions have IO suffix@@ -189,14 +120,14 @@ import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Internal.Data.IOFinalizer     (newIOFinalizer, runIOFinalizer, clearingIOFinalizer)-import Streamly.Internal.Data.Stream.StreamD.Type (Stream(..), Step(..))+import Streamly.Internal.Data.Stream.Type (Stream(..)) 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.Stream.StreamD.Type as D-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K+import qualified Streamly.Internal.Data.Stream.Type as D+import qualified Streamly.Internal.Data.StreamK.Type as K import qualified Prelude  import Streamly.Internal.Data.Unfold.Enumeration@@ -210,6 +141,10 @@  #include "DocTestDataUnfold.hs" +-------------------------------------------------------------------------------+-- Input operations+-------------------------------------------------------------------------------+ -- | Convert an 'Unfold' into an unfold accepting a tuple as an argument, -- using the argument of the original fold as the second element of tuple and -- discarding the first element of the tuple.@@ -270,7 +205,7 @@ -- {-# INLINE_NORMAL fold #-} fold :: Monad m => Fold m b c -> Unfold m a b -> a -> m c-fold (Fold fstep initial extract) (Unfold ustep inject) a = do+fold (Fold fstep initial _ final) (Unfold ustep inject) a = do     res <- initial     case res of         FL.Partial x -> inject a >>= go SPEC x@@ -288,7 +223,7 @@                     FL.Partial fs1 -> go SPEC fs1 s                     FL.Done c -> return c             Skip s -> go SPEC fs s-            Stop -> extract fs+            Stop -> final fs  -- {-# ANN type FoldMany Fuse #-} data FoldMany s fs b a@@ -303,7 +238,7 @@ -- /Pre-release/ {-# INLINE_NORMAL foldMany #-} foldMany :: Monad m => Fold m b c -> Unfold m a b -> Unfold m a c-foldMany (Fold fstep initial extract) (Unfold ustep inject1) =+foldMany (Fold fstep initial _ final) (Unfold ustep inject1) =     Unfold step inject      where@@ -334,14 +269,14 @@         case r of             Yield x s -> consume x s fs             Skip s -> return $ Skip (FoldManyFirst fs s)-            Stop -> return Stop+            Stop -> final fs >> return Stop     step (FoldManyLoop st fs) = do         r <- ustep st         case r of             Yield x s -> consume x s fs             Skip s -> return $ Skip (FoldManyLoop s fs)             Stop -> do-                b <- extract fs+                b <- final fs                 return $ Skip (FoldManyYield b FoldManyDone)     step (FoldManyYield b next) = return $ Yield b next     step FoldManyDone = return Stop@@ -382,7 +317,7 @@ -- /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) (Unfold stepU injectU) =+postscan (Fold stepF initial extract final) (Unfold stepU injectU) =     Unfold step inject      where@@ -405,7 +340,7 @@                         v <- extract fs1                         return $ Yield v (Just (fs1, s))             Skip s -> return $ Skip (Just (fs, s))-            Stop -> return Stop+            Stop -> final fs >> return Stop      step Nothing = return Stop @@ -413,7 +348,7 @@  {-# INLINE_NORMAL scanWith #-} scanWith :: Monad m => Bool -> Fold m b c -> Unfold m a b -> Unfold m a c-scanWith restart (Fold fstep initial extract) (Unfold stepU injectU) =+scanWith restart (Fold fstep initial extract final) (Unfold stepU injectU) =     Unfold step inject      where@@ -438,7 +373,7 @@         case res of             Yield x s -> runStep s (fstep fs x)             Skip s -> return $ Skip $ ScanDo s fs-            Stop -> return Stop+            Stop -> final fs >> return Stop     step ScanDone = return Stop  -- | Scan the output of an 'Unfold' to change it in a stateful manner.
src/Streamly/Internal/Data/Unfold/Enumeration.hs view
@@ -65,7 +65,6 @@ import Data.Word import Numeric.Natural import Data.Functor.Identity (Identity(..))-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..)) import Streamly.Internal.Data.Unfold.Type import Prelude        hiding (map, mapM, takeWhile, take, filter, const, zipWith@@ -88,7 +87,7 @@ -- the value overflows it keeps enumerating in a cycle: -- -- @--- >>> Stream.fold Fold.toList $ Stream.take 10 $ Stream.unfold Unfold.enumerateFromStepNum (255::Word8,1)+-- >>> Stream.toList $ Stream.take 10 $ Stream.unfold Unfold.enumerateFromStepNum (255::Word8,1) -- [255,0,1,2,3,4,5,6,7,8] -- -- @@@ -125,7 +124,7 @@ -- -- Example: -- @--- >>> Stream.fold Fold.toList $ Stream.take 10 $ Stream.unfold enumerateFromThenNum (255::Word8,0)+-- >>> Stream.toList $ Stream.take 10 $ Stream.unfold enumerateFromThenNum (255::Word8,0) -- [255,0,1,2,3,4,5,6,7,8] -- -- @@@ -152,7 +151,7 @@ -- -- @ -- >>> enumerateFromNum = lmap (\from -> (from, 1)) Unfold.enumerateFromStepNum--- >>> Stream.fold Fold.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9)+-- >>> Stream.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9) -- [0.9,1.9,2.9,3.9,4.9,5.9] -- -- @@@ -162,7 +161,7 @@ -- -- @ -- >>> enumerateFromNum = lmap (\from -> (from, from + 1)) Unfold.enumerateFromThenNum--- >>> Stream.fold Fold.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9)+-- >>> Stream.toList $ Stream.take 6 $ Stream.unfold enumerateFromNum (0.9) -- [0.9,1.9,2.9,3.8999999999999995,4.8999999999999995,5.8999999999999995] -- -- @@@ -284,7 +283,7 @@ -- specified upper limit rounded to the nearest integral value: -- -- @--- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromToFractional (0.1, 6.3)+-- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromToFractional (0.1, 6.3) -- [0.1,1.1,2.1,3.1,4.1,5.1,6.1] -- -- @@@ -389,24 +388,15 @@     -- @from@, enumerating up to 'maxBound' when the type is 'Bounded' or     -- generating an infinite stream when the type is not 'Bounded'.     ---    -- >>> import qualified Streamly.Data.Stream as Stream-    -- >>> import qualified Streamly.Internal.Data.Unfold as Unfold-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom (0 :: Int)+    -- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom (0 :: Int)     -- [0,1,2,3]     ---    -- @-    --     -- For 'Fractional' types, enumeration is numerically stable. However, no     -- overflow or underflow checks are performed.     ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom 1.1+    -- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFrom 1.1     -- [1.1,2.1,3.1,4.1]     ---    -- @-    --     -- /Pre-release/     --     enumerateFrom :: Monad m => Unfold m a a@@ -415,27 +405,18 @@     -- @from@, enumerating the type up to the value @to@. If @to@ is smaller than     -- @from@ then an empty stream is returned.     ---    -- >>> import qualified Streamly.Data.Stream as Stream-    -- >>> import qualified Streamly.Internal.Data.Unfold as Unfold-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromTo (0, 4)+    -- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromTo (0, 4)     -- [0,1,2,3,4]     ---    -- @-    --     -- For 'Fractional' types, the last element is equal to the specified @to@     -- value after rounding to the nearest integral value.     ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4)+    -- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4)     -- [1.1,2.1,3.1,4.1]     ---    -- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4.6)+    -- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromTo (1.1, 4.6)     -- [1.1,2.1,3.1,4.1,5.1]     ---    -- @-    --     -- /Pre-release/     enumerateFromTo :: Monad m => Unfold m (a, a) a @@ -445,18 +426,12 @@     -- after @from@. For 'Bounded' types the stream ends when 'maxBound' is     -- reached, for unbounded types it keeps enumerating infinitely.     ---    -- >>> import qualified Streamly.Data.Stream as Stream-    -- >>> import qualified Streamly.Internal.Data.Unfold as Unfold-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0, 2)+    -- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0, 2)     -- [0,2,4,6]     ---    -- >>> Stream.fold Fold.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0,(-2))+    -- >>> Stream.toList $ Stream.take 4 $ Stream.unfold Unfold.enumerateFromThen (0,(-2))     -- [0,-2,-4,-6]     ---    -- @-    --     -- /Pre-release/     enumerateFromThen :: Monad m => Unfold m (a, a) a @@ -465,17 +440,11 @@     -- @to@. Enumeration can occur downwards or upwards depending on whether @then@     -- comes before or after @from@.     ---    -- >>> import qualified Streamly.Data.Stream as Stream-    -- >>> import qualified Streamly.Internal.Data.Unfold as Unfold-    ---    -- @-    -- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, 2, 6)+    -- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, 2, 6)     -- [0,2,4,6]     ---    -- >>> Stream.fold Fold.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, (-2), (-6))+    -- >>> Stream.toList $ Stream.unfold Unfold.enumerateFromThenTo (0, (-2), (-6))     -- [0,-2,-4,-6]-    ---    -- @     --     -- /Pre-release/     enumerateFromThenTo :: Monad m => Unfold m (a, a, a) a
src/Streamly/Internal/Data/Unfold/Type.hs view
@@ -39,17 +39,13 @@  module Streamly.Internal.Data.Unfold.Type     (-    -- * Setup-    -- | To execute the code examples provided in this module in ghci, please-    -- run the following commands first.-    ---    -- $setup-     -- * General Notes     -- $notes      -- * Type-      Unfold (..)+    -- StreamD Step type re-exported+      Step(..)+    , Unfold (..)      -- * Basic Constructors     , mkUnfoldM@@ -116,7 +112,7 @@ import Data.Void (Void) import Fusion.Plugin.Types (Fuse(..)) import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))-import Streamly.Internal.Data.Stream.StreamD.Step (Step(..))+import Streamly.Internal.Data.Stream.Step (Step(..))  import Prelude hiding (map, mapM, concatMap, zipWith, takeWhile) 
src/Streamly/Internal/FileSystem/Dir.hs view
@@ -6,7 +6,6 @@ -- -- License     : BSD3 -- Maintainer  : streamly@composewell.com--- Stability   : pre-release -- Portability : GHC  module Streamly.Internal.FileSystem.Dir@@ -83,15 +82,24 @@     ) where +import Control.Monad.Catch (MonadCatch) import Control.Monad.IO.Class (MonadIO(..)) import Data.Bifunctor (bimap) import Data.Either (isRight, isLeft, fromLeft, fromRight)+import Data.Function ((&)) import Streamly.Data.Stream (Stream)+import Streamly.Internal.Data.Unfold (Step(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import System.FilePath ((</>))-+#if (defined linux_HOST_OS) || (defined darwin_HOST_OS)+import System.Posix (DirStream, openDirStream, readDirStream, closeDirStream)+#elif defined(mingw32_HOST_OS)+import qualified System.Win32 as Win32+#else+#error "Unsupported architecture"+#endif import qualified Streamly.Data.Unfold as UF-import qualified Streamly.Internal.Data.Unfold as UF (mapM2)+import qualified Streamly.Internal.Data.Unfold as UF (mapM2, bracketIO) import qualified Streamly.Data.Stream as S import qualified System.Directory as Dir @@ -231,17 +239,56 @@  -- XXX exception handling +#if (defined linux_HOST_OS) || (defined darwin_HOST_OS)+{-# INLINE streamReader #-}+streamReader :: MonadIO m => Unfold m DirStream FilePath+streamReader = Unfold step return++    where++    step strm = do+        -- XXX Use readDirStreamMaybe+        file <- liftIO $ readDirStream strm+        case file of+            [] -> return Stop+            _ -> return $ Yield file strm++#elif defined(mingw32_HOST_OS)+openDirStream :: String -> IO (Win32.HANDLE, Win32.FindData)+openDirStream = Win32.findFirstFile++closeDirStream :: (Win32.HANDLE, Win32.FindData) -> IO ()+closeDirStream (h, _) = Win32.findClose h++{-# INLINE streamReader #-}+streamReader :: MonadIO m => Unfold m (Win32.HANDLE, Win32.FindData) FilePath+streamReader = Unfold step return++    where++    step (h, fdat) = do+        more <- liftIO $ Win32.findNextFile h fdat+        if more+        then do+            file <- liftIO $ Win32.getFindDataFileName fdat+            return $ Yield file (h, fdat)+        else return Stop+#endif+ --  | Read a directory emitting a stream with names of the children. Filter out --  "." and ".." entries. -- --  /Internal/ -- {-# INLINE reader #-}-reader :: MonadIO m => Unfold m FilePath FilePath+reader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath reader =-    -- XXX use proper streaming read of the dir-      UF.filter (\x -> x /= "." && x /= "..")-    $ UF.lmapM (liftIO . Dir.getDirectoryContents) UF.fromList+-- XXX Instead of using bracketIO for each iteration of the loop we should+-- instead yield a buffer of dir entries in each iteration and then use an+-- unfold and concat to flatten those entries. That should improve the+-- performance.+      UF.bracketIO openDirStream closeDirStream streamReader+    & UF.filter (\x -> x /= "." && x /= "..")  -- XXX We can use a more general mechanism to filter the contents of a -- directory. We can just stat each child and pass on the stat information. We@@ -254,7 +301,7 @@ --  /Internal/ -- {-# INLINE eitherReader #-}-eitherReader :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)+eitherReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath) eitherReader = UF.mapM2 classify reader      where@@ -264,7 +311,7 @@         return $ if r then Left x else Right x  {-# INLINE eitherReaderPaths #-}-eitherReaderPaths :: MonadIO m => Unfold m FilePath (Either FilePath FilePath)+eitherReaderPaths ::(MonadIO m, MonadCatch m) => Unfold m FilePath (Either FilePath FilePath) eitherReaderPaths =     UF.mapM2 (\dir -> return . bimap (dir </>) (dir </>)) eitherReader @@ -274,7 +321,7 @@ --  /Internal/ -- {-# INLINE fileReader #-}-fileReader :: MonadIO m => Unfold m FilePath FilePath+fileReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath fileReader = fmap (fromRight undefined) $ UF.filter isRight eitherReader  -- | Read directories only. Filter out "." and ".." entries.@@ -282,19 +329,19 @@ --  /Internal/ -- {-# INLINE dirReader #-}-dirReader :: MonadIO m => Unfold m FilePath FilePath+dirReader :: (MonadIO m, MonadCatch m) => Unfold m FilePath FilePath dirReader = fmap (fromLeft undefined) $ UF.filter isLeft eitherReader  -- | Raw read of a directory. -- -- /Pre-release/ {-# INLINE read #-}-read :: MonadIO m => FilePath -> Stream m FilePath+read :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath read = S.unfold reader  {-# DEPRECATED toStream "Please use 'read' instead" #-} {-# INLINE toStream #-}-toStream :: MonadIO m => String -> Stream m String+toStream :: (MonadIO m, MonadCatch m) => String -> Stream m String toStream = read  -- | Read directories as Left and files as Right. Filter out "." and ".."@@ -302,18 +349,18 @@ -- -- /Pre-release/ {-# INLINE readEither #-}-readEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+readEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath) readEither = S.unfold eitherReader  -- | Like 'readEither' but prefix the names of the files and directories with -- the supplied directory path. {-# INLINE readEitherPaths #-}-readEitherPaths :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+readEitherPaths :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath) readEitherPaths dir = fmap (bimap (dir </>) (dir </>)) $ readEither dir  {-# DEPRECATED toEither "Please use 'readEither' instead" #-} {-# INLINE toEither #-}-toEither :: MonadIO m => FilePath -> Stream m (Either FilePath FilePath)+toEither :: (MonadIO m, MonadCatch m) => FilePath -> Stream m (Either FilePath FilePath) toEither = readEither  -- | Read files only.@@ -321,12 +368,12 @@ --  /Internal/ -- {-# INLINE readFiles #-}-readFiles :: MonadIO m => FilePath -> Stream m FilePath+readFiles :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath readFiles = S.unfold fileReader  {-# DEPRECATED toFiles "Please use 'readFiles' instead" #-} {-# INLINE toFiles #-}-toFiles :: MonadIO m => FilePath -> Stream m FilePath+toFiles :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath toFiles = readFiles  -- | Read directories only.@@ -334,12 +381,12 @@ --  /Internal/ -- {-# INLINE readDirs #-}-readDirs :: MonadIO m => FilePath -> Stream m FilePath+readDirs :: (MonadIO m, MonadCatch m) => FilePath -> Stream m FilePath readDirs = S.unfold dirReader  {-# DEPRECATED toDirs "Please use 'readDirs' instead" #-} {-# INLINE toDirs #-}-toDirs :: MonadIO m => String -> Stream m String+toDirs :: (MonadIO m, MonadCatch m) => String -> Stream m String toDirs = readDirs  {-@@ -397,7 +444,7 @@ -- @since 0.7.0 {-# INLINE fromStreamWithBufferOf #-} fromStreamWithBufferOf :: MonadIO m => Int -> Handle -> Stream m Word8 -> m ()-fromStreamWithBufferOf n h m = fromChunks h $ S.chunksOf n m+fromStreamWithBufferOf n h m = fromChunks h $ S.pinnedChunksOf n m -- fromStreamWithBufferOf n h m = fromChunks h $ AS.chunksOf n m  -- > write = 'writeWithBufferOf' A.defaultChunkSize@@ -448,7 +495,7 @@ -- @since 0.7.0 {-# INLINE writeWithBufferOf #-} writeWithBufferOf :: MonadIO m => Int -> Handle -> Fold m Word8 ()-writeWithBufferOf n h = FL.groupsOf n (writeNUnsafe n) (writeChunks h)+writeWithBufferOf n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)  -- > write = 'writeWithBufferOf' A.defaultChunkSize --
src/Streamly/Internal/FileSystem/File.hs view
@@ -6,7 +6,6 @@ -- -- License     : BSD3 -- Maintainer  : streamly@composewell.com--- Stability   : pre-release -- Portability : GHC -- -- Read and write streams and arrays to and from files specified by their paths@@ -76,11 +75,11 @@     , fromChunks      -- ** Append To File-    , append-    , appendWith+    , writeAppend+    , writeAppendWith     -- , appendShared-    , appendArray-    , appendChunks+    , writeAppendArray+    , writeAppendChunks      -- * Deprecated     , readWithBufferOf@@ -104,10 +103,10 @@ import qualified System.IO as SIO  import Streamly.Data.Fold (groupsOf, drain)-import Streamly.Internal.Data.Array.Type (Array(..), writeNUnsafe)+import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe) import Streamly.Internal.Data.Fold.Type (Fold(..)) import Streamly.Data.Stream (Stream)-import Streamly.Internal.Data.Unboxed (Unbox)+import Streamly.Internal.Data.Unbox (Unbox) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) -- import Streamly.String (encodeUtf8, decodeUtf8, foldLines) import Streamly.Internal.System.IO (defaultChunkSize)@@ -115,6 +114,7 @@ import qualified Streamly.Data.Array as A import qualified Streamly.Data.Stream as S import qualified Streamly.Data.Unfold as UF+import qualified Streamly.Internal.Data.Array.Type as IA (pinnedChunksOf) import qualified Streamly.Internal.Data.Unfold as UF (bracketIO) import qualified Streamly.Internal.Data.Fold.Type as FL     (Step(..), snoc, reduce)@@ -212,9 +212,9 @@ -- -- /Pre-release/ ---{-# INLINABLE appendArray #-}-appendArray :: FilePath -> Array a -> IO ()-appendArray file arr = SIO.withFile file AppendMode (`FH.putChunk` arr)+{-# INLINABLE writeAppendArray #-}+writeAppendArray :: FilePath -> Array a -> IO ()+writeAppendArray file arr = SIO.withFile file AppendMode (`FH.putChunk` arr)  ------------------------------------------------------------------------------- -- Stream of Arrays IO@@ -405,7 +405,7 @@ {-# INLINE fromBytesWith #-} fromBytesWith :: (MonadIO m, MonadCatch m)     => Int -> FilePath -> Stream m Word8 -> m ()-fromBytesWith n file xs = fromChunks file $ S.chunksOf n xs+fromBytesWith n file xs = fromChunks file $ IA.pinnedChunksOf n xs  {-# DEPRECATED fromBytesWithBufferOf "Please use 'fromBytesWith' instead"  #-} {-# INLINE fromBytesWithBufferOf #-}@@ -438,7 +438,7 @@ {-# INLINE writeChunks #-} writeChunks :: (MonadIO m, MonadCatch m)     => FilePath -> Fold m (Array a) ()-writeChunks path = Fold step initial extract+writeChunks path = Fold step initial extract final     where     initial = do         h <- liftIO (openFile path WriteMode)@@ -448,12 +448,15 @@     step (fld, h) x = do         r <- FL.snoc fld x `MC.onException` liftIO (hClose h)         return $ FL.Partial (r, h)-    extract (Fold _ initial1 extract1, h) = do++    extract _ = return ()++    final (Fold _ initial1 _ final1, h) = do         liftIO $ hClose h         res <- initial1         case res of-            FL.Partial fs -> extract1 fs-            FL.Done fb -> return fb+            FL.Partial fs -> final1 fs+            FL.Done () -> return ()  -- | @writeWith chunkSize handle@ writes the input stream to @handle@. -- Bytes in the input stream are collected into a buffer until we have a chunk@@ -464,7 +467,7 @@ writeWith :: (MonadIO m, MonadCatch m)     => Int -> FilePath -> Fold m Word8 () writeWith n path =-    groupsOf n (writeNUnsafe n) (writeChunks path)+    groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)  {-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead"  #-} {-# INLINE writeWithBufferOf #-}@@ -488,10 +491,10 @@ -- -- /Pre-release/ ---{-# INLINE appendChunks #-}-appendChunks :: (MonadIO m, MonadCatch m)+{-# INLINE writeAppendChunks #-}+writeAppendChunks :: (MonadIO m, MonadCatch m)     => FilePath -> Stream m (Array a) -> m ()-appendChunks = fromChunksMode AppendMode+writeAppendChunks = fromChunksMode AppendMode  -- | Like 'append' but provides control over the write buffer. Output will -- be written to the IO device as soon as we collect the specified number of@@ -499,10 +502,11 @@ -- -- /Pre-release/ ---{-# INLINE appendWith #-}-appendWith :: (MonadIO m, MonadCatch m)+{-# INLINE writeAppendWith #-}+writeAppendWith :: (MonadIO m, MonadCatch m)     => Int -> FilePath -> Stream m Word8 -> m ()-appendWith n file xs = appendChunks file $ S.chunksOf n xs+writeAppendWith n file xs =+    writeAppendChunks file $ IA.pinnedChunksOf n xs  -- | Append a byte stream to a file. Combines the bytes in chunks of size up to -- 'A.defaultChunkSize' before writing.  If the file exists then the new data@@ -511,9 +515,9 @@ -- -- /Pre-release/ ---{-# INLINE append #-}-append :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()-append = appendWith defaultChunkSize+{-# INLINE writeAppend #-}+writeAppend :: (MonadIO m, MonadCatch m) => FilePath -> Stream m Word8 -> m ()+writeAppend = writeAppendWith defaultChunkSize  {- -- | Like 'append' but the file is not locked for exclusive writes.
src/Streamly/Internal/FileSystem/Handle.hs view
@@ -1,3 +1,5 @@+{-# LANGUAGE CPP #-}+ #include "inline.hs"  -- |@@ -25,6 +27,12 @@ -- module Streamly.Internal.FileSystem.Handle     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Singleton APIs       getChunk     , getChunkOf@@ -115,7 +123,7 @@ import Data.Function ((&)) import Data.Maybe (isNothing, fromJust) import Data.Word (Word8)-import Streamly.Internal.Data.Unboxed (Unbox)+import Streamly.Internal.Data.Unbox (Unbox) import System.IO (Handle, SeekMode(..), hGetBufSome, hPutBuf, hSeek) import Prelude hiding (read) @@ -123,36 +131,26 @@ import Streamly.Internal.Data.Refold.Type (Refold(..)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.Data.Array.Type-       (Array(..), writeNUnsafe, unsafeFreezeWithShrink, byteLength)-import Streamly.Internal.Data.Stream.StreamD.Type (Stream)-import Streamly.Internal.Data.Stream.Chunked (lpackArraysChunksOf)+       (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)+import Streamly.Internal.Data.Stream.Type (Stream)+import Streamly.Internal.Data.Array.Stream (lpackArraysChunksOf) -- import Streamly.String (encodeUtf8, decodeUtf8, foldLines) import Streamly.Internal.System.IO (defaultChunkSize)  import qualified Streamly.Data.Fold as FL import qualified Streamly.Data.Array as A import qualified Streamly.Internal.Data.Array.Type as A-import qualified Streamly.Internal.Data.Stream.Chunked as AS-import qualified Streamly.Internal.Data.Array.Mut.Type as MArray+import qualified Streamly.Internal.Data.Array.Stream as AS+import qualified Streamly.Internal.Data.MutArray.Type as MArray import qualified Streamly.Internal.Data.Refold.Type as Refold import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)-import qualified Streamly.Internal.Data.Stream.StreamD as S-import qualified Streamly.Internal.Data.Stream.StreamD.Type as D+import qualified Streamly.Internal.Data.Stream as S+import qualified Streamly.Internal.Data.Stream as D     (Stream(..), Step(..)) import qualified Streamly.Internal.Data.Unfold as UF-import qualified Streamly.Internal.Data.Stream.StreamK.Type as K (mkStream)+import qualified Streamly.Internal.Data.StreamK.Type as K (mkStream) --- $setup--- >>> import qualified Streamly.Data.Array as Array--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Data.Unfold as Unfold--- >>> import qualified Streamly.Data.Stream as Stream------ >>> import qualified Streamly.Internal.Data.Array.Type as Array (writeNUnsafe)--- >>> import qualified Streamly.Internal.Data.Stream as Stream--- >>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)--- >>> import qualified Streamly.Internal.FileSystem.Handle as Handle--- >>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)+#include "DocTestFileSystemHandle.hs"  ------------------------------------------------------------------------------- -- References@@ -180,7 +178,7 @@ {-# INLINABLE getChunk #-} getChunk :: MonadIO m => Int -> Handle -> m (Array Word8) getChunk size h = liftIO $ do-    arr <- MArray.newPinnedBytes size+    arr <- MArray.pinnedNewBytes size     -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))     MArray.asPtrUnsafe arr $ \p -> do         n <- hGetBufSome h p size@@ -358,7 +356,7 @@ -- performed in sizes of -- 'Streamly.Internal.Data.Array.Type.defaultChunkSize'. ----- >>> reader = Unfold.many Array.reader chunkReader+-- >>> reader = Unfold.many Array.reader Handle.chunkReader -- {-# INLINE reader #-} reader :: MonadIO m => Unfold m Handle Word8@@ -425,15 +423,15 @@     => Int -> Handle -> Stream m (Array a) -> m () putChunksWith n h xs = putChunks h $ AS.compact n xs +-- > putBytesWith n h m = Handle.putChunks h $ A.pinnedChunksOf n m+ -- | @putBytesWith bufsize handle stream@ writes @stream@ to @handle@ -- in chunks of @bufsize@.  A write is performed to the IO device as soon as we -- collect the required input size. ----- >>> putBytesWith n h m = Handle.putChunks h $ Stream.chunksOf n m--- {-# INLINE putBytesWith #-} putBytesWith :: MonadIO m => Int -> Handle -> Stream m Word8 -> m ()-putBytesWith n h m = putChunks h $ A.chunksOf n m+putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m  -- | Write a byte stream to a file handle. Accumulates the input in chunks of -- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing.@@ -502,7 +500,7 @@ -- {-# INLINE writeWith #-} writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()-writeWith n h = FL.groupsOf n (writeNUnsafe n) (writeChunks h)+writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)  -- | Same as 'writeWith' --@@ -520,7 +518,7 @@ writeMaybesWith :: (MonadIO m )     => Int -> Handle -> Fold m (Maybe Word8) () writeMaybesWith n h =-    let writeNJusts = FL.lmap fromJust $ A.writeN n+    let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n         writeOnNothing = FL.takeEndBy_ isNothing writeNJusts     in FL.many writeOnNothing (writeChunks h) @@ -530,7 +528,7 @@ {-# INLINE writerWith #-} writerWith :: MonadIO m => Int -> Refold m Handle Word8 () writerWith n =-    FL.refoldMany (FL.take n $ writeNUnsafe n) chunkWriter+    FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter  -- | Write a byte stream to a file handle. Accumulates the input in chunks of -- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
− src/Streamly/Internal/Serialize/FromBytes.hs
@@ -1,394 +0,0 @@--- |--- Module      : Streamly.Internal.Serialize.FromBytes--- Copyright   : (c) 2020 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : pre-release--- Portability : GHC------ Decode Haskell data types from byte streams.--module Streamly.Internal.Serialize.FromBytes-    (-    -- * Type class-      FromBytes (..)--    -- * Decoders-    , unit-    , bool-    , ordering-    , eqWord8 -- XXX rename to word8Eq-    , word8-    , word16be-    , word16le-    , word32be-    , word32le-    , word64be-    , word64le-    , word64host-    , int8-    , int16be-    , int16le-    , int32be-    , int32le-    , int64be-    , int64le-    , float32be-    , float32le-    , double64be-    , double64le-    , charLatin1-    )-where--import Control.Monad.IO.Class (MonadIO)-import Data.Bits ((.|.), unsafeShiftL)-import Data.Char (chr)-import Data.Int (Int8, Int16, Int32, Int64)-import GHC.Float (castWord32ToFloat, castWord64ToDouble)-import Data.Word (Word8, Word16, Word32, Word64)-import Streamly.Internal.Data.Parser (Parser)-import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))-import Streamly.Internal.Data.Tuple.Strict (Tuple' (..))-import qualified Streamly.Data.Array as A-import qualified Streamly.Internal.Data.Array as A-    (unsafeIndex, castUnsafe)-import qualified Streamly.Internal.Data.Parser as PR-    (fromPure, either, satisfy, takeEQ)-import qualified Streamly.Internal.Data.Parser.ParserD as PRD-    (Parser(..), Initial(..), Step(..))---- Note: The () type does not need to have an on-disk representation in theory.--- But we use a concrete representation for it so that we count how many ()--- types we have. Or when we have an array of units the array a concrete--- length.---- | A value of type '()' is encoded as @0@ in binary encoding.------ @--- 0 ==> ()--- @------ /Pre-release/----{-# INLINE unit #-}-unit :: Monad m => Parser Word8 m ()-unit = eqWord8 0 *> PR.fromPure ()--{-# INLINE word8ToBool #-}-word8ToBool :: Word8 -> Either String Bool-word8ToBool 0 = Right False-word8ToBool 1 = Right True-word8ToBool w = Left ("Invalid Bool encoding " ++ Prelude.show w)---- | A value of type 'Bool' is encoded as follows in binary encoding.------ @--- 0 ==> False--- 1 ==> True--- @------ /Pre-release/----{-# INLINE bool #-}-bool :: Monad m => Parser Word8 m Bool-bool = PR.either word8ToBool--{-# INLINE word8ToOrdering #-}-word8ToOrdering :: Word8 -> Either String Ordering-word8ToOrdering 0 = Right LT-word8ToOrdering 1 = Right EQ-word8ToOrdering 2 = Right GT-word8ToOrdering w = Left ("Invalid Ordering encoding " ++ Prelude.show w)---- | A value of type 'Ordering' is encoded as follows in binary encoding.------ @--- 0 ==> LT--- 1 ==> EQ--- 2 ==> GT--- @------ /Pre-release/----{-# INLINE ordering #-}-ordering :: Monad m => Parser Word8 m Ordering-ordering = PR.either word8ToOrdering---- XXX should go in a Word8 parser module?--- | Accept the input byte only if it is equal to the specified value.------ /Pre-release/----{-# INLINE eqWord8 #-}-eqWord8 :: Monad m => Word8 -> Parser Word8 m Word8-eqWord8 b = PR.satisfy (== b)---- | Accept any byte.------ /Pre-release/----{-# INLINE word8 #-}-word8 :: Monad m => Parser Word8 m Word8-word8 = PR.satisfy (const True)---- | Big endian (MSB first) Word16-{-# INLINE word16beD #-}-word16beD :: Monad m => PRD.Parser Word8 m Word16-word16beD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial Nothing'--    step Nothing' a =-        -- XXX We can use a non-failing parser or a fold so that we do not-        -- have to buffer for backtracking which is inefficient.-        return $ PRD.Continue 0 (Just' (fromIntegral a `unsafeShiftL` 8))-    step (Just' w) a =-        return $ PRD.Done 0 (w .|. fromIntegral a)--    extract _ = return $ PRD.Error "word16be: end of input"---- | Parse two bytes as a 'Word16', the first byte is the MSB of the Word16 and--- second byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word16be #-}-word16be :: Monad m => Parser Word8 m Word16-word16be = word16beD---- | Little endian (LSB first) Word16-{-# INLINE word16leD #-}-word16leD :: Monad m => PRD.Parser Word8 m Word16-word16leD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial Nothing'--    step Nothing' a =-        return $ PRD.Continue 0 (Just' (fromIntegral a))-    step (Just' w) a =-        return $ PRD.Done 0 (w .|. fromIntegral a `unsafeShiftL` 8)--    extract _ = return $ PRD.Error "word16le: end of input"---- | Parse two bytes as a 'Word16', the first byte is the LSB of the Word16 and--- second byte is the MSB (little endian representation).------ /Pre-release/----{-# INLINE word16le #-}-word16le :: Monad m => Parser Word8 m Word16-word16le = word16leD---- | Big endian (MSB first) Word32-{-# INLINE word32beD #-}-word32beD :: Monad m => PRD.Parser Word8 m Word32-word32beD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial $ Tuple' 0 24--    step (Tuple' w sh) a = return $-        if sh /= 0-        then-            let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)-             in PRD.Continue 0 (Tuple' w1 (sh - 8))-        else PRD.Done 0 (w .|. fromIntegral a)--    extract _ = return $ PRD.Error "word32beD: end of input"---- | Parse four bytes as a 'Word32', the first byte is the MSB of the Word32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word32be #-}-word32be :: Monad m => Parser Word8 m Word32-word32be = word32beD---- | Little endian (LSB first) Word32-{-# INLINE word32leD #-}-word32leD :: Monad m => PRD.Parser Word8 m Word32-word32leD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial $ Tuple' 0 0--    step (Tuple' w sh) a = return $-        let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)-         in if sh /= 24-            then PRD.Continue 0 (Tuple' w1 (sh + 8))-            else PRD.Done 0 w1--    extract _ = return $ PRD.Error "word32leD: end of input"---- | Parse four bytes as a 'Word32', the first byte is the MSB of the Word32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word32le #-}-word32le :: Monad m => Parser Word8 m Word32-word32le = word32leD---- | Big endian (MSB first) Word64-{-# INLINE word64beD #-}-word64beD :: Monad m => PRD.Parser Word8 m Word64-word64beD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial $ Tuple' 0 56--    step (Tuple' w sh) a = return $-        if sh /= 0-        then-            let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)-             in PRD.Continue 0 (Tuple' w1 (sh - 8))-        else PRD.Done 0 (w .|. fromIntegral a)--    extract _ = return $ PRD.Error "word64beD: end of input"---- | Parse eight bytes as a 'Word64', the first byte is the MSB of the Word64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word64be #-}-word64be :: Monad m => Parser Word8 m Word64-word64be = word64beD---- | Little endian (LSB first) Word64-{-# INLINE word64leD #-}-word64leD :: Monad m => PRD.Parser Word8 m Word64-word64leD = PRD.Parser step initial extract--    where--    initial = return $ PRD.IPartial $ Tuple' 0 0--    step (Tuple' w sh) a = return $-        let w1 = w .|. (fromIntegral a `unsafeShiftL` sh)-         in if sh /= 56-            then PRD.Continue 0 (Tuple' w1 (sh + 8))-            else PRD.Done 0 w1--    extract _ = return $ PRD.Error "word64leD: end of input"---- | Parse eight bytes as a 'Word64', the first byte is the MSB of the Word64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word64le #-}-word64le :: Monad m => Parser Word8 m Word64-word64le = word64leD--{-# INLINE int8 #-}-int8 :: Monad m => Parser Word8 m Int8-int8 = fromIntegral <$> word8---- | Parse two bytes as a 'Int16', the first byte is the MSB of the Int16 and--- second byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int16be #-}-int16be :: Monad m => Parser Word8 m Int16-int16be = fromIntegral <$> word16be---- | Parse two bytes as a 'Int16', the first byte is the LSB of the Int16 and--- second byte is the MSB (little endian representation).------ /Pre-release/----{-# INLINE int16le #-}-int16le :: Monad m => Parser Word8 m Int16-int16le = fromIntegral <$> word16le---- | Parse four bytes as a 'Int32', the first byte is the MSB of the Int32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int32be #-}-int32be :: Monad m => Parser Word8 m Int32-int32be = fromIntegral <$> word32be---- | Parse four bytes as a 'Int32', the first byte is the MSB of the Int32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int32le #-}-int32le :: Monad m => Parser Word8 m Int32-int32le = fromIntegral <$> word32le---- | Parse eight bytes as a 'Int64', the first byte is the MSB of the Int64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int64be #-}-int64be :: Monad m => Parser Word8 m Int64-int64be = fromIntegral <$> word64be---- | Parse eight bytes as a 'Int64', the first byte is the MSB of the Int64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int64le #-}-int64le :: Monad m => Parser Word8 m Int64-int64le = fromIntegral <$> word64le--{-# INLINE float32be #-}-float32be :: MonadIO m => Parser Word8 m Float-float32be = castWord32ToFloat <$> word32be--{-# INLINE float32le #-}-float32le :: MonadIO m => Parser Word8 m Float-float32le = castWord32ToFloat <$> word32le--{-# INLINE double64be #-}-double64be :: MonadIO m => Parser Word8 m Double-double64be =  castWord64ToDouble <$> word64be--{-# INLINE double64le #-}-double64le :: MonadIO m => Parser Word8 m Double-double64le = castWord64ToDouble <$> word64le---- | Accept any byte.------ /Pre-release/----{-# INLINE charLatin1 #-}-charLatin1 :: Monad m => Parser Word8 m Char-charLatin1 = fmap (chr . fromIntegral) word8------------------------------------------------------------------------------------ Host byte order------------------------------------------------------------------------------------ | Parse eight bytes as a 'Word64' in the host byte order.------ /Pre-release/----{-# INLINE word64host #-}-word64host :: MonadIO m => Parser Word8 m Word64-word64host =-    fmap (A.unsafeIndex 0 . A.castUnsafe) $ PR.takeEQ 8 (A.writeN 8)------------------------------------------------------------------------------------ Type class----------------------------------------------------------------------------------class FromBytes a where-    -- | Decode a byte stream to a Haskell type.-    fromBytes :: Parser Word8 m a
− src/Streamly/Internal/Serialize/ToBytes.hs
@@ -1,374 +0,0 @@--- |--- Module      : Streamly.Internal.Serialize.ToBytes--- Copyright   : (c) 2022 Composewell Technologies--- License     : BSD-3-Clause--- Maintainer  : streamly@composewell.com--- Stability   : pre-release--- Portability : GHC------ Encode Haskell data types to byte streams.--module Streamly.Internal.Serialize.ToBytes-    (-    -- * Type class-      ToBytes (..)--    -- * Encoders-    , unit-    , bool-    , ordering-    , word8-    , word16be-    , word16le-    , word32be-    , word32le-    , word64be-    , word64le-    , word64host-    , int8-    , int16be-    , int16le-    , int32be-    , int32le-    , int64be-    , int64le-    , float32be-    , float32le-    , double64be-    , double64le-    , charLatin1-    , charUtf8-    )-where--#include "MachDeps.h"--import Data.Bits (shiftR)-import Data.Char (ord)-import Data.Int (Int8, Int16, Int32, Int64)-import Data.Word (Word8, Word16, Word32, Word64)-import GHC.Float (castDoubleToWord64, castFloatToWord32)-import Streamly.Internal.Data.Stream.StreamD (Stream)-import Streamly.Internal.Data.Stream.StreamD (Step(..))-import Streamly.Internal.Unicode.Stream (readCharUtf8)--import qualified Streamly.Internal.Data.Stream.StreamD as Stream-import qualified Streamly.Internal.Data.Stream.StreamD as D---- XXX Use StreamD directly?---- | A value of type '()' is encoded as @0@ in binary encoding.------ @--- 0 ==> ()--- @------ /Pre-release/----{-# INLINE unit #-}-unit :: Applicative m => Stream m Word8-unit = Stream.fromPure 0--{-# INLINE boolToWord8 #-}-boolToWord8 :: Bool -> Word8-boolToWord8 False = 0-boolToWord8 True = 1---- | A value of type 'Bool' is encoded as follows in binary encoding.------ @--- 0 ==> False--- 1 ==> True--- @------ /Pre-release/----{-# INLINE bool #-}-bool :: Applicative m => Bool -> Stream m Word8-bool = Stream.fromPure . boolToWord8--{-# INLINE orderingToWord8 #-}-orderingToWord8 :: Ordering -> Word8-orderingToWord8 LT = 0-orderingToWord8 EQ = 1-orderingToWord8 GT = 2---- | A value of type 'Ordering' is encoded as follows in binary encoding.------ @--- 0 ==> LT--- 1 ==> EQ--- 2 ==> GT--- @------ /Pre-release/----{-# INLINE ordering #-}-ordering :: Applicative m => Ordering -> Stream m Word8-ordering = Stream.fromPure . orderingToWord8---- | Stream a 'Word8'.------ /Pre-release/----{-# INLINE word8 #-}-word8 :: Applicative m => Word8 -> Stream m Word8-word8 = Stream.fromPure--data W16State = W16B1 | W16B2 | W16Done--{-# INLINE word16beD #-}-word16beD :: Applicative m => Word16 -> D.Stream m Word8-word16beD w = D.Stream step W16B1--    where--    step _ W16B1 = pure $ Yield (fromIntegral (shiftR w 8) :: Word8) W16B2-    step _ W16B2 = pure $ Yield (fromIntegral w :: Word8) W16Done-    step _ W16Done = pure Stop---- | Stream a 'Word16' as two bytes, the first byte is the MSB of the Word16--- and second byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word16be #-}-word16be :: Monad m => Word16 -> Stream m Word8-word16be = word16beD---- | Little endian (LSB first) Word16-{-# INLINE word16leD #-}-word16leD :: Applicative m => Word16 -> D.Stream m Word8-word16leD w = D.Stream step W16B1--    where--    step _ W16B1 = pure $ Yield (fromIntegral w :: Word8) W16B2-    step _ W16B2 = pure $ Yield (fromIntegral (shiftR w 8) :: Word8) W16Done-    step _ W16Done = pure Stop---- | Stream a 'Word16' as two bytes, the first byte is the LSB of the Word16--- and second byte is the MSB (little endian representation).------ /Pre-release/----{-# INLINE word16le #-}-word16le :: Monad m => Word16 -> Stream m Word8-word16le = word16leD--data W32State = W32B1 | W32B2 | W32B3 | W32B4 | W32Done---- | Big endian (MSB first) Word32-{-# INLINE word32beD #-}-word32beD :: Applicative m => Word32 -> D.Stream m Word8-word32beD w = D.Stream step W32B1--    where--    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s--    step _ W32B1 = yield 24 W32B2-    step _ W32B2 = yield 16 W32B3-    step _ W32B3 = yield 8 W32B4-    step _ W32B4 = pure $ Yield (fromIntegral w :: Word8) W32Done-    step _ W32Done = pure Stop---- | Stream a 'Word32' as four bytes, the first byte is the MSB of the Word32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word32be #-}-word32be :: Monad m => Word32 -> Stream m Word8-word32be = word32beD---- | Little endian (LSB first) Word32-{-# INLINE word32leD #-}-word32leD :: Applicative m => Word32 -> D.Stream m Word8-word32leD w = D.Stream step W32B1--    where--    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s--    step _ W32B1 = pure $ Yield (fromIntegral w :: Word8) W32B2-    step _ W32B2 = yield 8 W32B3-    step _ W32B3 = yield 16 W32B4-    step _ W32B4 = yield 24 W32Done-    step _ W32Done = pure Stop---- | Stream a 'Word32' as four bytes, the first byte is the MSB of the Word32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word32le #-}-word32le :: Monad m => Word32 -> Stream m Word8-word32le = word32leD--data W64State =-    W64B1 | W64B2 | W64B3 | W64B4 | W64B5 | W64B6 | W64B7 | W64B8 | W64Done---- | Big endian (MSB first) Word64-{-# INLINE word64beD #-}-word64beD :: Applicative m => Word64 -> D.Stream m Word8-word64beD w = D.Stream step W64B1--    where--    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s--    step _ W64B1 = yield 56 W64B2-    step _ W64B2 = yield 48 W64B3-    step _ W64B3 = yield 40 W64B4-    step _ W64B4 = yield 32 W64B5-    step _ W64B5 = yield 24 W64B6-    step _ W64B6 = yield 16 W64B7-    step _ W64B7 = yield  8 W64B8-    step _ W64B8 = pure $ Yield (fromIntegral w :: Word8) W64Done-    step _ W64Done = pure Stop---- | Stream a 'Word64' as eight bytes, the first byte is the MSB of the Word64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word64be #-}-word64be :: Monad m => Word64 -> Stream m Word8-word64be = word64beD---- | Little endian (LSB first) Word64-{-# INLINE word64leD #-}-word64leD :: Applicative m => Word64 -> D.Stream m Word8-word64leD w = D.Stream step W64B1--    where--    yield n s = pure $ Yield (fromIntegral (shiftR w n) :: Word8) s--    step _ W64B1 = pure $ Yield (fromIntegral w :: Word8) W64B2-    step _ W64B2 = yield  8 W64B3-    step _ W64B3 = yield 16 W64B4-    step _ W64B4 = yield 24 W64B5-    step _ W64B5 = yield 32 W64B6-    step _ W64B6 = yield 40 W64B7-    step _ W64B7 = yield 48 W64B8-    step _ W64B8 = yield 56 W64Done-    step _ W64Done = pure Stop---- | Stream a 'Word64' as eight bytes, the first byte is the MSB of the Word64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE word64le #-}-word64le :: Monad m => Word64 -> Stream m Word8-word64le = word64leD--{-# INLINE int8 #-}-int8 :: Applicative m => Int8 -> Stream m Word8-int8 i = word8 (fromIntegral i :: Word8)---- | Stream a 'Int16' as two bytes, the first byte is the MSB of the Int16--- and second byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int16be #-}-int16be :: Monad m => Int16 -> Stream m Word8-int16be i = word16be (fromIntegral i :: Word16)---- | Stream a 'Int16' as two bytes, the first byte is the LSB of the Int16--- and second byte is the MSB (little endian representation).------ /Pre-release/----{-# INLINE int16le #-}-int16le :: Monad m => Int16 -> Stream m Word8-int16le i = word16le (fromIntegral i :: Word16)---- | Stream a 'Int32' as four bytes, the first byte is the MSB of the Int32--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int32be #-}-int32be :: Monad m => Int32 -> Stream m Word8-int32be i = word32be (fromIntegral i :: Word32)--{-# INLINE int32le #-}-int32le :: Monad m => Int32 -> Stream m Word8-int32le i = word32le (fromIntegral i :: Word32)---- | Stream a 'Int64' as eight bytes, the first byte is the MSB of the Int64--- and last byte is the LSB (big endian representation).------ /Pre-release/----{-# INLINE int64be #-}-int64be :: Monad m => Int64 -> Stream m Word8-int64be i = word64be (fromIntegral i :: Word64)---- | Stream a 'Int64' as eight bytes, the first byte is the LSB of the Int64--- and last byte is the MSB (little endian representation).------ /Pre-release/----{-# INLINE int64le #-}-int64le :: Monad m => Int64 -> Stream m Word8-int64le i = word64le (fromIntegral i :: Word64)---- | Big endian (MSB first) Float-{-# INLINE float32be #-}-float32be :: Monad m => Float -> Stream m Word8-float32be = word32beD . castFloatToWord32---- | Little endian (LSB first) Float-{-# INLINE float32le #-}-float32le :: Monad m => Float -> Stream m Word8-float32le = word32leD . castFloatToWord32---- | Big endian (MSB first) Double-{-# INLINE double64be #-}-double64be :: Monad m => Double -> Stream m Word8-double64be = word64beD . castDoubleToWord64---- | Little endian (LSB first) Double-{-# INLINE double64le #-}-double64le :: Monad m => Double -> Stream m Word8-double64le = word64leD . castDoubleToWord64---- | Encode a Unicode character to stream of bytes in 0-255 range.----{-# INLINE charLatin1 #-}-charLatin1 :: Applicative m => Char -> Stream m Word8-charLatin1 = Stream.fromPure . fromIntegral . ord--{-# INLINE charUtf8 #-}-charUtf8 :: Monad m => Char -> Stream m Word8-charUtf8 = Stream.unfold readCharUtf8------------------------------------------------------------------------------------ Host byte order------------------------------------------------------------------------------------ | Stream a 'Word64' as eight bytes in the host byte order.------ /Pre-release/----{-# INLINE word64host #-}-word64host :: Monad m => Word64 -> Stream m Word8-word64host =-#ifdef WORDS_BIGENDIAN-    word64be-#else-    word64le-#endif------------------------------------------------------------------------------------ Type class----------------------------------------------------------------------------------class ToBytes a where-    -- | Convert a Haskell type to a byte stream.-    toBytes :: a -> Stream m Word8
src/Streamly/Internal/Unicode/Array.hs view
@@ -14,6 +14,12 @@ -- module Streamly.Internal.Unicode.Array     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Streams of Strings       lines     , words
src/Streamly/Internal/Unicode/Parser.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Unicode.Parser -- Copyright   : (c) 2021 Composewell Technologies@@ -12,6 +13,12 @@  module Streamly.Internal.Unicode.Parser     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Generic       char     , charIgnoreCase@@ -47,16 +54,23 @@      -- * Numeric     , signed+    , number+    , doubleParser     , double     , decimal     , hexadecimal++    -- * Utilities+    , mkDouble     ) where  import Control.Applicative (Alternative(..))-import Data.Bits (Bits, (.|.), shiftL)+import Data.Bits (Bits, (.|.), shiftL, (.&.)) import Data.Char (ord)-import Streamly.Internal.Data.Parser (Parser)+import Data.Ratio ((%))+import Fusion.Plugin.Types (Fuse(..))+import Streamly.Internal.Data.Parser (Parser(..), Initial(..),  Step(..))  import qualified Data.Char as Char import qualified Streamly.Data.Fold as Fold@@ -69,6 +83,8 @@     , dropWhile     ) +#include "DocTestUnicodeParser.hs"+ -------------------------------------------------------------------------------- -- Character classification --------------------------------------------------------------------------------@@ -263,36 +279,346 @@ signed :: (Num a, Monad m) => Parser Char m a -> Parser Char m a signed p = (negate <$> (char '-' *> p)) <|> (char '+' *> p) <|> p --- | Parse a 'Double'.+-- XXX Change Multiplier to Sign+type Multiplier = Int++-- XXX We can use Int instead of Integer to make it twice as fast. But then we+-- will have to truncate the significant digits before overflow occurs.+type Number = Integer+type DecimalPlaces = Int+type PowerMultiplier = Int+type Power = Int++{-# ANN type ScientificParseState Fuse #-}+data ScientificParseState+  = SPInitial+  | SPSign !Multiplier+  | SPAfterSign !Multiplier !Number+  | SPDot !Multiplier !Number+  | SPAfterDot !Multiplier !Number !DecimalPlaces+  | SPExponent !Multiplier !Number !DecimalPlaces+  | SPExponentWithSign !Multiplier !Number !DecimalPlaces !PowerMultiplier+  | SPAfterExponent !Multiplier !Number !DecimalPlaces !PowerMultiplier !Power++-- | A generic parser for scientific notation of numbers. Returns (mantissa,+-- exponent) tuple. The result can be mapped to 'Double' or any other number+-- representation e.g. @Scientific@. ----- This parser accepts an optional leading sign character, followed by--- at most one decimal digit.  The syntax is similar to that accepted by--- the 'read' function, with the exception that a trailing @\'.\'@ is--- consumed.+-- For example, using the @scientific@ package:+-- >> parserScientific = uncurry Data.Scientific.scientific <$> 'number'+{-# INLINE number #-}+number :: Monad m => Parser Char m (Integer, Int)+number =  Parser (\s a -> return $ step s a) initial (return . extract)++    where++    intToInteger :: Int -> Integer+    intToInteger = fromIntegral++    combineNum buf num = buf * 10 + num++    {-# INLINE initial #-}+    initial = pure $ IPartial SPInitial++    exitSPInitial msg =+        "number: expecting sign or decimal digit, got " ++ msg+    exitSPSign msg =+        "number: expecting decimal digit, got " ++ msg+    exitSPAfterSign multiplier num = (intToInteger multiplier * num, 0)+    exitSPAfterDot multiplier num decimalPlaces =+        ( intToInteger multiplier * num+        , -decimalPlaces+        )+    exitSPAfterExponent mult num decimalPlaces powerMult powerNum =+        let e = powerMult * powerNum - decimalPlaces+         in (intToInteger mult * num, e)++    {-# INLINE step #-}+    step SPInitial val =+        case val of+          '+' -> Continue 0 (SPSign 1)+          '-' -> Continue 0 $ (SPSign (-1))+          _ -> do+              let num = ord val - 48+              if num >= 0 && num <= 9+              then Partial 0 $ SPAfterSign 1 (intToInteger num)+              else Error $ exitSPInitial $ show val+    step (SPSign multiplier) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then Partial 0 $ SPAfterSign multiplier (intToInteger num)+            else Error $ exitSPSign $ show val+    step (SPAfterSign multiplier buf) val =+        case val of+            '.' -> Continue 0 $ SPDot multiplier buf+            'e' -> Continue 0 $ SPExponent multiplier buf 0+            'E' -> Continue 0 $ SPExponent multiplier buf 0+            _ ->+                let num = ord val - 48+                 in if num >= 0 && num <= 9+                    then+                        Partial 0+                            $ SPAfterSign multiplier (combineNum buf (intToInteger num))+                    else Done 1 $ exitSPAfterSign multiplier buf+    step (SPDot multiplier buf) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then Partial 0 $ SPAfterDot multiplier (combineNum buf (intToInteger num)) 1+            else Done 2 $ exitSPAfterSign multiplier buf+    step (SPAfterDot multiplier buf decimalPlaces) val =+        case val of+            'e' -> Continue 0 $ SPExponent multiplier buf decimalPlaces+            'E' -> Continue 0 $ SPExponent multiplier buf decimalPlaces+            _ ->+                let num = ord val - 48+                 in if num >= 0 && num <= 9+                    then+                        Partial 0+                            $ SPAfterDot+                                  multiplier+                                  (combineNum buf (intToInteger num))+                                  (decimalPlaces + 1)+                    else Done 1 $ exitSPAfterDot multiplier buf decimalPlaces+    step (SPExponent multiplier buf decimalPlaces) val =+        case val of+          '+' -> Continue 0 (SPExponentWithSign multiplier buf decimalPlaces 1)+          '-' -> Continue 0 (SPExponentWithSign multiplier buf decimalPlaces (-1))+          _ -> do+              let num = ord val - 48+              if num >= 0 && num <= 9+              then Partial 0 $ SPAfterExponent multiplier buf decimalPlaces 1 num+              else Done 2 $ exitSPAfterDot multiplier buf decimalPlaces+    step (SPExponentWithSign mult buf decimalPlaces powerMult) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then Partial 0 $ SPAfterExponent mult buf decimalPlaces powerMult num+            else Done 3 $ exitSPAfterDot mult buf decimalPlaces+    step (SPAfterExponent mult num decimalPlaces powerMult buf) val =+        let n = ord val - 48+         in if n >= 0 && n <= 9+            then+                Partial 0+                    $ SPAfterExponent+                          mult num decimalPlaces powerMult (combineNum buf n)+            else+                Done 1+                    $ exitSPAfterExponent mult num decimalPlaces powerMult buf++    {-# INLINE extract #-}+    extract SPInitial = Error $ exitSPInitial "end of input"+    extract (SPSign _) = Error $ exitSPSign "end of input"+    extract (SPAfterSign mult num) = Done 0 $ exitSPAfterSign mult num+    extract (SPDot mult num) = Done 1 $ exitSPAfterSign mult num+    extract (SPAfterDot mult num decimalPlaces) =+        Done 0 $ exitSPAfterDot mult num decimalPlaces+    extract (SPExponent mult num decimalPlaces) =+        Done 1 $ exitSPAfterDot mult num decimalPlaces+    extract (SPExponentWithSign mult num decimalPlaces _) =+        Done 2 $ exitSPAfterDot mult num decimalPlaces+    extract (SPAfterExponent mult num decimalPlaces powerMult powerNum) =+        Done 0 $ exitSPAfterExponent mult num decimalPlaces powerMult powerNum++type MantissaInt = Int+type OverflowPower = Int++{-# ANN type DoubleParseState Fuse #-}+data DoubleParseState+  = DPInitial+  | DPSign !Multiplier+  | DPAfterSign !Multiplier !MantissaInt !OverflowPower+  | DPDot !Multiplier !MantissaInt !OverflowPower+  | DPAfterDot !Multiplier !MantissaInt !OverflowPower+  | DPExponent !Multiplier !MantissaInt !OverflowPower+  | DPExponentWithSign !Multiplier !MantissaInt !OverflowPower !PowerMultiplier+  | DPAfterExponent !Multiplier !MantissaInt !OverflowPower !PowerMultiplier !Power++-- | A fast, custom parser for double precision flaoting point numbers. Returns+-- (mantissa, exponent) tuple. This is much faster than 'number' because it+-- assumes the number will fit in a 'Double' type and uses 'Int' representation+-- to store mantissa. ----- === Examples+-- Number larger than 'Double' may overflow. Int overflow is not checked in the+-- exponent. ----- Examples with behaviour identical to 'read', if you feed an empty--- continuation to the first result:+{-# INLINE doubleParser #-}+doubleParser :: Monad m => Parser Char m (Int, Int)+doubleParser =  Parser (\s a -> return $ step s a) initial (return . extract)++    where++    -- XXX Assuming Int = Int64++    -- Up to 58 bits Int won't overflow+    -- ghci> (2^59-1)*10+9 :: Int+    -- 5764607523034234879+    mask :: Word+    mask = 0x7c00000000000000 -- 58 bits, ignore the sign bit++    {-# INLINE combineNum #-}+    combineNum :: Int -> Int -> Int -> (Int, Int)+    combineNum mantissa power num =+         if fromIntegral mantissa .&. mask == 0+         then (mantissa * 10 + num, power)+         else (mantissa, power + 1)++    {-# INLINE initial #-}+    initial = pure $ IPartial DPInitial++    exitDPInitial msg =+        "number: expecting sign or decimal digit, got " ++ msg+    exitDPSign msg =+        "number: expecting decimal digit, got " ++ msg+    exitDPAfterSign multiplier num opower = (fromIntegral multiplier * num, opower)+    exitDPAfterDot multiplier num opow =+        (fromIntegral multiplier * num , opow)+    exitDPAfterExponent mult num opow powerMult powerNum =+        (fromIntegral mult * num, opow + powerMult * powerNum)++    {-# INLINE step #-}+    step DPInitial val =+        case val of+          '+' -> Continue 0 (DPSign 1)+          '-' -> Continue 0 $ (DPSign (-1))+          _ -> do+              let num = ord val - 48+              if num >= 0 && num <= 9+              then Partial 0 $ DPAfterSign 1 num 0+              else Error $ exitDPInitial $ show val+    step (DPSign multiplier) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then Partial 0 $ DPAfterSign multiplier num 0+            else Error $ exitDPSign $ show val+    step (DPAfterSign multiplier buf opower) val =+        case val of+            '.' -> Continue 0 $ DPDot multiplier buf opower+            'e' -> Continue 0 $ DPExponent multiplier buf opower+            'E' -> Continue 0 $ DPExponent multiplier buf opower+            _ ->+                let num = ord val - 48+                 in if num >= 0 && num <= 9+                    then+                        let (buf1, power1) = combineNum buf opower num+                         in Partial 0+                            $ DPAfterSign multiplier buf1 power1+                    else Done 1 $ exitDPAfterSign multiplier buf opower+    step (DPDot multiplier buf opower) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then+                let (buf1, power1) = combineNum buf opower num+                 in Partial 0 $ DPAfterDot multiplier buf1 (power1 - 1)+            else Done 2 $ exitDPAfterSign multiplier buf opower+    step (DPAfterDot multiplier buf opower) val =+        case val of+            'e' -> Continue 0 $ DPExponent multiplier buf opower+            'E' -> Continue 0 $ DPExponent multiplier buf opower+            _ ->+                let num = ord val - 48+                 in if num >= 0 && num <= 9+                    then+                        let (buf1, power1) = combineNum buf opower num+                         in Partial 0 $ DPAfterDot multiplier buf1 (power1 - 1)+                    else Done 1 $ exitDPAfterDot multiplier buf opower+    step (DPExponent multiplier buf opower) val =+        case val of+          '+' -> Continue 0 (DPExponentWithSign multiplier buf opower 1)+          '-' -> Continue 0 (DPExponentWithSign multiplier buf opower (-1))+          _ -> do+              let num = ord val - 48+              if num >= 0 && num <= 9+              then Partial 0 $ DPAfterExponent multiplier buf opower 1 num+              else Done 2 $ exitDPAfterDot multiplier buf opower+    step (DPExponentWithSign mult buf opower powerMult) val =+        let num = ord val - 48+         in if num >= 0 && num <= 9+            then Partial 0 $ DPAfterExponent mult buf opower powerMult num+            else Done 3 $ exitDPAfterDot mult buf opower+    step (DPAfterExponent mult num opower powerMult buf) val =+        let n = ord val - 48+         in if n >= 0 && n <= 9+            then+                Partial 0+                    $ DPAfterExponent mult num opower powerMult (buf * 10 + n)+            else Done 1 $ exitDPAfterExponent mult num opower powerMult buf++    {-# INLINE extract #-}+    extract DPInitial = Error $ exitDPInitial "end of input"+    extract (DPSign _) = Error $ exitDPSign "end of input"+    extract (DPAfterSign mult num opow) = Done 0 $ exitDPAfterSign mult num opow+    extract (DPDot mult num opow) = Done 1 $ exitDPAfterSign mult num opow+    extract (DPAfterDot mult num opow) =+        Done 0 $ exitDPAfterDot mult num opow+    extract (DPExponent mult num opow) =+        Done 1 $ exitDPAfterDot mult num opow+    extract (DPExponentWithSign mult num opow _) =+        Done 2 $ exitDPAfterDot mult num opow+    extract (DPAfterExponent mult num opow powerMult powerNum) =+        Done 0 $ exitDPAfterExponent mult num opow powerMult powerNum++-- XXX We can have a `realFloat` parser instead to parse any RealFloat value.+-- And a integral parser to read any integral value.++-- XXX This is very expensive, takes much more time than the rest of the+-- parsing. Need to look into fromRational.++-- | @mkDouble mantissa exponent@ converts a mantissa and exponent to a+-- 'Double' value equivalent to @mantissa * 10^exponent@. It does not check for+-- overflow, powers more than 308 will overflow.+{-# INLINE mkDouble #-}+mkDouble :: Integer -> Int -> Double+mkDouble mantissa power =+    if power > 0+    then fromRational ((mantissa * 10 ^ power) % 1)+    else fromRational (mantissa % 10 ^ (-power))++-- | Parse a decimal 'Double' value. This parser accepts an optional sign (+ or+-- -) followed by at least one decimal digit. Decimal digits are optionally+-- followed by a decimal point and at least one decimal digit after the point.+-- This parser accepts the maximal valid input as long as it gives a valid+-- number. Specifcally a trailing decimal point is allowed but not consumed.+-- This function does not accept \"NaN\" or \"Infinity\" string representations+-- of double values. ----- > IS.parse double (IS.fromList "3")     == 3.0--- > IS.parse double (IS.fromList "3.1")   == 3.1--- > IS.parse double (IS.fromList "3e4")   == 30000.0--- > IS.parse double (IS.fromList "3.1e4") == 31000.0--- > IS.parse double (IS.fromList "3e")    == 30+-- Definition: ----- Examples with behaviour identical to 'read':+-- >>> double = uncurry Unicode.mkDouble <$> Unicode.number ----- > IS.parse (IS.fromList ".3")    == error "Parse failed"--- > IS.parse (IS.fromList "e3")    == error "Parse failed"+-- Examples: ----- Example of difference from 'read':+-- >>> p = Stream.parse Unicode.double . Stream.fromList ----- > IS.parse double (IS.fromList "3.foo") == 3.0+-- >>> p "-1.23e-123"+-- Right (-1.23e-123) ----- This function does not accept string representations of \"NaN\" or--- \"Infinity\".+-- Trailing input examples: ----- /Unimplemented/-double :: Parser Char m Double-double = undefined+-- >>> p "1."+-- Right 1.0+--+-- >>> p "1.2.3"+-- Right 1.2+--+-- >>> p "1e"+-- Right 1.0+--+-- >>> p "1e2.3"+-- Right 100.0+--+-- >>> p "1+2"+-- Right 1.0+--+-- Error cases:+--+-- >>> p ""+-- Left (ParseError "number: expecting sign or decimal digit, got end of input")+--+-- >>> p ".1"+-- Left (ParseError "number: expecting sign or decimal digit, got '.'")+--+-- >>> p "+"+-- Left (ParseError "number: expecting decimal digit, got end of input")+--+{-# INLINE double #-}+double :: Monad m => Parser Char m Double+double = fmap (\(m,e) -> mkDouble (fromIntegral m) e) doubleParser
src/Streamly/Internal/Unicode/Stream.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Unicode.Stream -- Copyright   : (c) 2018 Composewell Technologies@@ -11,6 +12,12 @@  module Streamly.Internal.Unicode.Stream     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Construction (Decoding)       decodeLatin1 @@ -102,13 +109,14 @@ import GHC.Ptr (Ptr (..), plusPtr) import System.IO.Unsafe (unsafePerformIO) import Streamly.Internal.Data.Array.Type (Array(..))-import Streamly.Internal.Data.Array.Mut.Type (MutableByteArray)+import Streamly.Internal.Data.MutArray.Type (MutByteArray) import Streamly.Internal.Data.Fold (Fold)-import Streamly.Internal.Data.Stream.StreamD (Stream)-import Streamly.Internal.Data.Stream.StreamD (Step (..))+import Streamly.Internal.Data.Parser (Parser)+import Streamly.Internal.Data.Stream (Stream)+import Streamly.Internal.Data.Stream (Step (..)) import Streamly.Internal.Data.SVar.Type (adaptState) import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))-import Streamly.Internal.Data.Unboxed (peekWith)+import Streamly.Internal.Data.Unbox (Unbox(peekAt)) import Streamly.Internal.Data.Unfold.Type (Unfold(..)) import Streamly.Internal.System.IO (unsafeInlineIO) @@ -116,20 +124,13 @@ import qualified Streamly.Data.Unfold as Unfold import qualified Streamly.Internal.Data.Array.Type as Array import qualified Streamly.Internal.Data.Parser as Parser (Parser)-import qualified Streamly.Internal.Data.Parser.ParserD as ParserD-import qualified Streamly.Internal.Data.Stream.StreamD as Stream-import qualified Streamly.Internal.Data.Stream.StreamD as D+import qualified Streamly.Internal.Data.Parser as ParserD+import qualified Streamly.Internal.Data.Stream as Stream+import qualified Streamly.Internal.Data.Stream as D  import Prelude hiding (lines, words, unlines, unwords) --- $setup--- >>> :m--- >>> :set -XMagicHash--- >>> import Prelude hiding (lines, words, unlines, unwords)--- >>> import qualified Streamly.Data.Stream as Stream--- >>> import qualified Streamly.Data.Fold as Fold--- >>> import qualified Streamly.Internal.Unicode.Stream as Unicode--- >>> import Streamly.Internal.Unicode.Stream+#include "DocTestUnicodeStream.hs"  ------------------------------------------------------------------------------- -- Latin1 decoding@@ -523,8 +524,8 @@ -- workflow requires backtracking 1 element. This can be revisited once "Fold" -- supports backtracking. {-# INLINE writeCharUtf8' #-}-writeCharUtf8' :: Monad m => Fold m Word8 Char-writeCharUtf8' =  ParserD.toFold (parseCharUtf8WithD ErrorOnCodingFailure)+writeCharUtf8' :: Monad m => Parser Word8 m Char+writeCharUtf8' =  parseCharUtf8WithD ErrorOnCodingFailure  -- XXX The initial idea was to have "parseCharUtf8" and offload the error -- handling to another parser. So, say we had "parseCharUtf8'",@@ -556,7 +557,7 @@      where -    prefix = "Streamly.Internal.Data.Stream.StreamD.decodeUtf8With: "+    prefix = "Streamly.Internal.Data.Stream.decodeUtf8With: "      {-# INLINE handleError #-}     handleError e s =@@ -684,9 +685,9 @@ #endif data FlattenState s     = OuterLoop s !(Maybe (DecodeState, CodePoint))-    | InnerLoopDecodeInit s MutableByteArray !Int !Int-    | InnerLoopDecodeFirst s MutableByteArray !Int !Int Word8-    | InnerLoopDecoding s MutableByteArray !Int !Int+    | InnerLoopDecodeInit s MutByteArray !Int !Int+    | InnerLoopDecodeFirst s MutByteArray !Int !Int Word8+    | InnerLoopDecoding s MutByteArray !Int !Int         !DecodeState !CodePoint     | YAndC !Char (FlattenState s)   -- These constructors can be                                      -- encoded in the UTF8DecodeState@@ -720,7 +721,7 @@         case cfm of             ErrorOnCodingFailure ->                 error $-                show "Streamly.Internal.Data.Stream.StreamD."+                show "Streamly.Internal.Data.Stream."                 ++ "decodeUtf8ArraysWith: Input Underflow"             TransliterateCodingFailure -> YAndC replacementChar D             DropOnCodingFailure -> D@@ -745,7 +746,7 @@         | p == end = do             return $ Skip $ OuterLoop st Nothing     step' _ _ (InnerLoopDecodeInit st contents p end) = do-        x <- liftIO $ peekWith contents p+        x <- liftIO $ peekAt p contents         -- Note: It is important to use a ">" instead of a "<=" test here for         -- GHC to generate code layout for default branch prediction for the         -- common case. This is fragile and might change with the compiler@@ -769,7 +770,7 @@                     Skip $                     transliterateOrError                         (-                           "Streamly.Internal.Data.Stream.StreamD."+                           "Streamly.Internal.Data.Stream."                         ++ "decodeUtf8ArraysWith: Invalid UTF8"                         ++ " codepoint encountered"                         )@@ -779,7 +780,7 @@     step' _ _ (InnerLoopDecoding st _ p end sv cp)         | p == end = return $ Skip $ OuterLoop st (Just (sv, cp))     step' table _ (InnerLoopDecoding st contents p end statePtr codepointPtr) = do-        x <- liftIO $ peekWith contents p+        x <- liftIO $ peekAt p contents         let (Tuple' sv cp) = decode1 table statePtr codepointPtr x         return $             case sv of@@ -792,7 +793,7 @@                     Skip $                     transliterateOrError                         (-                           "Streamly.Internal.Data.Stream.StreamD."+                           "Streamly.Internal.Data.Stream."                         ++ "decodeUtf8ArraysWith: Invalid UTF8"                         ++ " codepoint encountered"                         )@@ -1026,7 +1027,7 @@ -- | Fold each line of the stream using the supplied 'Fold' -- and stream the result. ----- >>> Stream.fold Fold.toList $ lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")+-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n") -- ["lines","this","string","",""] -- -- > lines = Stream.splitOnSuffix (== '\n')@@ -1057,7 +1058,7 @@ -- | Fold each word of the stream using the supplied 'Fold' -- and stream the result. ----- >>>  Stream.fold Fold.toList $ words Fold.toList (Stream.fromList "fold these     words")+-- >>>  Stream.fold Fold.toList $ Unicode.words Fold.toList (Stream.fromList "fold these     words") -- ["fold","these","words"] -- -- > words = Stream.wordsBy isSpace
src/Streamly/Internal/Unicode/String.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE TemplateHaskell #-}+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Internal.Unicode.String -- Copyright   : (c) 2022 Composewell Technologies@@ -31,7 +32,14 @@ -- using Haskell functions.  module Streamly.Internal.Unicode.String-    ( str+    (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup++    str     ) where  @@ -49,11 +57,8 @@ import qualified Streamly.Data.Stream as Stream  (fromList, parse) import qualified Streamly.Internal.Unicode.Parser as Parser --- $setup--- >>> :m--- >>> :set -XQuasiQuotes--- >>> import Streamly.Internal.Unicode.String---+#include "DocTestUnicodeString.hs"+ -------------------------------------------------------------------------------- -- Parsing --------------------------------------------------------------------------------
src/Streamly/Unicode/Parser.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Unicode.Parser -- Copyright   : (c) 2021 Composewell Technologies@@ -12,9 +13,15 @@  module Streamly.Unicode.Parser     (-     -- * Single Chars+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup -     -- Any char+    -- * Single Chars++    -- Any char       char     , charIgnoreCase @@ -50,6 +57,7 @@     -- * Digit Sequences (Numbers)     , decimal     , hexadecimal+    , double      -- * Modifiers     , signed@@ -57,3 +65,5 @@ where  import Streamly.Internal.Unicode.Parser++#include "DocTestUnicodeParser.hs"
src/Streamly/Unicode/Stream.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Unicode.Stream -- Copyright   : (c) 2020 Composewell Technologies@@ -24,14 +25,11 @@ -- routines in this module and then written to IO devices or to arrays in -- memory. ----- If you have to store a 'Char' stream in memory you can convert it into a--- 'String' using 'Streamly.Data.Fold.toList' fold. The 'String' type can be--- more efficient than pinned arrays for short and short lived strings.------ For longer or long lived streams you can 'Streamly.Data.Stream.fold' the+-- If you have to store a 'Char' stream in memory you can+-- 'Streamly.Data.Stream.fold' the -- 'Char' stream as @Array Char@ using the array 'Streamly.Data.Array.write'--- fold.  The 'Array' type provides a more compact representation and pinned--- memory reducing GC overhead. If space efficiency is a concern you can use+-- fold.  The 'Array' type provides a more compact representation+-- reducing GC overhead. If space efficiency is a concern you can use -- 'encodeUtf8'' on the 'Char' stream before writing it to an 'Array' providing -- an even more compact representation. --@@ -69,12 +67,15 @@ -- Some experimental APIs to conveniently process text using the -- @Array Char@ represenation directly can be found in -- "Streamly.Internal.Unicode.Array".---- XXX an unpinned array representation can be useful to store short and short--- lived strings in memory. -- module Streamly.Unicode.Stream     (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup+     -- * Construction (Decoding)       decodeLatin1     , decodeUtf8@@ -105,3 +106,5 @@  import Streamly.Internal.Unicode.Stream import Prelude hiding (lines, words, unlines, unwords)++#include "DocTestUnicodeStream.hs"
src/Streamly/Unicode/String.hs view
@@ -1,3 +1,4 @@+{-# LANGUAGE CPP #-} -- | -- Module      : Streamly.Unicode.String -- Copyright   : (c) 2022 Composewell Technologies@@ -7,10 +8,23 @@ -- Portability : GHC -- -- Convenient template Haskell quasiquoters to format strings.+--+-- The 'str' quasiquoter retains newlines in the string when the line is split+-- across multiple lines. The @unwords . lines@ idiom can be used on the+-- resulting string to collapse it into a single line.  module Streamly.Unicode.String-    ( str+    (+    -- * Setup+    -- | To execute the code examples provided in this module in ghci, please+    -- run the following commands first.+    --+    -- $setup++    str     ) where  import Streamly.Internal.Unicode.String++#include "DocTestUnicodeString.hs"
streamly-core.cabal view
@@ -1,8 +1,11 @@ cabal-version:      2.2 name:               streamly-core-version:            0.1.0+version:            0.2.0 synopsis:           Streaming, parsers, arrays and more description:+  For upgrading to streamly-0.9.0+ please read the+  <https://github.com/composewell/streamly/blob/streamly-0.10.0/docs/User/Project/Upgrading-0.8-to-0.9.md Streamly-0.9.0 upgrade guide>.+  .   Streamly consists of two packages: "streamly-core" and "streamly".   <https://hackage.haskell.org/package/streamly-core streamly-core>   provides basic features, and depends only on GHC boot libraries (see@@ -27,8 +30,10 @@                    , GHC==8.8.4                    , GHC==8.10.7                    , GHC==9.0.2-                   , GHC==9.2.7-                   , GHC==9.4.4+                   , GHC==9.2.8+                   , GHC==9.4.7+                   , GHC==9.6.3+                   , GHC==9.8.1 author:              Composewell Technologies maintainer:          streamly@composewell.com copyright:           2017 Composewell Technologies@@ -48,9 +53,14 @@     src/DocTestDataMutArray.hs     src/DocTestDataMutArrayGeneric.hs     src/DocTestDataParser.hs+    src/DocTestDataParserK.hs     src/DocTestDataStream.hs     src/DocTestDataStreamK.hs     src/DocTestDataUnfold.hs+    src/DocTestUnicodeParser.hs+    src/DocTestUnicodeStream.hs+    src/DocTestUnicodeString.hs+    src/DocTestFileSystemHandle.hs      -- This is duplicated     src/Streamly/Internal/Data/Array/ArrayMacros.h@@ -70,6 +80,7 @@     Changelog.md     docs/*.md     docs/ApiChangelogs/0.1.0.txt+    docs/ApiChangelogs/0.1.0-0.2.0.txt  source-repository head     type: git@@ -90,16 +101,6 @@   manual: True   default: False -flag no-fusion-  description: Disable rewrite rules for stream fusion-  manual: True-  default: False--flag use-c-malloc-  description: Use C malloc instead of GHC malloc-  manual: True-  default: False- flag opt   description: off=GHC default, on=-O2   manual: True@@ -132,9 +133,6 @@ common compile-options     default-language: Haskell2010 -    if flag(no-fusion)-      cpp-options:    -DDISABLE_FUSION-     if flag(dev)       cpp-options:    -DDEVBUILD @@ -144,9 +142,6 @@     if flag(use-folds)       cpp-options:    -DUSE_FOLDS_EVERYWHERE -    if flag(use-c-malloc)-      cpp-options:    -DUSE_C_MALLOC-     ghc-options:    -Weverything                     -Wno-implicit-prelude                     -Wno-missing-deriving-strategies@@ -167,6 +162,10 @@         -Wno-redundant-bang-patterns         -Wno-operator-whitespace +    if impl(ghc >= 9.8)+      ghc-options:+        -Wno-missing-role-annotations+     if flag(has-llvm)       ghc-options: -fllvm @@ -226,6 +225,7 @@    -- For this to be effective it must come after the -O2 option   if flag(dev) || flag(debug) || !flag(opt)+    cpp-options: -DDEBUG     ghc-options: -fno-ignore-asserts  common threading-options@@ -289,81 +289,46 @@                      -- streamly-time                      , Streamly.Internal.Data.Time.TimeSpec                      , Streamly.Internal.Data.Time.Units-                     , Streamly.Internal.Data.Time.Clock.Type                      , Streamly.Internal.Data.Time.Clock                       -- streamly-core-stream-types                      , Streamly.Internal.Data.SVar.Type-                     , Streamly.Internal.Data.Stream.StreamK.Type-                     , Streamly.Internal.Data.Fold.Step                      , Streamly.Internal.Data.Refold.Type-                     , Streamly.Internal.Data.Fold.Type-                     , Streamly.Internal.Data.Stream.StreamD.Step-                     , Streamly.Internal.Data.Stream.StreamD.Type-                     , Streamly.Internal.Data.Unfold.Type-                     , Streamly.Internal.Data.Producer.Type                      , Streamly.Internal.Data.Producer-                     , Streamly.Internal.Data.Producer.Source-                     , Streamly.Internal.Data.Parser.ParserK.Type-                     , Streamly.Internal.Data.Parser.ParserD.Type-                     , Streamly.Internal.Data.Pipe.Type                       -- streamly-core-array-types-                     , Streamly.Internal.Data.Unboxed-                    -- Unboxed IORef-                     , Streamly.Internal.Data.IORef.Unboxed+                     , Streamly.Internal.Data.MutByteArray++                     -- streaming and parsing Haskell types to/from bytes+                     , Streamly.Internal.Data.Binary.Parser+                     , Streamly.Internal.Data.Binary.Stream+                      -- May depend on streamly-core-stream-                     , Streamly.Internal.Data.Array.Mut.Type-                     , Streamly.Internal.Data.Array.Mut-                     , Streamly.Internal.Data.Array.Type-                     , Streamly.Internal.Data.Array.Generic.Mut.Type+                     , Streamly.Internal.Data.MutArray+                     , Streamly.Internal.Data.MutArray.Generic                       -- streamly-core-streams-                     , Streamly.Internal.Data.Stream.StreamK+                     , Streamly.Internal.Data.StreamK                      -- StreamD depends on streamly-array-types-                     , Streamly.Internal.Data.Stream.StreamD.Generate-                     , Streamly.Internal.Data.Stream.StreamD.Eliminate-                     , Streamly.Internal.Data.Stream.StreamD.Nesting-                     , Streamly.Internal.Data.Stream.StreamD.Transform-                     , Streamly.Internal.Data.Stream.StreamD.Exception-                     , Streamly.Internal.Data.Stream.StreamD.Lift-                     , Streamly.Internal.Data.Stream.StreamD.Top                      , Streamly.Internal.Data.Stream.StreamD-                     , Streamly.Internal.Data.Stream.Common                      , Streamly.Internal.Data.Stream -                     , Streamly.Internal.Data.Parser.ParserD.Tee-                     , Streamly.Internal.Data.Parser.ParserD-                      -- streamly-core-data                      , Streamly.Internal.Data.Builder                      , Streamly.Internal.Data.Unfold-                     , Streamly.Internal.Data.Unfold.Enumeration-                     , Streamly.Internal.Data.Fold.Tee-                     , Streamly.Internal.Data.Fold                      , Streamly.Internal.Data.Fold.Chunked-                     , Streamly.Internal.Data.Fold.Window                      , Streamly.Internal.Data.Parser+                     , Streamly.Internal.Data.ParserK                      , Streamly.Internal.Data.Pipe -                     -- streamly-transformers (non-base)-                     , Streamly.Internal.Data.Stream.StreamD.Transformer-                     , Streamly.Internal.Data.Stream.StreamK.Transformer-                      -- streamly-containers (non-base)-                     , Streamly.Internal.Data.Stream.StreamD.Container-                     , Streamly.Internal.Data.Fold.Container--                     , Streamly.Internal.Data.Stream.Chunked+                     , Streamly.Internal.Data.Fold                       -- streamly-core-data-arrays                      , Streamly.Internal.Data.Array.Generic                      , Streamly.Internal.Data.Array-                     , Streamly.Internal.Data.Array.Mut.Stream--                     -- streamly-serde-                     , Streamly.Internal.Serialize.FromBytes-                     , Streamly.Internal.Serialize.ToBytes+                     , Streamly.Internal.Data.MutArray.Stream+                     , Streamly.Internal.Data.Array.Stream                      -- streamly-unicode-core                      , Streamly.Internal.Unicode.Stream@@ -377,8 +342,8 @@                      , Streamly.Internal.FileSystem.Dir                      -- Ring Arrays-                     , Streamly.Internal.Data.Ring.Unboxed                      , Streamly.Internal.Data.Ring+                     , Streamly.Internal.Data.Ring.Generic                       -- streamly-console                      , Streamly.Internal.Console.Stdio@@ -409,6 +374,7 @@                      -- streamly-core released modules in alphabetic order                      -- NOTE: these must be added to streamly.cabal as well                      , Streamly.Console.Stdio+                     , Streamly.Data.MutByteArray                      , Streamly.Data.Array                      , Streamly.Data.Array.Generic                      , Streamly.Data.MutArray@@ -425,27 +391,64 @@                      , Streamly.Unicode.Parser                      , Streamly.Unicode.Stream                      , Streamly.Unicode.String+    other-modules:+                      Streamly.Internal.Data.Fold.Step+                    , Streamly.Internal.Data.Fold.Type+                    , Streamly.Internal.Data.Fold.Combinators+                    , Streamly.Internal.Data.Fold.Container+                    , Streamly.Internal.Data.Fold.Tee+                    , Streamly.Internal.Data.Fold.Window +                    , Streamly.Internal.Data.Parser.Type+                    , Streamly.Internal.Data.Parser.Tee+                    , Streamly.Internal.Data.ParserK.Type++                    , Streamly.Internal.Data.Stream.Container+                    , Streamly.Internal.Data.Stream.Eliminate+                    , Streamly.Internal.Data.Stream.Exception+                    , Streamly.Internal.Data.Stream.Generate+                    , Streamly.Internal.Data.Stream.Lift+                    , Streamly.Internal.Data.Stream.Nesting+                    , Streamly.Internal.Data.Stream.Step+                    , Streamly.Internal.Data.Stream.Top+                    , Streamly.Internal.Data.Stream.Transform+                    , Streamly.Internal.Data.Stream.Transformer+                    , Streamly.Internal.Data.Stream.Type++                    , Streamly.Internal.Data.StreamK.Type+                    , Streamly.Internal.Data.StreamK.Transformer++                    , Streamly.Internal.Data.Pipe.Type++                    , Streamly.Internal.Data.Unfold.Type+                    , Streamly.Internal.Data.Unfold.Enumeration++                    , Streamly.Internal.Data.MutArray.Type++                    , Streamly.Internal.Data.Array.Type++                    -- Unboxed IORef+                    , Streamly.Internal.Data.IORef.Unboxed++                    , Streamly.Internal.Data.MutByteArray.Type+                    , Streamly.Internal.Data.Unbox+                    , Streamly.Internal.Data.Unbox.TH+                    , Streamly.Internal.Data.Serialize.Type+                    , Streamly.Internal.Data.Serialize.TH+                    , Streamly.Internal.Data.Serialize.TH.RecHeader+                    , Streamly.Internal.Data.Serialize.TH.Common+                    , Streamly.Internal.Data.Serialize.TH.Bottom++                    , Streamly.Internal.Data.Producer.Type+                    , Streamly.Internal.Data.Producer.Source++                    , Streamly.Internal.Data.Time.Clock.Type+     if flag(dev)       exposed-modules:-                        Streamly.Internal.Data.Stream.StreamK.Alt-                      , Streamly.Internal.Data.Stream.Type-                      , Streamly.Internal.Data.Stream.Eliminate-                      , Streamly.Internal.Data.Stream.Enumerate-                      , Streamly.Internal.Data.Stream.Generate-                      , Streamly.Internal.Data.Stream.Transform-                      , Streamly.Internal.Data.Stream.Bottom-                      , Streamly.Internal.Data.Stream.Exception-                      , Streamly.Internal.Data.Stream.Expand-                      , Streamly.Internal.Data.Stream.Lift-                      , Streamly.Internal.Data.Stream.Reduce-                      , Streamly.Internal.Data.Stream.Transformer-                      , Streamly.Internal.Data.Stream.StreamDK-                      , Streamly.Internal.Data.Stream.Zip-                      , Streamly.Internal.Data.Stream.Cross-                      , Streamly.Internal.Data.List-                      , Streamly.Data.Stream.Zip-                      --, Streamly.Internal.Data.Parser.ParserDK+                        Streamly.Internal.Data.StreamK.Alt+                        -- XXX Compilation needs to be fixed+                      -- , Streamly.Internal.Data.List      build-depends:                     -- streamly-base@@ -459,21 +462,33 @@                     -- packages depending on the "ghc" package (packages                     -- depending on doctest is a common example) can                     -- depend on streamly.-                       ghc-prim          >= 0.5.3 && < 0.10+                       ghc-prim          >= 0.5.3 && < 0.12                      , fusion-plugin-types >= 0.1 && < 0.2-                     , base              >= 4.12  && < 4.19+                     , base              >= 4.12  && < 4.20                      , exceptions        >= 0.8.0 && < 0.11                      , transformers      >= 0.5.5 && < 0.7                      , filepath          >= 1.4.2 && < 1.5                      -- streamly-unicode-core-                     , template-haskell  >= 2.14  && < 2.20+                     , template-haskell  >= 2.14  && < 2.22                       -- streamly-filesystem-core                      , directory         >= 1.3.3 && < 1.4                       -- XXX to be removed-                     , containers        >= 0.6.0 && < 0.7+                     , containers        >= 0.6.0 && < 0.8                      , heaps             >= 0.3   && < 0.5++    if impl(ghc >= 9.0)+      build-depends:  ghc-bignum >= 1.0  && < 2+    else+      build-depends:  integer-gmp >= 1.0 && < 1.2+     if !flag(use-unliftio)       build-depends:   monad-control     >= 1.0 && < 1.1++    if os(linux) || os (darwin)+      build-depends: unix              >= 2.7.0 && < 2.9++    if os(windows)+      build-depends: Win32            >= 2.6 && < 2.14