on master help sign in
46 files changed+1450−587
since splitsnapshots
unifiedsplit
core/docs/ApiChangelogs/0.2.0-0.2.2.txt+306 core/src/DocTestDataStream.hs+2 core/src/DocTestFileSystemHandle.hs+1−1 core/src/Streamly/Data/Array.hs+30−27 core/src/Streamly/Data/Stream.hs+129−20 core/src/Streamly/Internal/Data/Array.hs+30−5 core/src/Streamly/Internal/Data/Array/Type.hs+115−44 core/src/Streamly/Internal/Data/IsMap.hs+4 core/src/Streamly/Internal/Data/MutArray.hs+20−3 core/src/Streamly/Internal/Data/MutArray/Type.hs+152−84 core/src/Streamly/Internal/Data/MutByteArray/Type.hs+21−9 core/src/Streamly/Internal/Data/Serialize/TH.hs+5−1 core/src/Streamly/Internal/Data/Stream/Container.hs+56−48 core/src/Streamly/Internal/Data/Stream/Eliminate.hs+33−25 core/src/Streamly/Internal/Data/Stream/Exception.hs+9−6 core/src/Streamly/Internal/Data/Stream/Generate.hs−12 core/src/Streamly/Internal/Data/Stream/Nesting.hs+10−64 core/src/Streamly/Internal/Data/Stream/Top.hs+243−115 core/src/Streamly/Internal/Data/Stream/Transform.hs+52−26 core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1 core/src/Streamly/Internal/Data/Stream/Type.hs+37−3 core/src/Streamly/Internal/Data/StreamK.hs+19−8 core/src/Streamly/Internal/Data/StreamK/Type.hs+60−11 core/src/Streamly/Internal/Data/Time/Units.hs−4 core/src/Streamly/Internal/Data/Unbox.hs+4−1 core/src/Streamly/Internal/Data/Unfold.hs+6−4 core/src/Streamly/Internal/FileSystem/File.hs+2−2 core/src/Streamly/Internal/FileSystem/Handle.hs+14−15 core/src/Streamly/Internal/Unicode/Stream.hs+1−1 core/src/Streamly/Internal/Unicode/String.hs+7−7 core/streamly-core.cabal+13−9 docs/Developer/optimization-guidelines.md+12 docs/User/Project/README.md+1 src/Streamly/Internal/Data/IsMap/HashMap.hs+5 src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5 src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1 src/Streamly/Internal/Data/Stream/Serial.hs+5 src/Streamly/Internal/Data/Stream/Zip.hs+5 src/Streamly/Internal/FileSystem/Event.hs+3 src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1 src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2 src/Streamly/Internal/FileSystem/FD.hs+5−5 src/Streamly/Internal/Network/Inet/TCP.hs+3−3 src/Streamly/Internal/Network/Socket.hs+7−7 streamly.cabal+10−10 test/Streamly/Test/Data/Array.hs+2−2
core/docs/ApiChangelogs/0.2.0-0.2.2.txtnew file+306
@@ -0,0 +1,306 @@
1+---------------------------------
2+API Annotations
3+---------------------------------
4+
5+[A] : Added
6+[R] : Removed
7+[C] : Changed
8+[O] : Old definition
9+[N] : New definition
10+[D] : Deprecated
11+
12+---------------------------------
13+API diff
14+---------------------------------
15+
16+[C] Streamly.Data.Stream
17+ [A] (FixityR,5)
18+ [A] (FixityR,5)
19+[C] Streamly.Data.MutArray.Generic
20+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
21+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
22+ [A] create :: MonadIO m => Fold m a (MutArray a)
23+[C] Streamly.Data.MutArray
24+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
25+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
26+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
27+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
28+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
29+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
30+[C] Streamly.Data.Array.Generic
31+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
32+ [A] create :: MonadIO m => Fold m a (Array a)
33+[C] Streamly.Data.Array
34+ [A] class Serialize a
35+ [A] serializeAt :: Serialize a => Int -> MutByteArray -> a -> IO Int
36+ [A] pinnedSerialize :: Serialize a => a -> Array Word8
37+ [A] deserializeAt :: Serialize a => Int -> MutByteArray -> Int -> IO (Int, a)
38+ [A] deserialize :: Serialize a => Array Word8 -> a
39+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
40+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
41+ [A] addSizeTo :: Serialize a => Int -> a -> Int
42+
43+---------------------------------
44+Internal API diff
45+---------------------------------
46+
47+[C] Streamly.Internal.Unicode.Stream
48+ [A] encodeUtf16le' :: Stream m Char -> Stream m Word16
49+ [A] decodeUtf16le' :: Stream m Word16 -> Stream m Char
50+[A] Streamly.Internal.FileSystem.Path
51+ [A] class IsPath a
52+ [A] Rel
53+ [A] File
54+ [A] Dir
55+ [A] Abs
56+ [A] Streamly.Internal.FileSystem.Path.IsPath
57+ [A] instance Streamly.Internal.FileSystem.Path.IsPath Streamly.Internal.FileSystem.Path.Path
58+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel Streamly.Internal.FileSystem.Path.Path)
59+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
60+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Rel (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
61+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path)
62+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path)
63+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs Streamly.Internal.FileSystem.Path.Path)
64+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.File Streamly.Internal.FileSystem.Path.Path))
65+ [A] instance Streamly.Internal.FileSystem.Path.IsPath (Streamly.Internal.FileSystem.Path.Abs (Streamly.Internal.FileSystem.Path.Dir Streamly.Internal.FileSystem.Path.Path))
66+ [A] GHC.Show.Show
67+ [A] instance GHC.Show.Show Streamly.Internal.FileSystem.Path.PathException
68+ [A] GHC.Exception.Type.Exception
69+ [A] instance GHC.Exception.Type.Exception Streamly.Internal.FileSystem.Path.PathException
70+ [A] GHC.Classes.Eq
71+ [A] instance GHC.Classes.Eq Streamly.Internal.FileSystem.Path.PathException
72+ [A] Path
73+ [A] Path :: Array Word8 -> Path
74+ [A] toString :: Path -> [Char]
75+ [A] toPath :: IsPath a => a -> Path
76+ [A] toChunk :: Path -> Array Word8
77+ [A] toChars :: Monad m => Path -> Stream m Char
78+ [A] relfile :: QuasiQuoter
79+ [A] reldir :: QuasiQuoter
80+ [A] rel :: QuasiQuoter
81+ [A] primarySeparator :: Char
82+ [A] path :: QuasiQuoter
83+ [A] mkRelFile :: String -> Q Exp
84+ [A] mkRelDir :: String -> Q Exp
85+ [A] mkRel :: String -> Q Exp
86+ [A] mkPath :: String -> Q Exp
87+ [A] mkFile :: String -> Q Exp
88+ [A] mkDir :: String -> Q Exp
89+ [A] mkAbsFile :: String -> Q Exp
90+ [A] mkAbsDir :: String -> Q Exp
91+ [A] mkAbs :: String -> Q Exp
92+ [A] isSeparator :: Char -> Bool
93+ [A] fromString :: MonadThrow m => [Char] -> m Path
94+ [A] fromPathUnsafe :: IsPath a => Path -> a
95+ [A] fromPath :: (IsPath a, MonadThrow m) => Path -> m a
96+ [A] fromChunkUnsafe :: Array Word8 -> Path
97+ [A] fromChunk :: MonadThrow m => Array Word8 -> m Path
98+ [A] fromChars :: MonadThrow m => Stream Identity Char -> m Path
99+ [A] file :: QuasiQuoter
100+ [A] extendPath :: Path -> Path -> Path
101+ [A] extendDir :: (IsPath (a (Dir Path)), IsPath b, IsPath (a b)) => a (Dir Path) -> Rel b -> a b
102+ [A] dir :: QuasiQuoter
103+ [A] adaptPath :: (MonadThrow m, IsPath a, IsPath b) => a -> m b
104+ [A] absfile :: QuasiQuoter
105+ [A] absdir :: QuasiQuoter
106+ [A] abs :: QuasiQuoter
107+[C] Streamly.Internal.Data.Stream
108+ [A] (FixityR,5)
109+ [A] (FixityR,5)
110+ [C] splitInnerBySuffix
111+ [O] 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)
112+ [N] splitInnerBySuffix :: Monad m => (f a -> Bool) -> (f a -> m (f a, Maybe (f a))) -> (f a -> f a -> m (f a)) -> Stream m (f a) -> Stream m (f a)
113+ [D] sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
114+ [A] indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
115+[C] Streamly.Internal.Data.MutByteArray
116+ [A] unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
117+ [A] unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
118+ [D] nil :: MutByteArray
119+ [A] empty :: MutByteArray
120+ [D] asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
121+[D] Streamly.Internal.Data.MutArray.Stream
122+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
123+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
124+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
125+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
126+[C] Streamly.Internal.Data.MutArray.Generic
127+ [D] writeWith :: MonadIO m => Int -> Fold m a (MutArray a)
128+ [D] writeNUnsafe :: MonadIO m => Int -> Fold m a (MutArray a)
129+ [A] unsafeCreateOf :: MonadIO m => Int -> Fold m a (MutArray a)
130+ [A] emptyOf :: MonadIO m => Int -> m (MutArray a)
131+ [A] createWith :: MonadIO m => Int -> Fold m a (MutArray a)
132+ [A] createOf :: MonadIO m => Int -> Fold m a (MutArray a)
133+ [A] create :: MonadIO m => Fold m a (MutArray a)
134+[C] Streamly.Internal.Data.MutArray
135+ [A] SpliceState
136+ [A] SpliceYielding :: arr -> SpliceState s arr -> SpliceState s arr
137+ [A] SpliceInitial :: s -> SpliceState s arr
138+ [A] SpliceFinish :: SpliceState s arr
139+ [A] SpliceBuffering :: s -> arr -> SpliceState s arr
140+ [R] MutByteArray
141+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
142+ [D] writeRevN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
143+ [D] writeNWithUnsafe :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
144+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
145+ [D] writeChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
146+ [D] writeAppendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
147+ [D] writeAppendNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
148+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
149+ [A] unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
150+ [A] unsafeCreateOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
151+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
152+ [A] unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
153+ [A] unsafeAppendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
154+ [A] toStreamWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
155+ [A] toStreamRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
156+ [R] toStreamDWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
157+ [R] toStreamDRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> MutArray a -> Stream m a
158+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
159+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
160+ [A] revCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
161+ [D] resizeExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
162+ [D] resize :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
163+ [A] pokeSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
164+ [A] pokeAppendMay :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (Maybe (MutArray Word8))
165+ [A] pokeAppend :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> a -> m (MutArray Word8)
166+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
167+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
168+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
169+ [D] pinnedNewBytes :: MonadIO m => Int -> m (MutArray a)
170+ [A] pinnedEmptyOf :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a)
171+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
172+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
173+ [A] pinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
174+ [A] peekUnconsUnsafe :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (a, MutArray Word8)
175+ [A] peekUncons :: forall m a. (MonadIO m, Unbox a) => MutArray Word8 -> m (Maybe a, MutArray Word8)
176+ [A] peekSkipUnsafe :: Int -> MutArray Word8 -> MutArray Word8
177+ [A] pPinnedCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
178+ [A] pCompactLE :: forall m a. (MonadIO m, Unbox a) => Int -> Parser (MutArray a) m (MutArray a)
179+ [D] nil :: MutArray a
180+ [A] lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
181+ [A] lCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
182+ [A] indexReaderWith :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
183+ [A] indexReader :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
184+ [A] growExp :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
185+ [A] grow :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
186+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (MutArray a)
187+ [R] getIndicesD :: (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m Int -> Unfold m (MutArray a) a
188+ [D] getIndices :: (MonadIO m, Unbox a) => Stream m Int -> Unfold m (MutArray a) a
189+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (MutArray a) (Int, Int)
190+ [A] fromStreamN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
191+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (MutArray a)
192+ [D] fromStreamD :: (MonadIO m, Unbox a) => Stream m a -> m (MutArray a)
193+ [A] fromPureStreamN :: (MonadIO m, Unbox a) => Int -> Stream Identity a -> m (MutArray a)
194+ [A] fromPtrN :: MonadIO m => Int -> Ptr Word8 -> m (MutArray Word8)
195+ [A] fromChunksRealloced :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> m (MutArray a)
196+ [A] fromChunksK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
197+ [A] fromByteStr# :: MonadIO m => Addr# -> m (MutArray Word8)
198+ [D] fromArrayStreamK :: (Unbox a, MonadIO m) => StreamK m (MutArray a) -> m (MutArray a)
199+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
200+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
201+ [A] fPinnedCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
202+ [A] fCompactGE :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m (MutArray a) (MutArray a)
203+ [A] emptyOf :: (MonadIO m, Unbox a) => Int -> m (MutArray a)
204+ [A] empty :: MutArray a
205+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
206+ [A] createOfWith :: forall m a. (MonadIO m, Unbox a) => (Int -> m (MutArray a)) -> Int -> Fold m a (MutArray a)
207+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (MutArray a)
208+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (MutArray a)
209+ [A] concatWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
210+ [A] concatRevWith :: forall m a. (Monad m, Unbox a) => (forall b. IO b -> m b) -> Stream m (MutArray a) -> Stream m a
211+ [A] concatRev :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
212+ [A] concat :: forall m a. (MonadIO m, Unbox a) => Stream m (MutArray a) -> Stream m a
213+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
214+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (MutArray Word8) -> Stream m (MutArray Word8)
215+ [A] compactLeAs :: forall m a. (MonadIO m, Unbox a) => PinnedState -> Int -> Stream m (MutArray a) -> Stream m (MutArray a)
216+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
217+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
218+ [A] compactEQ :: Int -> Stream m (MutArray a) -> Stream m (MutArray a)
219+ [D] cmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
220+ [A] byteEq :: MonadIO m => MutArray a -> MutArray a -> m Bool
221+ [A] byteCmp :: MonadIO m => MutArray a -> MutArray a -> m Ordering
222+ [A] buildChunks :: (MonadIO m, Unbox a) => Int -> Fold m a (StreamK n (MutArray a))
223+ [D] asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
224+ [A] appendWith :: forall m a. (MonadIO m, Unbox a) => (Int -> Int) -> m (MutArray a) -> Fold m a (MutArray a)
225+ [A] appendN :: forall m a. (MonadIO m, Unbox a) => Int -> m (MutArray a) -> Fold m a (MutArray a)
226+ [A] append :: forall m a. (MonadIO m, Unbox a) => m (MutArray a) -> Fold m a (MutArray a)
227+[D] Streamly.Internal.Data.Array.Stream
228+ [C] interposeSuffix
229+ [O] interposeSuffix :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
230+ [N] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
231+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
232+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
233+ [C] concatRev
234+ [O] concatRev :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
235+ [N] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
236+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
237+[C] Streamly.Internal.Data.Array.Generic
238+ [A] createOf :: MonadIO m => Int -> Fold m a (Array a)
239+ [A] create :: MonadIO m => Fold m a (Array a)
240+[C] Streamly.Internal.Data.Array
241+ [R] ArrayUnsafe
242+ [D] writeWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
243+ [D] writeNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
244+ [A] unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
245+ [A] unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
246+ [A] unsafeCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
247+ [D] toStreamDRev :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
248+ [D] toStreamD :: forall m a. (Monad m, Unbox a) => Array a -> Stream m a
249+ [C] splice
250+ [O] splice :: (MonadIO m, Unbox a) => Array a -> Array a -> m (Array a)
251+ [N] splice :: MonadIO m => Array a -> Array a -> m (Array a)
252+ [A] slicerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
253+ [A] sliceIndexerFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
254+ [D] pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
255+ [D] pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a) => Int -> Int -> Fold m a (Array a)
256+ [D] pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
257+ [D] pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
258+ [A] pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
259+ [A] pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
260+ [A] pinnedCompactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
261+ [A] parseBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Parser a m b -> StreamK m (Array a) -> m (Either ParseError b, StreamK m (Array a))
262+ [D] nil :: Array a
263+ [A] lPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
264+ [A] lCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) () -> Fold m (Array a) ()
265+ [A] interposeSuffix :: forall m a. (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
266+ [A] interpose :: (Monad m, Unbox a) => a -> Stream m (Array a) -> Stream m a
267+ [A] intercalateSuffix :: (Monad m, Unbox a) => Array a -> Stream m (Array a) -> Stream m a
268+ [A] indexReaderFromThenTo :: Unfold m (Int, Int, Int, Array a) a
269+ [A] indexReader :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
270+ [A] indexFinder :: (a -> Bool) -> Unfold Identity (Array a) Int
271+ [D] getSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Array a)
272+ [R] getIndicesFromThenTo :: Unfold m (Int, Int, Int, Array a) a
273+ [D] getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
274+ [D] genSlicesFromLen :: forall m a. (Monad m, Unbox a) => Int -> Int -> Unfold m (Array a) (Int, Int)
275+ [D] fromStreamDN :: forall m a. (MonadIO m, Unbox a) => Int -> Stream m a -> m (Array a)
276+ [D] fromStreamD :: forall m a. (MonadIO m, Unbox a) => Stream m a -> m (Array a)
277+ [A] fromPureStreamN :: Unbox a => Int -> Stream Identity a -> Array a
278+ [A] fromPtrN :: Int -> Ptr Word8 -> Array Word8
279+ [A] fromChunksK :: (MonadIO m, Unbox a) => StreamK m (Array a) -> m (Array a)
280+ [A] fromChunks :: (MonadIO m, Unbox a) => Stream m (Array a) -> m (Array a)
281+ [A] fromByteStr :: Ptr Word8 -> Array Word8
282+ [A] foldChunks :: (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m b
283+ [A] foldBreakChunksK :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> StreamK m (Array a) -> m (b, StreamK m (Array a))
284+ [A] foldBreakChunks :: forall m a b. (MonadIO m, Unbox a) => Fold m a b -> Stream m (Array a) -> m (b, Stream m (Array a))
285+ [D] flattenArraysRev :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
286+ [D] flattenArrays :: forall m a. (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
287+ [C] findIndicesOf
288+ [O] findIndicesOf :: (a -> Bool) -> Unfold Identity (Array a) Int
289+ [N] findIndicesOf :: (a -> Bool) -> Array a -> Stream Identity Int
290+ [A] fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
291+ [A] fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
292+ [A] empty :: Array a
293+ [A] createWith :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
294+ [A] createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
295+ [A] create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
296+ [A] concatRev :: forall m a. (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
297+ [A] concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
298+ [A] compactOnByteSuffix :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
299+ [A] compactOnByte :: MonadIO m => Word8 -> Stream m (Array Word8) -> Stream m (Array Word8)
300+ [A] compactLE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
301+ [A] compactGE :: (MonadIO m, Unbox a) => Int -> Stream m (Array a) -> Stream m (Array a)
302+ [A] byteEq :: Array a -> Array a -> Bool
303+ [A] byteCmp :: Array a -> Array a -> Ordering
304+ [A] buildChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
305+ [D] bufferChunks :: (MonadIO m, Unbox a) => Stream m a -> m (StreamK m (Array a))
306+ [D] asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
core/src/DocTestDataStream.hs+2
@@ -6,6 +6,7 @@
66>>> import Control.Monad.IO.Class (MonadIO (liftIO))
77>>> import Control.Monad.Trans.Class (lift)
88>>> import Control.Monad.Trans.Identity (runIdentityT)
9+>>> import Data.Char (isSpace)
910>>> import Data.Either (fromLeft, fromRight, isLeft, isRight, either)
1011>>> import Data.Maybe (fromJust, isJust)
1112>>> import Data.Function (fix, (&))
@@ -32,6 +33,7 @@ For APIs that have not been released yet.
3233>>> import qualified Streamly.Internal.Data.Fold as Fold
3334>>> import qualified Streamly.Internal.Data.Parser as Parser
3435>>> import qualified Streamly.Internal.Data.Stream as Stream
36+>>> import qualified Streamly.Internal.Data.StreamK as StreamK
3537>>> import qualified Streamly.Internal.Data.Unfold as Unfold
3638>>> import qualified Streamly.Internal.FileSystem.Dir as Dir
3739-}
core/src/DocTestFileSystemHandle.hs+1−1
@@ -8,7 +8,7 @@
88
99For APIs that have not been released yet.
1010
11−>>> import qualified Streamly.Internal.Data.Array as Array (writeNUnsafe)
11+>>> import qualified Streamly.Internal.Data.Array as Array (unsafeCreateOf)
1212>>> import qualified Streamly.Internal.Data.Unfold as Unfold (first)
1313>>> import qualified Streamly.Internal.FileSystem.Handle as Handle
1414>>> import qualified Streamly.Internal.System.IO as IO (defaultChunkSize)
core/src/Streamly/Data/Array.hs+30−27
@@ -31,42 +31,42 @@ module Streamly.Data.Array
3131 -- * The Array Type
3232 Array
3333
34+ -- * Pinning & Unpinning
35+ -- | Arrays are created unpinned by default unless pinned versions of
36+ -- creation APIs are used. Look for APIs with @pinned@ prefix in
37+ -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
38+ -- If an array is to be sent to the OS without any further modification
39+ -- then it should be created pinned in the first place instead of pinning
40+ -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
41+ -- APIs create a pinned array directly or convert an unpinned array to
42+ -- pinned array before sending it to the OS.
43+ , pin
44+ , unpin
45+ , isPinned
46+
3447 -- * Construction
3548 -- | When performance matters, the fastest way to generate an array is
36− -- 'writeN'. 'IsList' and 'IsString' instances can be
49+ -- 'createOf'. 'IsList' and 'IsString' instances can be
3750 -- used to conveniently construct arrays from literal values.
3851 -- 'OverloadedLists' extension or 'fromList' can be used to construct an
3952 -- array from a list literal. Similarly, 'OverloadedStrings' extension or
4053 -- 'fromList' can be used to construct an array from a string literal.
4154
42− -- Pure List APIs
55+ -- ** From Stream
56+ , createOf
57+ , create
58+ , writeLastN -- drop old (ring buffer)
59+
60+ -- ** From List
4361 , fromListN
4462 , fromList
4563
46− -- Monadic APIs
47− , writeN -- drop new
48− , write -- full buffer
49− , writeLastN -- drop old (ring buffer)
50−
51− -- * Conversion
64+ -- * To List
5265 -- 'GHC.Exts.toList' from "GHC.Exts" can be used to convert an array to a
5366 -- list.
5467 , toList
5568
56− -- * Pinning & Unpinning
57− -- | Arrays are created unpinned by default unless pinned versions of
58− -- creation APIs are used. Look for APIs with @pinned@ prefix in
59− -- "Streamly.Internal.Data.Array" for some unreleased pinned creation APIs.
60− -- If an array is to be sent to the OS without any further modification
61− -- then it should be created pinned in the first place instead of pinning
62− -- it later. Pinning an unpinned array has a copy overhead. OS interfacing
63− -- APIs create a pinned array directly or convert an unpinned array to
64− -- pinned array before sending it to the OS.
65− , pin
66− , unpin
67− , isPinned
68−
69− -- * Streams
69+ -- * To Stream
7070 , read
7171 , readRev
7272
@@ -90,6 +90,10 @@ module Streamly.Data.Array
9090 -- * Re-exports
9191 , Unbox (..)
9292 , Serialize(..)
93+
94+ -- * Deprecated
95+ , writeN -- drop new
96+ , write -- full buffer
9397 )
9498where
9599
@@ -122,7 +126,7 @@ import Prelude hiding (read, length)
122126--
123127-- Convert array to stream, transform, and fold back to array:
124128--
125−-- >>> amap f arr = Array.read arr & fmap f & Stream.fold Array.write
129+-- >>> amap f arr = Array.read arr & fmap f & Stream.fold (Array.createOf (Array.length arr))
126130-- >>> amap (+1) (Array.fromList [1,2,3::Int])
127131-- fromList [2,3,4]
128132--
@@ -149,12 +153,11 @@ import Prelude hiding (read, length)
149153-- See the 'fromPureStream' unreleased API to generate an array from an
150154-- Identity stream safely without using MonadIO constraint.
151155--
152−--
153156-- Note that 'Identity' streams can be generalized to IO streams:
154157--
155−-- >>> pure = Stream.fromList [1,2,3] :: Stream Identity Int
156−-- >>> generally = Stream.morphInner (return . runIdentity)
157−-- >>> Stream.fold Array.write (generally pure :: Stream IO Int)
158+-- >>> stream = Stream.fromList [1,2,3] :: Stream Identity Int
159+-- >>> pure2IO s = Stream.morphInner (return . runIdentity) s :: Stream IO Int
160+-- >>> Stream.fold Array.create (pure2IO stream)
158161-- fromList [1,2,3]
159162--
160163-- == Programming Tips
core/src/Streamly/Data/Stream.hs+129−20
@@ -70,6 +70,15 @@ module Streamly.Data.Stream
7070
7171 -- ** From Values
7272 -- | Generate a monadic stream from a seed value or values.
73+ --
74+ -- Non-empty pure streams can be cycled using repeat and unfold:
75+ --
76+ -- >>> cycle = Stream.unfoldMany Unfold.fromList . Stream.repeat
77+ --
78+ -- Non-empty StreamK can be cycled using append:
79+ --
80+ -- >>> cycle xs = let ys = xs `StreamK.append` ys in ys
81+ --
7382 , fromPure
7483 , fromEffect
7584 , repeat
@@ -212,21 +221,57 @@ module Streamly.Data.Stream
212221 -- operations like mapping a function over the stream.
213222 , foldrM
214223 , foldr
224+ -- foldr1
215225
216226 -- ** Specific Folds
217− -- | Usually you can use the folds in "Streamly.Data.Fold". However, some
218− -- folds that may be commonly used or may have an edge in performance in
219− -- some cases are provided here.
220− --
221− -- Useful idioms:
227+ -- | Streams are folded using folds in "Streamly.Data.Fold". Here are some
228+ -- idioms and equivalents of Data.List APIs using folds:
222229 --
223230 -- >>> foldlM' f a = Stream.fold (Fold.foldlM' f a)
224− -- >>> foldl1 f = Stream.fold (Fold.foldl1' f)
231+ -- >>> foldl1' f = Stream.fold (Fold.foldl1' f)
225232 -- >>> foldl' f a = Stream.fold (Fold.foldl' f a)
226233 -- >>> drain = Stream.fold Fold.drain
227234 -- >>> mapM_ f = Stream.fold (Fold.drainMapM f)
228235 -- >>> length = Stream.fold Fold.length
236+ -- >>> genericLength = Stream.fold Fold.lengthGeneric
229237 -- >>> head = Stream.fold Fold.one
238+ -- >>> last = Stream.fold Fold.latest
239+ -- >>> null = Stream.fold Fold.null
240+ -- >>> and = Stream.fold Fold.and
241+ -- >>> or = Stream.fold Fold.or
242+ -- >>> any p = Stream.fold (Fold.any p)
243+ -- >>> all p = Stream.fold (Fold.all p)
244+ -- >>> sum = Stream.fold Fold.sum
245+ -- >>> product = Stream.fold Fold.product
246+ -- >>> maximum = Stream.fold Fold.maximum
247+ -- >>> maximumBy cmp = Stream.fold (Fold.maximumBy cmp)
248+ -- >>> minimum = Stream.fold Fold.minimum
249+ -- >>> minimumBy cmp = Stream.fold (Fold.minimumBy cmp)
250+ -- >>> elem x = Stream.fold (Fold.elem x)
251+ -- >>> notElem x = Stream.fold (Fold.notElem x)
252+ -- >>> lookup x = Stream.fold (Fold.lookup x)
253+ -- >>> find p = Stream.fold (Fold.find p)
254+ -- >>> (!?) i = Stream.fold (Fold.index i)
255+ -- >>> genericIndex i = Stream.fold (Fold.indexGeneric i)
256+ -- >>> elemIndex x = Stream.fold (Fold.elemIndex x)
257+ -- >>> findIndex p = Stream.fold (Fold.findIndex p)
258+ --
259+ -- Some equivalents of Data.List APIs from the Stream module:
260+ --
261+ -- >>> head = fmap (fmap fst) . Stream.uncons
262+ -- >>> tail = fmap (fmap snd) . Stream.uncons
263+ -- >>> tail = Stream.tail -- unreleased API
264+ -- >>> init = Stream.init -- unreleased API
265+ --
266+ -- A Stream based toList fold implementation is provided below because it
267+ -- has a better performance compared to the fold.
268+
269+ -- Functions in Data.List, missing here:
270+ -- unsnoc = Stream.parseBreak (Parser.init Fold.toList)
271+ -- genericTake
272+ -- genericDrop
273+ -- genericSplitAt
274+ -- genericReplicate
230275 , toList
231276
232277 -- * Mapping
@@ -280,6 +325,15 @@ module Streamly.Data.Stream
280325 , scan
281326 , postscan
282327 -- XXX postscan1 can be implemented using Monoids or Refolds.
328+ -- The following scans from Data.List are not provided.
329+ -- XXX scanl
330+ -- XXX scanl1
331+ -- XXX scanr
332+ -- XXX scanr1
333+ -- XXX mapAccumL
334+ -- XXX mapAccumR
335+ -- XXX inits
336+ -- XXX tails
283337
284338 -- ** Specific scans
285339 -- Indexing can be considered as a special type of zipping where we zip a
@@ -288,6 +342,8 @@ module Streamly.Data.Stream
288342
289343 -- * Insertion
290344 -- | Add elements to the stream.
345+ --
346+ -- >>> insert = Stream.insertBy compare
291347
292348 -- Inserting elements is a special case of interleaving/merging streams.
293349 , insertBy
@@ -323,12 +379,15 @@ module Streamly.Data.Stream
323379 -- "Streamly.Internal.Data.Fold" can be used along with 'scanMaybe' to
324380 -- perform stateful filtering operations in general.
325381 --
326− -- Useful idioms:
382+ -- Idioms and equivalents of Data.List APIs:
327383 --
328384 -- >>> deleteBy cmp x = Stream.scanMaybe (Fold.deleteBy cmp x)
385+ -- >>> deleteBy = Stream.deleteBy -- unreleased API
386+ -- >>> delete = deleteBy (==)
329387 -- >>> findIndices p = Stream.scanMaybe (Fold.findIndices p)
330388 -- >>> elemIndices a = findIndices (== a)
331389 -- >>> uniq = Stream.scanMaybe (Fold.uniqBy (==))
390+ -- >>> partition p = Stream.fold (Fold.partition Fold.toList Fold.toList) . fmap (if p then Left else Right)
332391 , scanMaybe
333392 , take
334393 , takeWhile
@@ -336,6 +395,8 @@ module Streamly.Data.Stream
336395 , drop
337396 , dropWhile
338397 , dropWhileM
398+ -- XXX write to an array in reverse and then read in reverse
399+ -- > dropWhileEnd = reverse . dropWhile p . reverse
339400
340401 -- XXX These are available as scans in folds. We need to check the
341402 -- performance though. If these are common and we need convenient stream
@@ -372,6 +433,9 @@ module Streamly.Data.Stream
372433 -- >>> s = s1 `Stream.append` s2
373434
374435 -- ** Appending
436+ -- | Equivalent of Data.List append:
437+ --
438+ -- >>> (++) = Stream.append
375439 , append
376440
377441 -- ** Interleaving
@@ -382,8 +446,14 @@ module Streamly.Data.Stream
382446 , mergeByM
383447
384448 -- ** Zipping
449+ -- | Idioms and equivalents of Data.List APIs:
450+ --
451+ -- >>> zip = Stream.zipWith (,)
452+ -- >>> unzip = Stream.fold (Fold.unzip Fold.toList Fold.toList)
385453 , zipWith
386454 , zipWithM
455+ -- XXX zipWith3,4,5,6,7
456+ -- XXX unzip3,4,5,6,7
387457 -- , ZipStream (..)
388458
389459 -- ** Cross Product
@@ -416,48 +486,89 @@ module Streamly.Data.Stream
416486 -- One dimension loops are just a special case of nested loops. For
417487 -- example, 'concatMap' can degenerate to a simple map operation:
418488 --
419− -- > map f m = S.concatMap (\x -> S.fromPure (f x)) m
489+ -- >>> map f m = Stream.concatMap (\x -> Stream.fromPure (f x)) m
420490 --
421491 -- Similarly, 'concatMap' can perform filtering by mapping an element to a
422492 -- 'nil' stream:
423493 --
424− -- > filter p m = S.concatMap (\x -> if p x then S.fromPure x else S.nil) m
494+ -- >>> filter p m = Stream.concatMap (\x -> if p x then Stream.fromPure x else Stream.nil) m
495+ --
496+ -- Idioms and equivalents of Data.List APIs:
425497 --
498+ -- >>> concat = Stream.concatMap id
499+ -- >>> unlines = Stream.interposeSuffix '\n'
500+ -- >>> unlines = Stream.intercalateSuffix Unfold.fromList "\n"
501+ -- >>> unwords = Stream.interpose ' '
502+ -- >>> unwords = Stream.intercalate Unfold.fromList " "
426503
427504 , concatEffect
428505 , concatMap
429506 , concatMapM
430507
431508 -- * Repeated Fold
432− -- | Useful idioms:
509+ -- | Idioms and equivalents of Data.List APIs:
433510 --
434− -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
435− -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
436− -- >>> groupsBy eq f = Stream.parseMany (Parser.groupBy eq f)
437− -- >>> groupsByRolling eq f = Stream.parseMany (Parser.groupByRolling eq f)
438− -- >>> groupsOf n f = Stream.foldMany (Fold.take n f)
511+ -- >>> groupsOf n = Stream.foldMany (Fold.take n Fold.toList)
512+ -- >>> groupBy eq = Stream.groupsWhile eq Fold.toList
513+ -- >>> groupBy eq = Stream.parseMany (Parser.groupBy eq Fold.toList)
514+ -- >>> groupsByRolling eq = Stream.parseMany (Parser.groupByRolling eq Fold.toList)
515+ -- >>> groups = groupBy (==)
439516 , foldMany -- XXX Rename to foldRepeat
440517 , groupsOf
441518 , parseMany
442519
443520 -- * Splitting
521+ -- | Idioms and equivalents of Data.List APIs:
522+ --
523+ -- >>> splitWithSuffix p f = Stream.foldMany (Fold.takeEndBy p f)
524+ -- >>> splitOnSuffix p f = Stream.foldMany (Fold.takeEndBy_ p f)
525+ -- >>> lines = splitOnSuffix (== '\n')
526+ -- >>> words = Stream.wordsBy isSpace
527+ -- >>> splitAt n = Stream.fold (Fold.splitAt n Fold.toList Fold.toList)
528+ -- >>> span p = Parser.splitWith (,) (Parser.takeWhile p Fold.toList) (Parser.fromFold Fold.toList)
529+ -- >>> break p = span (not . p)
444530 , splitOn
445531 , wordsBy
446532
447533 -- * Buffered Operations
448534 -- | Operations that require buffering of the stream.
449535 -- Reverse is essentially a left fold followed by an unfold.
536+ --
537+ -- Idioms and equivalents of Data.List APIs:
538+ --
539+ -- >>> nub = Stream.fold Fold.toList . Stream.scanMaybe Fold.nub
540+ -- >>> nub = Stream.ordNub -- unreleased API
541+ -- >>> sortBy = StreamK.sortBy
542+ -- >>> sortOn f = StreamK.sortOn -- unreleased API
543+ -- >>> deleteFirstsBy = Stream.deleteFirstsBy -- unreleased
544+ -- >>> (\\) = Stream.deleteFirstsBy (==) -- unreleased
545+ -- >>> intersectBy = Stream.intersectBy -- unreleased
546+ -- >>> intersect = Stream.intersectBy (==) -- unreleased
547+ -- >>> unionBy = Stream.unionBy -- unreleased
548+ -- >>> union = Stream.unionBy (==) -- unreleased
549+ --
450550 , reverse
551+ -- XXX transpose: write the streams to arrays and then stream transposed.
552+ -- XXX subsequences
553+ -- XXX permutations
554+ -- , nub
555+ -- , ordNub
556+ -- , nubBy
451557
452558 -- * Multi-Stream folds
453559 -- | Operations that consume multiple streams at the same time.
454560 , eqBy
455561 , cmpBy
456562 , isPrefixOf
563+ , isInfixOf
564+ -- , isSuffixOf
565+ -- , isSuffixOfUnbox
457566 , isSubsequenceOf
458567
459568 -- trimming sequences
460− , stripPrefix
569+ -- , stripPrefix
570+ -- , stripSuffix
571+ -- , stripSuffixUnbox
461572
462573 -- Exceptions and resource management depend on the "exceptions" package
463574 -- XXX We can have IO Stream operations not depending on "exceptions"
@@ -521,10 +632,8 @@ where
521632import Streamly.Internal.Data.Stream
522633import Prelude
523634 hiding (filter, drop, dropWhile, take, takeWhile, zipWith, foldr,
524− foldl, map, mapM, mapM_, sequence, all, any, sum, product, elem,
525− notElem, maximum, minimum, head, last, tail, length, null,
526− reverse, iterate, init, and, or, lookup, foldr1, (!!),
527− scanl, scanl1, repeat, replicate, concatMap, span)
635+ mapM, sequence, reverse, iterate, foldr1, repeat, replicate,
636+ concatMap)
528637
529638import qualified Streamly.Internal.Data.Array.Type as Array
530639
core/src/Streamly/Internal/Data/Array.hs+30−5
@@ -74,6 +74,8 @@ module Streamly.Internal.Data.Array
7474 , interposeSuffix
7575 , intercalateSuffix
7676
77+ , compactLE
78+ , pinnedCompactLE
7779 , compactOnByte
7880 , compactOnByteSuffix
7981
@@ -203,6 +205,8 @@ last = getIndexRev 0
203205-- Folds with Array as the container
204206-------------------------------------------------------------------------------
205207
208+-- XXX We should generate this from Ring.
209+
206210-- | @writeLastN n@ folds a maximum of @n@ elements from the end of the input
207211-- stream to an 'Array'.
208212--
@@ -324,6 +328,7 @@ sliceIndexerFromLen from len =
324328 Unfold.lmap unsafeThaw (MA.sliceIndexerFromLen from len)
325329
326330{-# DEPRECATED genSlicesFromLen "Please use sliceIndexerFromLen instead." #-}
331+{-# INLINE genSlicesFromLen #-}
327332genSlicesFromLen :: forall m a. (Monad m, Unbox a)
328333 => Int -- ^ from index
329334 -> Int -- ^ length of the slice
@@ -345,6 +350,7 @@ slicerFromLen from len =
345350 $ Unfold.lmap unsafeThaw (MA.slicerFromLen from len)
346351
347352{-# DEPRECATED getSlicesFromLen "Please use slicerFromLen instead." #-}
353+{-# INLINE getSlicesFromLen #-}
348354getSlicesFromLen :: forall m a. (Monad m, Unbox a)
349355 => Int -- ^ from index
350356 -> Int -- ^ length of the slice
@@ -395,6 +401,7 @@ indexReader m =
395401
396402-- XXX DO NOT REMOVE, change the signature to use Stream instead of unfold
397403{-# DEPRECATED getIndices "Please use getIndices instead." #-}
404+{-# INLINE getIndices #-}
398405getIndices :: (Monad m, Unbox a) => Stream m Int -> Unfold m (Array a) a
399406getIndices = indexReader
400407
@@ -445,7 +452,7 @@ runPipe f arr = P.runPipe (toArrayMinChunk (length arr)) $ f (read arr)
445452streamTransform :: forall m a b. (MonadIO m, Unbox a, Unbox b)
446453 => (Stream m a -> Stream m b) -> Array a -> m (Array b)
447454streamTransform f arr =
448− Stream.fold (writeWith (length arr)) $ f (read arr)
455+ Stream.fold (createWith (length arr)) $ f (read arr)
449456
450457-------------------------------------------------------------------------------
451458-- Casts
@@ -498,8 +505,8 @@ cast arr =
498505asCStringUnsafe :: Array a -> (CString -> IO b) -> IO b
499506asCStringUnsafe arr act = do
500507 let arr1 = asBytes arr <> fromList [0]
501− -- asPtrUnsafe makes sure the array is pinned
502− asPtrUnsafe arr1 $ \ptr -> act (castPtr ptr)
508+ -- unsafePinnedAsPtr makes sure the array is pinned
509+ unsafePinnedAsPtr arr1 $ \ptr -> act (castPtr ptr)
503510
504511-------------------------------------------------------------------------------
505512-- Folds
@@ -591,7 +598,7 @@ data FlattenState s =
591598-- >>> interposeSuffix x = Stream.interposeSuffix x Array.reader
592599--
593600{-# INLINE_NORMAL interposeSuffix #-}
594−interposeSuffix :: forall m a. (MonadIO m, Unbox a)
601+interposeSuffix :: forall m a. (Monad m, Unbox a)
595602 => a -> Stream m (Array a) -> Stream m a
596603-- This does not require MonadIO constraint.
597604-- interposeSuffix x = D.interposeSuffix x reader
@@ -612,7 +619,7 @@ interposeSuffix sep (D.Stream step state) = D.Stream step' (OuterLoop state)
612619 return $ D.Yield sep $ OuterLoop st
613620
614621 step' _ (InnerLoop st contents p end) = do
615− x <- liftIO $ peekAt p contents
622+ x <- pure $ unsafeInlineIO $ peekAt p contents
616623 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
617624
618625-- | Insert the given array after each array and flatten.
@@ -624,6 +631,24 @@ intercalateSuffix :: (Monad m, Unbox a)
624631 => Array a -> Stream m (Array a) -> Stream m a
625632intercalateSuffix = D.intercalateSuffix reader
626633
634+-- | @compactLE n@ coalesces adjacent arrays in the input stream
635+-- only if the combined size would be less than or equal to n.
636+--
637+-- Generates unpinned arrays irrespective of the pinning status of input
638+-- arrays.
639+{-# INLINE_NORMAL compactLE #-}
640+compactLE :: (MonadIO m, Unbox a)
641+ => Int -> Stream m (Array a) -> Stream m (Array a)
642+compactLE n stream =
643+ D.map unsafeFreeze $ MA.compactLE n $ D.map unsafeThaw stream
644+
645+-- | Pinned version of 'compactLE'.
646+{-# INLINE_NORMAL pinnedCompactLE #-}
647+pinnedCompactLE :: (MonadIO m, Unbox a)
648+ => Int -> Stream m (Array a) -> Stream m (Array a)
649+pinnedCompactLE n stream =
650+ D.map unsafeFreeze $ MA.pinnedCompactLE n $ D.map unsafeThaw stream
651+
627652-- | Split a stream of arrays on a given separator byte, dropping the separator
628653-- and coalescing all the arrays between two separators into a single array.
629654--
core/src/Streamly/Internal/Data/Array/Type.hs+115−44
@@ -1,4 +1,8 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
26-- |
37-- Module : Streamly.Internal.Data.Array.Type
48-- Copyright : (c) 2020 Composewell Technologies
@@ -28,7 +32,7 @@ module Streamly.Internal.Data.Array.Type
2832 , isPinned
2933
3034 -- *** Casting
31− , asPtrUnsafe
35+ , unsafePinnedAsPtr
3236
3337 -- ** Construction
3438 , empty
@@ -45,15 +49,13 @@ module Streamly.Internal.Data.Array.Type
4549
4650 -- *** Stream Folds
4751 , unsafeMakePure
48− , writeWith
49− , writeN
50− , pinnedWriteN
51− , writeNUnsafe
52− , pinnedWriteNUnsafe
53− -- , MA.ArrayUnsafe (..)
54− , pinnedWriteNAligned
55− , write
56− , pinnedWrite
52+ , createOf
53+ , pinnedCreateOf
54+ , unsafeCreateOf
55+ , unsafePinnedCreateOf
56+ , create
57+ , pinnedCreate
58+ , createWith
5759
5860 -- *** From containers
5961 , fromListN
@@ -75,8 +77,8 @@ module Streamly.Internal.Data.Array.Type
7577 -- ** Reading
7678
7779 -- *** Indexing
78− , unsafeIndexIO -- XXX rename to getIndexUnsafeIO
79− , getIndexUnsafe
80+ , unsafeIndexIO -- XXX unsafeGetIndexIO
81+ , getIndexUnsafe -- XXX unsafeGetIndex
8082
8183 -- *** To Streams
8284 , read
@@ -123,12 +125,14 @@ module Streamly.Internal.Data.Array.Type
123125
124126 -- *** Compact
125127 -- | Append the arrays in a stream to form a stream of larger arrays.
126− , compactLE
127128 , fCompactGE
129+ , fPinnedCompactGE
128130 , lCompactGE
131+ , lPinnedCompactGE
129132 , compactGE
130133
131134 -- ** Deprecated
135+ , asPtrUnsafe
132136 , unsafeIndex
133137 , bufferChunks
134138 , flattenArrays
@@ -141,6 +145,14 @@ module Streamly.Internal.Data.Array.Type
141145 , toStream
142146 , toStreamRev
143147 , nil
148+ , writeWith
149+ , writeN
150+ , pinnedWriteN
151+ , writeNUnsafe
152+ , pinnedWriteNUnsafe
153+ , pinnedWriteNAligned
154+ , write
155+ , pinnedWrite
144156 )
145157where
146158
@@ -231,15 +243,20 @@ data Array a =
231243
232244-- | Use an @Array a@ as @Ptr a@.
233245--
234−-- See 'MA.asPtrUnsafe' in the Mutable array module for more details.
246+-- See 'MA.unsafePinnedAsPtr' in the Mutable array module for more details.
235247--
236248-- /Unsafe/
237249--
238250-- /Pre-release/
239251--
252+{-# INLINE unsafePinnedAsPtr #-}
253+unsafePinnedAsPtr :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
254+unsafePinnedAsPtr arr = MA.unsafePinnedAsPtr (unsafeThaw arr)
255+
256+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
240257{-# INLINE asPtrUnsafe #-}
241258asPtrUnsafe :: MonadIO m => Array a -> (Ptr a -> m b) -> m b
242−asPtrUnsafe arr = MA.asPtrUnsafe (unsafeThaw arr)
259+asPtrUnsafe = unsafePinnedAsPtr
243260
244261-------------------------------------------------------------------------------
245262-- Freezing and Thawing
@@ -460,12 +477,13 @@ pinnedChunksOf n str = D.map unsafeFreeze $ MA.pinnedChunksOf n str
460477-- >>> concat = Stream.unfoldMany Array.reader
461478--
462479{-# INLINE_NORMAL concat #-}
463−concat :: (MonadIO m, Unbox a) => Stream m (Array a) -> Stream m a
480+concat :: (Monad m, Unbox a) => Stream m (Array a) -> Stream m a
464481-- XXX this requires MonadIO whereas the unfoldMany version does not
465−concat = MA.concat . D.map unsafeThaw
482+concat = MA.concatWith (pure . unsafeInlineIO) . D.map unsafeThaw
466483-- concat = D.unfoldMany reader
467484
468485{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
486+{-# INLINE flattenArrays #-}
469487flattenArrays :: forall m a. (MonadIO m, Unbox a)
470488 => D.Stream m (Array a) -> D.Stream m a
471489flattenArrays = concat
@@ -476,13 +494,14 @@ flattenArrays = concat
476494-- >>> concatRev = Stream.unfoldMany Array.readerRev
477495--
478496{-# INLINE_NORMAL concatRev #-}
479−concatRev :: forall m a. (MonadIO m, Unbox a)
497+concatRev :: forall m a. (Monad m, Unbox a)
480498 => D.Stream m (Array a) -> D.Stream m a
481499-- XXX this requires MonadIO whereas the unfoldMany version does not
482−concatRev = MA.concatRev . D.map unsafeThaw
500+concatRev = MA.concatRevWith (pure . unsafeInlineIO) . D.map unsafeThaw
483501-- concatRev = D.unfoldMany readerRev
484502
485503{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
504+{-# INLINE flattenArraysRev #-}
486505flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
487506 => D.Stream m (Array a) -> D.Stream m a
488507flattenArraysRev = concatRev
@@ -496,26 +515,28 @@ flattenArraysRev = concatRev
496515-- arrays would have no capacity to append, therefore, a copy will be forced
497516-- anyway.
498517
499−-- | Scan @compactLE n@ coalesces adjacent arrays in the input stream
500−-- only if the combined size would be less than or equal to n.
501−{-# INLINE_NORMAL compactLE #-}
502−compactLE :: (MonadIO m, Unbox a)
503− => Int -> Stream m (Array a) -> Stream m (Array a)
504−compactLE n stream =
505− D.map unsafeFreeze $ MA.rCompactLE n $ D.map unsafeThaw stream
506−
507518-- | Fold @fCompactGE n@ coalesces adjacent arrays in the input stream
508519-- until the size becomes greater than or equal to n.
509520--
521+-- Generates unpinned arrays irrespective of the pinning status of input
522+-- arrays.
510523{-# INLINE_NORMAL fCompactGE #-}
511524fCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
512525fCompactGE n = fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fCompactGE n
513526
527+-- | PInned version of 'fCompactGE'.
528+{-# INLINE_NORMAL fPinnedCompactGE #-}
529+fPinnedCompactGE :: (MonadIO m, Unbox a) => Int -> Fold m (Array a) (Array a)
530+fPinnedCompactGE n =
531+ fmap unsafeFreeze $ Fold.lmap unsafeThaw $ MA.fPinnedCompactGE n
532+
514533-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
515534-- the size becomes greater than or equal to @n@.
516535--
517536-- >>> compactGE n = Stream.foldMany (Array.fCompactGE n)
518537--
538+-- Generates unpinned arrays irrespective of the pinning status of input
539+-- arrays.
519540{-# INLINE compactGE #-}
520541compactGE ::
521542 (MonadIO m, Unbox a)
@@ -527,12 +548,21 @@ compactGE n stream =
527548--
528549-- >>> lCompactGE n = Fold.many (Array.fCompactGE n)
529550--
551+-- Generates unpinned arrays irrespective of the pinning status of input
552+-- arrays.
530553{-# INLINE_NORMAL lCompactGE #-}
531554lCompactGE :: (MonadIO m, Unbox a)
532555 => Int -> Fold m (Array a) () -> Fold m (Array a) ()
533556lCompactGE n fld =
534557 Fold.lmap unsafeThaw $ MA.lCompactGE n (Fold.lmap unsafeFreeze fld)
535558
559+-- | Pinned version of 'lCompactGE'.
560+{-# INLINE_NORMAL lPinnedCompactGE #-}
561+lPinnedCompactGE :: (MonadIO m, Unbox a)
562+ => Int -> Fold m (Array a) () -> Fold m (Array a) ()
563+lPinnedCompactGE n fld =
564+ Fold.lmap unsafeThaw $ MA.lPinnedCompactGE n (Fold.lmap unsafeFreeze fld)
565+
536566-------------------------------------------------------------------------------
537567-- Splitting
538568-------------------------------------------------------------------------------
@@ -727,17 +757,28 @@ toList s = build (\c n -> toListFB c n s)
727757-- Folds
728758-------------------------------------------------------------------------------
729759
730−-- | @writeN n@ folds a maximum of @n@ elements from the input stream to an
760+-- | @createOf n@ folds a maximum of @n@ elements from the input stream to an
731761-- 'Array'.
732762--
733−{-# INLINE_NORMAL writeN #-}
763+{-# INLINE_NORMAL createOf #-}
764+createOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
765+createOf = fmap unsafeFreeze . MA.createOf
766+
767+-- XXX Deprecate in major
768+-- {-# DEPRECATED writeN "Please use createOf instead." #-}
769+{-# INLINE writeN #-}
734770writeN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
735−writeN = fmap unsafeFreeze . MA.writeN
771+writeN = createOf
736772
737−-- | Like 'fromListN' but creates a pinned array.
738−{-# INLINE_NORMAL pinnedWriteN #-}
773+-- | Like 'createOf' but creates a pinned array.
774+{-# INLINE_NORMAL pinnedCreateOf #-}
775+pinnedCreateOf :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
776+pinnedCreateOf = fmap unsafeFreeze . MA.pinnedCreateOf
777+
778+{-# DEPRECATED pinnedWriteN "Please use pinnedCreateOf instead." #-}
779+{-# INLINE pinnedWriteN #-}
739780pinnedWriteN :: forall m a. (MonadIO m, Unbox a) => Int -> Fold m a (Array a)
740−pinnedWriteN = fmap unsafeFreeze . MA.pinnedCreateOf
781+pinnedWriteN = pinnedCreateOf
741782
742783-- | @pinnedWriteNAligned alignment n@ folds a maximum of @n@ elements from the input
743784-- stream to an 'Array' aligned to the given size.
@@ -745,44 +786,74 @@ pinnedWriteN = fmap unsafeFreeze . MA.pinnedCreateOf
745786-- /Pre-release/
746787--
747788{-# INLINE_NORMAL pinnedWriteNAligned #-}
789+{-# DEPRECATED pinnedWriteNAligned "To be removed." #-}
748790pinnedWriteNAligned :: forall m a. (MonadIO m, Unbox a)
749791 => Int -> Int -> Fold m a (Array a)
750792pinnedWriteNAligned alignSize = fmap unsafeFreeze . MA.pinnedWriteNAligned alignSize
751793
752−-- | Like 'writeN' but does not check the array bounds when writing. The fold
794+-- | Like 'createOf' but does not check the array bounds when writing. The fold
753795-- driver must not call the step function more than 'n' times otherwise it will
754796-- corrupt the memory and crash. This function exists mainly because any
755797-- conditional in the step function blocks fusion causing 10x performance
756798-- slowdown.
757799--
758−{-# INLINE_NORMAL writeNUnsafe #-}
800+{-# INLINE_NORMAL unsafeCreateOf #-}
801+unsafeCreateOf :: forall m a. (MonadIO m, Unbox a)
802+ => Int -> Fold m a (Array a)
803+unsafeCreateOf n = unsafeFreeze <$> MA.unsafeCreateOf n
804+
805+{-# DEPRECATED writeNUnsafe "Please use unsafeCreateOf instead." #-}
806+{-# INLINE writeNUnsafe #-}
759807writeNUnsafe :: forall m a. (MonadIO m, Unbox a)
760808 => Int -> Fold m a (Array a)
761−writeNUnsafe n = unsafeFreeze <$> MA.unsafeCreateOf n
809+writeNUnsafe = unsafeCreateOf
810+
811+{-# INLINE_NORMAL unsafePinnedCreateOf #-}
812+unsafePinnedCreateOf :: forall m a. (MonadIO m, Unbox a)
813+ => Int -> Fold m a (Array a)
814+unsafePinnedCreateOf n = unsafeFreeze <$> MA.unsafePinnedCreateOf n
762815
763−{-# INLINE_NORMAL pinnedWriteNUnsafe #-}
816+{-# DEPRECATED pinnedWriteNUnsafe "Please use unsafePinnedCreateOf instead." #-}
817+{-# INLINE pinnedWriteNUnsafe #-}
764818pinnedWriteNUnsafe :: forall m a. (MonadIO m, Unbox a)
765819 => Int -> Fold m a (Array a)
766−pinnedWriteNUnsafe n = unsafeFreeze <$> MA.unsafePinnedCreateOf n
820+pinnedWriteNUnsafe = unsafePinnedCreateOf
767821
768−{-# INLINE_NORMAL writeWith #-}
822+{-# INLINE_NORMAL createWith #-}
823+createWith :: forall m a. (MonadIO m, Unbox a)
824+ => Int -> Fold m a (Array a)
825+-- createWith n = FL.rmapM spliceArrays $ toArraysOf n
826+createWith elemCount = unsafeFreeze <$> MA.createWith elemCount
827+
828+{-# DEPRECATED writeWith "Please use createWith instead." #-}
829+{-# INLINE writeWith #-}
769830writeWith :: forall m a. (MonadIO m, Unbox a)
770831 => Int -> Fold m a (Array a)
771−-- writeWith n = FL.rmapM spliceArrays $ toArraysOf n
772−writeWith elemCount = unsafeFreeze <$> MA.createWith elemCount
832+writeWith = createWith
773833
774834-- | Fold the whole input to a single array.
775835--
776836-- /Caution! Do not use this on infinite streams./
777837--
838+{-# INLINE create #-}
839+create :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
840+create = fmap unsafeFreeze MA.create
841+
842+-- XXX Deprecate in major
843+-- {-# DEPRECATED write "Please use create instead." #-}
778844{-# INLINE write #-}
779845write :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
780−write = fmap unsafeFreeze MA.write
846+write = create
847+
848+-- | Like 'create' but creates a pinned array.
849+{-# INLINE pinnedCreate #-}
850+pinnedCreate :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
851+pinnedCreate = fmap unsafeFreeze MA.pinnedCreate
781852
782−-- | Like 'write' but creates a pinned array.
853+{-# DEPRECATED pinnedWrite "Please use pinnedCreate instead." #-}
783854{-# INLINE pinnedWrite #-}
784855pinnedWrite :: forall m a. (MonadIO m, Unbox a) => Fold m a (Array a)
785−pinnedWrite = fmap unsafeFreeze MA.pinnedCreate
856+pinnedWrite = pinnedCreate
786857
787858-- | Fold "step" has a dependency on "initial", and each step is dependent on
788859-- the previous invocation of step due to state passing, finally extract
core/src/Streamly/Internal/Data/IsMap.hs+4
@@ -1,3 +1,7 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15-- |
26-- Module : Streamly.Internal.Data.IsMap
37-- Copyright : (c) 2022 Composewell Technologies
core/src/Streamly/Internal/Data/MutArray.hs+20−3
@@ -14,6 +14,7 @@ module Streamly.Internal.Data.MutArray
1414 , sliceIndexerFromLen
1515 , slicerFromLen
1616 , compactLE
17+ , pinnedCompactLE
1718 , compactOnByte
1819 , compactOnByteSuffix
1920 -- * Unboxed IORef
@@ -29,6 +30,7 @@ where
2930
3031import Control.Monad.IO.Class (MonadIO(..))
3132import Data.Word (Word8)
33+import Streamly.Internal.Data.MutByteArray.Type (PinnedState(..))
3234import Streamly.Internal.Data.Stream.Type (Stream)
3335import Streamly.Internal.Data.Unbox (Unbox)
3436import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -91,15 +93,30 @@ getSlicesFromLen = slicerFromLen
9193-- Compacting Streams of Arrays
9294-------------------------------------------------------------------------------
9395
94−-- | Scan @compactLE n@ coalesces adjacent arrays in the input stream
95−-- only if the combined size would be less than or equal to n.
96+-- | @compactLE maxElems@ coalesces adjacent arrays in the input stream
97+-- only if the combined size would be less than or equal to @maxElems@
98+-- elements. Note that it won't split an array if the original array is already
99+-- larger than maxElems.
100+--
101+-- @maxElems@ must be greater than 0.
102+--
103+-- Generates unpinned arrays irrespective of the pinning status of input
104+-- arrays.
96105{-# INLINE compactLE #-}
97106compactLE :: (MonadIO m, Unbox a) =>
98107 Int -> Stream m (MutArray a) -> Stream m (MutArray a)
99108-- XXX compactLE can be moved to MutArray/Type if we are not using the parser
100109-- to implement it.
110+compactLE = compactLeAs Unpinned
111+-- The parser version turns out to be a little bit slower.
101112-- compactLE n = Stream.catRights . Stream.parseManyD (pCompactLE n)
102−compactLE = rCompactLE
113+
114+-- | Pinned version of 'compactLE'.
115+{-# INLINE pinnedCompactLE #-}
116+pinnedCompactLE :: forall m a. (MonadIO m, Unbox a)
117+ => Int -> Stream m (MutArray a) -> Stream m (MutArray a)
118+pinnedCompactLE = compactLeAs Pinned
119+-- pinnedCompactLE n = Stream.catRights . Stream.parseManyD (pPinnedCompactLE n)
103120
104121data SplitState s arr
105122 = Initial s
core/src/Streamly/Internal/Data/MutArray/Type.hs+152−84
@@ -42,8 +42,8 @@ module Streamly.Internal.Data.MutArray.Type
4242 , cast
4343 , castUnsafe -- XXX unsafeCast
4444 , asBytes
45− , asPtrUnsafe -- XXX rename to unsafePinnedAsPtr?
46− , asUnpinnedPtrUnsafe -- XXX rename to unsafeAsPtr
45+ , unsafePinnedAsPtr
46+ , unsafeAsPtr
4747
4848 -- ** Construction
4949 , empty
@@ -242,6 +242,8 @@ module Streamly.Internal.Data.MutArray.Type
242242
243243 -- *** Concat
244244 -- | Append the arrays in a stream to form a stream of elements.
245+ , concatWith
246+ , concatRevWith
245247 , concat
246248 , concatRev
247249
@@ -249,9 +251,12 @@ module Streamly.Internal.Data.MutArray.Type
249251 -- | Append the arrays in a stream to form a stream of larger arrays.
250252 , SpliceState (..)
251253 , pCompactLE
252− , rCompactLE
254+ , pPinnedCompactLE
255+ , compactLeAs
253256 , fCompactGE
257+ , fPinnedCompactGE
254258 , lCompactGE
259+ , lPinnedCompactGE
255260 , compactGE
256261 , compactEQ
257262
@@ -262,6 +267,7 @@ module Streamly.Internal.Data.MutArray.Type
262267 , c_memchr
263268
264269 -- * Deprecated
270+ , asPtrUnsafe
265271 , writeChunks
266272 , flattenArrays
267273 , flattenArraysRev
@@ -836,15 +842,15 @@ roundDownTo elemSize size = size - (size `mod` elemSize)
836842-- Since this is not inlined Unboxed consrraint leads to dictionary passing
837843-- which complicates some inspection tests.
838844--
839−{-# NOINLINE reallocExplicit #-}
840−reallocExplicit :: Int -> Int -> MutArray a -> IO (MutArray a)
841−reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
845+{-# NOINLINE reallocExplicitAs #-}
846+reallocExplicitAs :: PinnedState -> Int -> Int -> MutArray a -> IO (MutArray a)
847+reallocExplicitAs ps elemSize newCapacityInBytes MutArray{..} = do
842848 assertM(arrEnd <= arrBound)
843849
844850 -- Allocate new array
845851 let newCapMaxInBytes = roundUpLargeArray newCapacityInBytes
846852 contents <-
847− if Unboxed.isPinned arrContents
853+ if ps == Pinned
848854 then Unboxed.pinnedNew newCapMaxInBytes
849855 else Unboxed.new newCapMaxInBytes
850856 let !(MutByteArray mbarrFrom#) = arrContents
@@ -884,7 +890,12 @@ reallocExplicit elemSize newCapacityInBytes MutArray{..} = do
884890-- If the original array is pinned, the newly allocated array is also pinned.
885891{-# INLINABLE realloc #-}
886892realloc :: forall m a. (MonadIO m, Unbox a) => Int -> MutArray a -> m (MutArray a)
887−realloc bytes arr = liftIO $ reallocExplicit (SIZE_OF(a)) bytes arr
893+realloc bytes arr =
894+ let ps =
895+ if isPinned arr
896+ then Pinned
897+ else Unpinned
898+ in liftIO $ reallocExplicitAs ps (SIZE_OF(a)) bytes arr
888899
889900-- | @reallocWith label capSizer minIncrBytes array@. The label is used
890901-- in error messages and the capSizer is used to determine the capacity of the
@@ -1653,16 +1664,10 @@ data FlattenState s contents a =
16531664 OuterLoop s
16541665 | InnerLoop s contents !Int !Int
16551666
1656−-- | Use the "reader" unfold instead.
1657−--
1658−-- @concat = unfoldMany reader@
1659−--
1660−-- We can try this if there are any fusion issues in the unfold.
1661−--
1662−{-# INLINE_NORMAL concat #-}
1663−concat :: forall m a. (MonadIO m, Unbox a)
1664− => D.Stream m (MutArray a) -> D.Stream m a
1665−concat (D.Stream step state) = D.Stream step' (OuterLoop state)
1667+{-# INLINE_NORMAL concatWith #-}
1668+concatWith :: forall m a. (Monad m, Unbox a)
1669+ => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1670+concatWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
16661671
16671672 where
16681673
@@ -1679,25 +1684,30 @@ concat (D.Stream step state) = D.Stream step' (OuterLoop state)
16791684 return $ D.Skip $ OuterLoop st
16801685
16811686 step' _ (InnerLoop st contents p end) = do
1682− x <- liftIO $ peekAt p contents
1687+ x <- liftio $ peekAt p contents
16831688 return $ D.Yield x (InnerLoop st contents (INDEX_NEXT(p,a)) end)
16841689
1690+-- | Use the "reader" unfold instead.
1691+--
1692+-- @concat = unfoldMany reader@
1693+--
1694+-- We can try this if there are any fusion issues in the unfold.
1695+--
1696+{-# INLINE_NORMAL concat #-}
1697+concat :: forall m a. (MonadIO m, Unbox a)
1698+ => D.Stream m (MutArray a) -> D.Stream m a
1699+concat = concatWith liftIO
1700+
16851701{-# DEPRECATED flattenArrays "Please use \"unfoldMany reader\" instead." #-}
16861702{-# INLINE flattenArrays #-}
16871703flattenArrays :: forall m a. (MonadIO m, Unbox a)
16881704 => D.Stream m (MutArray a) -> D.Stream m a
16891705flattenArrays = concat
16901706
1691−-- | Use the "readerRev" unfold instead.
1692−--
1693−-- @concat = unfoldMany readerRev@
1694−--
1695−-- We can try this if there are any fusion issues in the unfold.
1696−--
1697−{-# INLINE_NORMAL concatRev #-}
1698−concatRev :: forall m a. (MonadIO m, Unbox a)
1699− => D.Stream m (MutArray a) -> D.Stream m a
1700−concatRev (D.Stream step state) = D.Stream step' (OuterLoop state)
1707+{-# INLINE_NORMAL concatRevWith #-}
1708+concatRevWith :: forall m a. (Monad m, Unbox a)
1709+ => (forall b. IO b -> m b) -> D.Stream m (MutArray a) -> D.Stream m a
1710+concatRevWith liftio (D.Stream step state) = D.Stream step' (OuterLoop state)
17011711
17021712 where
17031713
@@ -1715,10 +1725,21 @@ concatRev (D.Stream step state) = D.Stream step' (OuterLoop state)
17151725 return $ D.Skip $ OuterLoop st
17161726
17171727 step' _ (InnerLoop st contents p start) = do
1718− x <- liftIO $ peekAt p contents
1728+ x <- liftio $ peekAt p contents
17191729 let cur = INDEX_PREV(p,a)
17201730 return $ D.Yield x (InnerLoop st contents cur start)
17211731
1732+-- | Use the "readerRev" unfold instead.
1733+--
1734+-- @concat = unfoldMany readerRev@
1735+--
1736+-- We can try this if there are any fusion issues in the unfold.
1737+--
1738+{-# INLINE_NORMAL concatRev #-}
1739+concatRev :: forall m a. (MonadIO m, Unbox a)
1740+ => D.Stream m (MutArray a) -> D.Stream m a
1741+concatRev = concatRevWith liftIO
1742+
17221743{-# DEPRECATED flattenArraysRev "Please use \"unfoldMany readerRev\" instead." #-}
17231744{-# INLINE flattenArraysRev #-}
17241745flattenArraysRev :: forall m a. (MonadIO m, Unbox a)
@@ -2271,7 +2292,7 @@ writeWithAs ps elemCount =
22712292 | INDEX_NEXT(end,a) > bound = do
22722293 let oldSize = end - start
22732294 newSize = max (oldSize * 2) 1
2274− arr1 <- liftIO $ reallocExplicit (SIZE_OF(a)) newSize arr
2295+ arr1 <- liftIO $ reallocExplicitAs ps (SIZE_OF(a)) newSize arr
22752296 snocUnsafe arr1 x
22762297 step arr x = snocUnsafe arr x
22772298
@@ -2419,7 +2440,7 @@ fromPtrN len addr = do
24192440 -- XXX We can implement a stream copy in a similar way by streaming Word64
24202441 -- first and then remaining Word8.
24212442 arr <- new len
2422− _ <- asUnpinnedPtrUnsafe arr
2443+ _ <- unsafeAsPtr arr
24232444 (\ptr -> liftIO $ c_memcpy ptr addr (fromIntegral len))
24242445 return (arr {arrEnd = len})
24252446
@@ -2435,7 +2456,7 @@ fromByteStr# addr = do
24352456 len <- liftIO $ c_strlen (Ptr addr)
24362457 let lenInt = fromIntegral len
24372458 arr <- new lenInt
2438− _ <- asUnpinnedPtrUnsafe arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
2459+ _ <- unsafeAsPtr arr (\ptr -> liftIO $ c_memcpy ptr (Ptr addr) len)
24392460 return (arr {arrEnd = lenInt})
24402461
24412462-------------------------------------------------------------------------------
@@ -2723,7 +2744,7 @@ splitOn predicate arr =
27232744{-# INLINE breakOn #-}
27242745breakOn :: MonadIO m
27252746 => Word8 -> MutArray Word8 -> m (MutArray Word8, Maybe (MutArray Word8))
2726−breakOn sep arr@MutArray{..} = asUnpinnedPtrUnsafe arr $ \p -> liftIO $ do
2747+breakOn sep arr@MutArray{..} = unsafeAsPtr arr $ \p -> liftIO $ do
27272748 -- XXX We do not need memchr here, we can use a Haskell equivalent.
27282749 -- Need efficient stream based primitives that work on Word64.
27292750 loc <- c_memchr p sep (fromIntegral $ byteLength arr)
@@ -2854,16 +2875,21 @@ cast arr =
28542875--
28552876-- /Pre-release/
28562877--
2878+{-# INLINE unsafePinnedAsPtr #-}
2879+unsafePinnedAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2880+unsafePinnedAsPtr arr f =
2881+ Unboxed.unsafePinnedAsPtr
2882+ (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2883+
2884+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
28572885{-# INLINE asPtrUnsafe #-}
28582886asPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2859−asPtrUnsafe arr f =
2860− Unboxed.asPtrUnsafe
2861− (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
2887+asPtrUnsafe = unsafePinnedAsPtr
28622888
2863−{-# INLINE asUnpinnedPtrUnsafe #-}
2864−asUnpinnedPtrUnsafe :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2865−asUnpinnedPtrUnsafe arr f =
2866− Unboxed.asUnpinnedPtrUnsafe
2889+{-# INLINE unsafeAsPtr #-}
2890+unsafeAsPtr :: MonadIO m => MutArray a -> (Ptr a -> m b) -> m b
2891+unsafeAsPtr arr f =
2892+ Unboxed.unsafeAsPtr
28672893 (arrContents arr) (\ptr -> f (ptr `plusPtr` arrStart arr))
28682894
28692895-------------------------------------------------------------------------------
@@ -2925,19 +2951,11 @@ byteEq arr1 arr2 = fmap (EQ ==) $ byteCmp arr1 arr2
29252951-- can leave some memory unused. They can split the last array to fit it
29262952-- exactly in the space.
29272953
2928−-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2929−-- only if the combined size would be less than or equal to @maxElems@
2930−-- elements. Note that it won't split an array if the original array is already
2931−-- larger than maxElems.
2932−--
2933−-- @maxElems@ must be greater than 0.
2934−--
2935−-- /Internal/
2936−{-# INLINE_NORMAL pCompactLE #-}
2937−pCompactLE ::
2954+{-# INLINE_NORMAL pCompactLeAs #-}
2955+pCompactLeAs ::
29382956 forall m a. (MonadIO m, Unbox a)
2939− => Int -> Parser (MutArray a) m (MutArray a)
2940−pCompactLE maxElems = Parser step initial extract
2957+ => PinnedState -> Int -> Parser (MutArray a) m (MutArray a)
2958+pCompactLeAs ps maxElems = Parser step initial extract
29412959
29422960 where
29432961
@@ -2968,7 +2986,8 @@ pCompactLE maxElems = Parser step initial extract
29682986 else do
29692987 buf1 <-
29702988 if byteCapacity buf < maxBytes
2971− then realloc maxBytes buf
2989+ then liftIO $ reallocExplicitAs
2990+ ps (SIZE_OF(a)) maxBytes buf
29722991 else return buf
29732992 buf2 <- spliceUnsafe buf1 arr
29742993 return $ Parser.Partial 0 (Just buf2)
@@ -2976,6 +2995,30 @@ pCompactLE maxElems = Parser step initial extract
29762995 extract Nothing = return $ Parser.Done 0 nil
29772996 extract (Just buf) = return $ Parser.Done 0 buf
29782997
2998+-- | Parser @pCompactLE maxElems@ coalesces adjacent arrays in the input stream
2999+-- only if the combined size would be less than or equal to @maxElems@
3000+-- elements. Note that it won't split an array if the original array is already
3001+-- larger than maxElems.
3002+--
3003+-- @maxElems@ must be greater than 0.
3004+--
3005+-- Generates unpinned arrays irrespective of the pinning status of input
3006+-- arrays.
3007+--
3008+-- /Internal/
3009+{-# INLINE pCompactLE #-}
3010+pCompactLE ::
3011+ forall m a. (MonadIO m, Unbox a)
3012+ => Int -> Parser (MutArray a) m (MutArray a)
3013+pCompactLE = pCompactLeAs Unpinned
3014+
3015+-- | Pinned version of 'pCompactLE'.
3016+{-# INLINE pPinnedCompactLE #-}
3017+pPinnedCompactLE ::
3018+ forall m a. (MonadIO m, Unbox a)
3019+ => Int -> Parser (MutArray a) m (MutArray a)
3020+pPinnedCompactLE = pCompactLeAs Pinned
3021+
29793022data SpliceState s arr
29803023 = SpliceInitial s
29813024 | SpliceBuffering s arr
@@ -2987,18 +3030,10 @@ data SpliceState s arr
29873030-- immutable array never has additional space so a new array is allocated
29883031-- instead of mutating it.
29893032
2990−-- | Scan @rCompactLE maxElems@ coalesces adjacent arrays in the input stream
2991−-- only if the combined size would be less than or equal to @maxElems@
2992−-- elements. Note that it won't split an array if the original array is already
2993−-- larger than maxElems.
2994−--
2995−-- @maxElems@ must be greater than 0.
2996−--
2997−-- /Internal/
2998−{-# INLINE_NORMAL rCompactLE #-}
2999−rCompactLE :: forall m a. (MonadIO m, Unbox a)
3000− => Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
3001−rCompactLE maxElems (D.Stream step state) =
3033+{-# INLINE_NORMAL compactLeAs #-}
3034+compactLeAs :: forall m a. (MonadIO m, Unbox a)
3035+ => PinnedState -> Int -> D.Stream m (MutArray a) -> D.Stream m (MutArray a)
3036+compactLeAs ps maxElems (D.Stream step state) =
30023037 D.Stream step' (SpliceInitial state)
30033038
30043039 where
@@ -3034,7 +3069,8 @@ rCompactLE maxElems (D.Stream step state) =
30343069 D.Skip (SpliceYielding buf (SpliceBuffering s arr))
30353070 else do
30363071 buf1 <- if byteCapacity buf < maxBytes
3037− then realloc maxBytes buf
3072+ then liftIO $ reallocExplicitAs
3073+ ps (SIZE_OF(a)) maxBytes buf
30383074 else return buf
30393075 buf2 <- spliceUnsafe buf1 arr
30403076 return $ D.Skip (SpliceBuffering s buf2)
@@ -3045,14 +3081,12 @@ rCompactLE maxElems (D.Stream step state) =
30453081
30463082 step' _ (SpliceYielding arr next) = return $ D.Yield arr next
30473083
3048−-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3049−-- until the size becomes greater than or equal to @minElems@.
3050−--
3051−{-# INLINE_NORMAL fCompactGE #-}
3052−fCompactGE ::
3084+
3085+{-# INLINE_NORMAL fCompactGeAs #-}
3086+fCompactGeAs ::
30533087 forall m a. (MonadIO m, Unbox a)
3054− => Int -> FL.Fold m (MutArray a) (MutArray a)
3055−fCompactGE minElems = Fold step initial extract extract
3088+ => PinnedState -> Int -> FL.Fold m (MutArray a) (MutArray a)
3089+fCompactGeAs ps minElems = Fold step initial extract extract
30563090
30573091 where
30583092
@@ -3080,7 +3114,8 @@ fCompactGE minElems = Fold step initial extract extract
30803114 let len = byteLength buf + byteLength arr
30813115 buf1 <-
30823116 if byteCapacity buf < len
3083− then realloc (max len minBytes) buf
3117+ then liftIO $ reallocExplicitAs
3118+ ps (SIZE_OF(a)) (max minBytes len) buf
30843119 else return buf
30853120 buf2 <- spliceUnsafe buf1 arr
30863121 if len >= minBytes
@@ -3090,15 +3125,30 @@ fCompactGE minElems = Fold step initial extract extract
30903125 extract Nothing = return nil
30913126 extract (Just buf) = return buf
30923127
3093−-- | Like 'compactGE' but for transforming folds instead of stream.
3094−--
3095−-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3128+-- | Fold @fCompactGE minElems@ coalesces adjacent arrays in the input stream
3129+-- until the size becomes greater than or equal to @minElems@.
30963130--
3097−{-# INLINE_NORMAL lCompactGE #-}
3098−lCompactGE :: forall m a. (MonadIO m, Unbox a)
3099− => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3100−-- lCompactGE n = Fold.many (fCompactGE n)
3101−lCompactGE minElems (Fold step1 initial1 _ final1) =
3131+-- Generates unpinned arrays irrespective of the pinning status of input
3132+-- arrays.
3133+{-# INLINE fCompactGE #-}
3134+fCompactGE ::
3135+ forall m a. (MonadIO m, Unbox a)
3136+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3137+fCompactGE = fCompactGeAs Unpinned
3138+
3139+-- | Pinned version of 'fCompactGE'.
3140+{-# INLINE fPinnedCompactGE #-}
3141+fPinnedCompactGE ::
3142+ forall m a. (MonadIO m, Unbox a)
3143+ => Int -> FL.Fold m (MutArray a) (MutArray a)
3144+fPinnedCompactGE = fCompactGeAs Pinned
3145+
3146+{-# INLINE_NORMAL lCompactGeAs #-}
3147+lCompactGeAs :: forall m a. (MonadIO m, Unbox a)
3148+ => PinnedState -> Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3149+-- The fold version turns out to be a little bit slower.
3150+-- lCompactGeAs ps n = FL.many (fCompactGeAs ps n)
3151+lCompactGeAs ps minElems (Fold step1 initial1 _ final1) =
31023152 Fold step initial extract final
31033153
31043154 where
@@ -3136,7 +3186,8 @@ lCompactGE minElems (Fold step1 initial1 _ final1) =
31363186 step (Tuple' (Just buf) r1) arr = do
31373187 let len = byteLength buf + byteLength arr
31383188 buf1 <- if byteCapacity buf < len
3139− then realloc (max minBytes len) buf
3189+ then liftIO $ reallocExplicitAs
3190+ ps (SIZE_OF(a)) (max minBytes len) buf
31403191 else return buf
31413192 buf2 <- spliceUnsafe buf1 arr
31423193 runInner len r1 buf2
@@ -3156,6 +3207,23 @@ lCompactGE minElems (Fold step1 initial1 _ final1) =
31563207 FL.Partial rr -> final1 rr
31573208 FL.Done _ -> return ()
31583209
3210+-- | Like 'compactGE' but for transforming folds instead of stream.
3211+--
3212+-- >>> lCompactGE n = Fold.many (MutArray.fCompactGE n)
3213+--
3214+-- Generates unpinned arrays irrespective of the pinning status of input
3215+-- arrays.
3216+{-# INLINE lCompactGE #-}
3217+lCompactGE :: forall m a. (MonadIO m, Unbox a)
3218+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3219+lCompactGE = lCompactGeAs Unpinned
3220+
3221+-- | Pinned version of 'lCompactGE'.
3222+{-# INLINE lPinnedCompactGE #-}
3223+lPinnedCompactGE :: forall m a. (MonadIO m, Unbox a)
3224+ => Int -> Fold m (MutArray a) () -> Fold m (MutArray a) ()
3225+lPinnedCompactGE = lCompactGeAs Pinned
3226+
31593227-- | @compactGE n stream@ coalesces adjacent arrays in the @stream@ until
31603228-- the size becomes greater than or equal to @n@.
31613229--
core/src/Streamly/Internal/Data/MutByteArray/Type.hs+21−9
@@ -34,10 +34,11 @@ module Streamly.Internal.Data.MutByteArray.Type
3434 , cloneSliceUnsafeAs
3535 , cloneSliceUnsafe
3636 , pinnedCloneSliceUnsafe
37− , asPtrUnsafe -- XXX unsafePinnedAsPtr
38− , asUnpinnedPtrUnsafe -- XXX unsafeAsPtr
37+ , unsafePinnedAsPtr
38+ , unsafeAsPtr
3939
4040 -- ** Deprecated
41+ , asPtrUnsafe
4142 , nil
4243 ) where
4344
@@ -57,7 +58,7 @@ import GHC.Exts
5758
5859data PinnedState
5960 = Pinned
60− | Unpinned
61+ | Unpinned deriving (Show, Eq)
6162
6263-- XXX can use UnliftedNewtypes
6364
@@ -117,9 +118,9 @@ touch (MutByteArray contents) =
117118--
118119-- /Pre-release/
119120--
120−{-# INLINE asPtrUnsafe #-}
121−asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
122−asPtrUnsafe arr f = do
121+{-# INLINE unsafePinnedAsPtr #-}
122+unsafePinnedAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
123+unsafePinnedAsPtr arr f = do
123124 contents <- liftIO $ pin arr
124125 let !ptr = Ptr (byteArrayContents#
125126 (unsafeCoerce# (getMutableByteArray# contents)))
@@ -127,10 +128,15 @@ asPtrUnsafe arr f = do
127128 liftIO $ touch contents
128129 return r
129130
131+{-# DEPRECATED asPtrUnsafe "Please use unsafePinnedAsPtr instead." #-}
132+{-# INLINE asPtrUnsafe #-}
133+asPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
134+asPtrUnsafe = unsafePinnedAsPtr
135+
130136-- | For use with unsafe FFI functions. Does not force pin the array memory.
131−{-# INLINE asUnpinnedPtrUnsafe #-}
132−asUnpinnedPtrUnsafe :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
133−asUnpinnedPtrUnsafe arr f = do
137+{-# INLINE unsafeAsPtr #-}
138+unsafeAsPtr :: MonadIO m => MutByteArray -> (Ptr a -> m b) -> m b
139+unsafeAsPtr arr f = do
134140 let !ptr = Ptr (byteArrayContents#
135141 (unsafeCoerce# (getMutableByteArray# arr)))
136142 r <- f ptr
@@ -190,6 +196,12 @@ newBytesAs Pinned = pinnedNew
190196-- Copying
191197-------------------------------------------------------------------------------
192198
199+-- Note: Array copy is more efficient than streaming copy.
200+-- CopyMutableByteArray# translates to genMemcpy in GHC/CmmToAsm/X86/CodeGen.hs
201+-- glibc memcpy copies bytes/words/pages - unrolls the loops:
202+-- https://github.com/bminor/glibc/blob/4290aed05135ae4c0272006442d147f2155e70d7/string/memcpy.c
203+-- https://github.com/bminor/glibc/blob/4290aed05135ae4c0272006442d147f2155e70d7/string/wordcopy.c
204+
193205-- | Put a sub range of a source array into a subrange of a destination array.
194206-- This is not safe as it does not check the bounds of neither the src array
195207-- nor the destination array.
core/src/Streamly/Internal/Data/Serialize/TH.hs+5−1
@@ -7,7 +7,11 @@
77-- Maintainer : streamly@composewell.com
88-- Stability : experimental
99-- Portability : GHC
10−--
10+
11+-- XXX Instead of applying the product constructor in one go can we apply it
12+-- one at a time in case of too many arguments, compiler may not have to save
13+-- them in local vars.
14+
1115module Streamly.Internal.Data.Serialize.TH
1216 (
1317 -- Deriving
core/src/Streamly/Internal/Data/Stream/Container.hs+56−48
@@ -11,16 +11,17 @@
1111
1212module Streamly.Internal.Data.Stream.Container
1313 (
14− nub
14+ -- * Deduplication
15+ ordNub
1516
16− -- * Joins for unconstrained types
17− , joinLeftGeneric
18− , joinOuterGeneric
17+ -- * Joins
18+ , leftJoin
19+ , outerJoin
1920
20− -- * Joins with Ord constraint
21− , joinInner
22− , joinLeft
23− , joinOuter
21+ -- * Ord Joins
22+ , innerOrdJoin
23+ , leftOrdJoin
24+ , outerOrdJoin
2425 )
2526where
2627
@@ -47,12 +48,15 @@ import qualified Streamly.Internal.Data.Stream.Transformer as Stream
4748
4849#include "DocTestDataStream.hs"
4950
50−-- | The memory used is proportional to the number of unique elements in the
51−-- stream. If we want to limit the memory we can just use "take" to limit the
52−-- uniq elements in the stream.
53−{-# INLINE_NORMAL nub #-}
54−nub :: (Monad m, Ord a) => Stream m a -> Stream m a
55−nub (Stream step1 state1) = Stream step (Set.empty, state1)
51+-- | @nub@ specialized to 'Ord' types for better performance. Returns a
52+-- subsequence of the stream removing any duplicate elements.
53+--
54+-- The memory used is proportional to the number of unique elements in the
55+-- stream. One way to limit the memory is to use @take@ on the resulting
56+-- stream to limit the unique elements in the stream.
57+{-# INLINE_NORMAL ordNub #-}
58+ordNub :: (Monad m, Ord a) => Stream m a -> Stream m a
59+ordNub (Stream step1 state1) = Stream step (Set.empty, state1)
5660
5761 where
5862
@@ -78,8 +82,8 @@ toMap =
7882--
7983-- XXX An IntMap may be faster when the keys are Int.
8084-- XXX Use hashmap instead of map?
81−--
82−-- | Like 'joinInner' but uses a 'Map' for efficiency.
85+
86+-- | 'innerJoin' specialized to 'Ord' types for better performance.
8387--
8488-- If the input streams have duplicate keys, the behavior is undefined.
8589--
@@ -90,10 +94,10 @@ toMap =
9094-- Time: O(m + n)
9195--
9296-- /Pre-release/
93−{-# INLINE joinInner #-}
94−joinInner :: (Monad m, Ord k) =>
97+{-# INLINE innerOrdJoin #-}
98+innerOrdJoin :: (Monad m, Ord k) =>
9599 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, b)
96−joinInner s1 s2 =
100+innerOrdJoin s1 s2 =
97101 Stream.concatEffect $ do
98102 km <- toMap s2
99103 pure $ Stream.mapMaybe (joinAB km) s1
@@ -106,32 +110,33 @@ joinInner s1 s2 =
106110 Nothing -> Nothing
107111
108112-- XXX We can do this concurrently.
113+-- XXX Check performance of StreamD vs StreamK
109114-- XXX If the second stream is sorted and passed as an Array or a seek capable
110115-- stream then we could use binary search if we have an Ord instance or
111116-- Ordering returning function. The time complexity would then become (m x log
112117-- n).
113118
114−-- XXX Check performance of StreamD vs StreamK
115−
116−-- | Like 'joinInner' but emit @(a, Just b)@, and additionally, for those @a@'s
117−-- that are not equal to any @b@ emit @(a, Nothing)@.
119+-- | Like 'innerJoin' but emits @(a, Just b)@ whenever a and b are equal, for
120+-- those @a@'s that are not equal to any @b@ emits @(a, Nothing)@.
118121--
119−-- The second stream is evaluated multiple times. If the stream is a
120−-- consume-once stream then the caller should cache it in an 'Data.Array.Array'
121−-- before calling this function. Caching may also improve performance if the
122−-- stream is expensive to evaluate.
122+-- This is a generalization of 'innerJoin' to include all elements from the
123+-- left stream and not just those which have an equal in the right stream. This
124+-- is not a commutative operation, the order of the stream arguments matters.
123125--
124−-- >>> joinRightGeneric eq = flip (Stream.joinLeftGeneric eq)
126+-- All the caveats mentioned in 'innerJoin' apply here as well. Right join is
127+-- not provided because it is just a flipped left join:
128+--
129+-- >>> rightJoin eq = flip (Stream.leftJoin eq)
125130--
126131-- Space: O(n) assuming the second stream is cached in memory.
127132--
128133-- Time: O(m x n)
129134--
130135-- /Unimplemented/
131−{-# INLINE joinLeftGeneric #-}
132−joinLeftGeneric :: Monad m =>
136+{-# INLINE leftJoin #-}
137+leftJoin :: Monad m =>
133138 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
134−joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
139+leftJoin eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
135140 a <- mkCross (Stream.liftInner s1)
136141 -- XXX should we use StreamD monad here?
137142 -- XXX Is there a better way to perform some action at the end of a loop
@@ -152,19 +157,17 @@ joinLeftGeneric eq s1 s2 = Stream.evalStateT (return False) $ unCross $ do
152157 else mkCross Stream.nil
153158 Nothing -> return (a, Nothing)
154159
155−-- XXX rename to joinLeftOrd?
156−
157−-- | A more efficient 'joinLeft' using a hashmap for efficiency.
160+-- | 'leftJoin' specialized to 'Ord' types for better performance.
158161--
159162-- Space: O(n)
160163--
161164-- Time: O(m + n)
162165--
163166-- /Pre-release/
164−{-# INLINE joinLeft #-}
165−joinLeft :: (Ord k, Monad m) =>
167+{-# INLINE leftOrdJoin #-}
168+leftOrdJoin :: (Ord k, Monad m) =>
166169 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, a, Maybe b)
167−joinLeft s1 s2 =
170+leftOrdJoin s1 s2 =
168171 Stream.concatEffect $ do
169172 km <- toMap s2
170173 return $ fmap (joinAB km) s1
@@ -177,14 +180,19 @@ joinLeft s1 s2 =
177180 Nothing -> (k, a, Nothing)
178181
179182-- XXX We can do this concurrently.
183+-- XXX Check performance of StreamD vs StreamK cross operation.
180184
181−-- XXX Check performance of StreamD vs StreamK
182−
183−-- | Like 'joinLeft' but emits a @(Just a, Just b)@. Like 'joinLeft', for those
185+-- | Like 'leftJoin' but emits a @(Just a, Just b)@. Like 'leftJoin', for those
184186-- @a@'s that are not equal to any @b@ emit @(Just a, Nothing)@, but
185187-- additionally, for those @b@'s that are not equal to any @a@ emit @(Nothing,
186188-- Just b)@.
187189--
190+-- This is a generalization of left join to include all the elements from the
191+-- right stream as well, in other words it is a combination of left and right
192+-- joins. This is a commutative operation. The order of stream arguments can be
193+-- changed without affecting results, except for the ordering of elements in
194+-- the resulting tuple.
195+--
188196-- For space efficiency use the smaller stream as the second stream.
189197--
190198-- Space: O(n)
@@ -192,15 +200,15 @@ joinLeft s1 s2 =
192200-- Time: O(m x n)
193201--
194202-- /Pre-release/
195−{-# INLINE joinOuterGeneric #-}
196−joinOuterGeneric :: MonadIO m =>
203+{-# INLINE outerJoin #-}
204+outerJoin :: MonadIO m =>
197205 (a -> b -> Bool)
198206 -> Stream m a
199207 -> Stream m b
200208 -> Stream m (Maybe a, Maybe b)
201−joinOuterGeneric eq s1 s =
209+outerJoin eq s1 s2 =
202210 Stream.concatEffect $ do
203− inputArr <- Array.fromStream s
211+ inputArr <- Array.fromStream s2
204212 let len = Array.length inputArr
205213 foundArr <-
206214 Stream.fold
@@ -254,18 +262,18 @@ joinOuterGeneric eq s1 s =
254262-- a flag. At the end go through @Stream m b@ and find those that are not in that
255263-- hash to return (Nothing, b).
256264
257−-- | Like 'joinOuter' but uses a 'Map' for efficiency.
265+-- | 'outerJoin' specialized to 'Ord' types for better performance.
258266--
259267-- Space: O(m + n)
260268--
261269-- Time: O(m + n)
262270--
263271-- /Pre-release/
264−{-# INLINE joinOuter #-}
265−joinOuter ::
272+{-# INLINE outerOrdJoin #-}
273+outerOrdJoin ::
266274 (Ord k, MonadIO m) =>
267275 Stream m (k, a) -> Stream m (k, b) -> Stream m (k, Maybe a, Maybe b)
268−joinOuter s1 s2 =
276+outerOrdJoin s1 s2 =
269277 Stream.concatEffect $ do
270278 km1 <- kvFold s1
271279 km2 <- kvFold s2
core/src/Streamly/Internal/Data/Stream/Eliminate.hs+33−25
@@ -13,36 +13,24 @@
1313--
1414module Streamly.Internal.Data.Stream.Eliminate
1515 (
16− -- * Running a 'Fold'
17− fold
18−
19− -- -- * Running a 'Parser'
20− , parse
16+ -- * Running a Parser
17+ parse
2118 , parseD
2219 , parseBreak
2320 , parseBreakD
2421
25− -- * Stream Deconstruction
22+ -- * Deconstruction
2623 , uncons
2724
2825 -- * Right Folds
29− , foldrM
30− , foldr
31− , foldrMx
3226 , foldr1
3327
34− -- * Left Folds
35− , foldlM'
36− , foldl'
37− , foldlMx'
38− , foldlx'
39−
4028 -- * Specific Fold Functions
41− , drain
4229 , mapM_ -- Map and Fold
4330 , null
4431 , head
4532 , headElse
33+ , init
4634 , tail
4735 , last
4836 , elem
@@ -60,16 +48,9 @@ module Streamly.Internal.Data.Stream.Eliminate
6048 , the
6149
6250 -- * To containers
63− , toList
6451 , toListRev
6552
6653 -- * Multi-Stream Folds
67− -- ** Comparisons
68− -- | These should probably be expressed using zipping operations.
69− , eqBy
70− , cmpBy
71−
72− -- ** Substreams
7354 -- | These should probably be expressed using parsers.
7455 , isPrefixOf
7556 , isInfixOf
@@ -90,7 +71,7 @@ import Foreign.Storable (Storable)
9071import GHC.Exts (SpecConstrAnnotation(..))
9172import GHC.Types (SPEC(..))
9273import Streamly.Internal.Data.Parser (ParseError(..))
93−import Streamly.Internal.Data.SVar.Type (defState)
74+import Streamly.Internal.Data.SVar.Type (adaptState, defState)
9475import Streamly.Internal.Data.Unbox (Unbox)
9576
9677import Streamly.Internal.Data.Maybe.Strict (Maybe'(..))
@@ -105,7 +86,7 @@ import qualified Streamly.Internal.Data.Stream.Transform as StreamD
10586
10687import Prelude hiding
10788 ( Foldable(..), all, any, head, last, lookup, mapM, mapM_
108− , notElem, splitAt, tail, (!!))
89+ , notElem, splitAt, init, tail, (!!))
10990import Data.Foldable (length)
11091import Streamly.Internal.Data.Stream.Type
11192
@@ -391,9 +372,35 @@ head = foldrM (\x _ -> return (Just x)) (return Nothing)
391372headElse :: Monad m => a -> Stream m a -> m a
392373headElse a = foldrM (\x _ -> return x) (return a)
393374
375+{-# INLINE_NORMAL init #-}
376+init :: Monad m => Stream m a -> m (Maybe (Stream m a))
377+init stream = do
378+ r <- uncons stream
379+ case r of
380+ Nothing -> return Nothing
381+ Just (h, Stream step1 state1) ->
382+ return $ Just $ Stream step (h, state1)
383+
384+ where
385+
386+ step gst (a, s1) = do
387+ res <- step1 (adaptState gst) s1
388+ return $
389+ case res of
390+ Yield x s -> Yield a (x, s)
391+ Skip s -> Skip (a, s)
392+ Stop -> Stop
393+
394+-- | Same as:
395+--
396+-- >>> tail = fmap (fmap snd) . Stream.uncons
397+--
394398-- Does not fuse, has the same performance as the StreamK version.
399+--
395400{-# INLINE_NORMAL tail #-}
396401tail :: Monad m => Stream m a -> m (Maybe (Stream m a))
402+tail = fmap (fmap snd) . uncons
403+{-
397404tail (UnStream step state) = go SPEC state
398405 where
399406 go !_ st = do
@@ -402,6 +409,7 @@ tail (UnStream step state) = go SPEC state
402409 Yield _ s -> return (Just $ Stream step s)
403410 Skip s -> go SPEC s
404411 Stop -> return Nothing
412+-}
405413
406414-- XXX will it fuse? need custom impl?
407415{-# INLINE_NORMAL last #-}
core/src/Streamly/Internal/Data/Stream/Exception.hs+9−6
@@ -9,17 +9,20 @@
99
1010module Streamly.Internal.Data.Stream.Exception
1111 (
12− gbracket_
13− , gbracket
14− , before
15− , afterUnsafe
12+ -- * Resources
13+ before
1614 , afterIO
15+ , afterUnsafe
16+ , finallyIO
17+ , finallyUnsafe
18+ , gbracket_
19+ , gbracket
1720 , bracketUnsafe
1821 , bracketIO3
1922 , bracketIO
23+
24+ -- * Exceptions
2025 , onException
21− , finallyUnsafe
22− , finallyIO
2326 , ghandle
2427 , handle
2528 )
core/src/Streamly/Internal/Data/Stream/Generate.hs−12
@@ -16,20 +16,13 @@ module Streamly.Internal.Data.Stream.Generate
1616 (
1717 -- * Primitives
1818 nil
19− , nilM
2019 , cons
21− , consM
22−
23− -- * From 'Unfold'
24− , unfold
2520
2621 -- * Unfolding
2722 , unfoldr
2823 , unfoldrM
2924
3025 -- * From Values
31− , fromPure
32− , fromEffect
3326 , repeat
3427 , repeatM
3528 , replicate
@@ -95,7 +88,6 @@ module Streamly.Internal.Data.Stream.Generate
9588 -- * From Containers
9689 -- | Transform an input structure into a stream.
9790
98− , fromList
9991 , fromListM
10092 , fromFoldable
10193 , fromFoldableM
@@ -104,10 +96,6 @@ module Streamly.Internal.Data.Stream.Generate
10496 , fromPtr
10597 , fromPtrN
10698 , fromByteStr#
107−
108− -- * Conversions
109− , fromStreamK
110− , toStreamK
11199 )
112100where
113101
core/src/Streamly/Internal/Data/Stream/Nesting.hs+10−64
@@ -55,11 +55,6 @@ module Streamly.Internal.Data.Stream.Nesting
5555 -- yields an element. A special case of unfoldRoundRobin.
5656 , roundRobin -- interleaveFair?/ParallelFair
5757
58− -- *** Zipping
59− -- | Zip corresponding elements of two streams.
60− , zipWith
61− , zipWithM
62−
6358 -- *** Merging
6459 -- | Interleave elements from two streams based on a condition.
6560 , mergeBy
@@ -76,17 +71,10 @@ module Streamly.Internal.Data.Stream.Nesting
7671 -- unfoldMany: Unfold m a b -> t m a -> t m b
7772 -- @
7873
79− -- *** ConcatMap
80− -- | Generate streams by mapping a stream generator on each element of an
81− -- input stream, append the resulting streams and flatten.
82− , concatMap
83− , concatMapM
84−
8574 -- *** ConcatUnfold
8675 -- | Generate streams by using an unfold on each element of an input
8776 -- stream, append the resulting streams and flatten. A special case of
8877 -- gintercalate.
89− , unfoldMany
9078 , ConcatUnfoldInterleaveState (..)
9179 , unfoldInterleave
9280 , unfoldRoundRobin
@@ -118,11 +106,8 @@ module Streamly.Internal.Data.Stream.Nesting
118106
119107 -- ** Folding
120108 -- | Apply folds on a stream.
121− , foldMany
122− , refoldMany
123109 , foldSequence
124110 , foldIterateM
125− , refoldIterateM
126111
127112 -- ** Parsing
128113 -- | Parsing is opposite to flattening. 'parseMany' is dual to concatMap or
@@ -139,7 +124,6 @@ module Streamly.Internal.Data.Stream.Nesting
139124
140125 -- ** Grouping
141126 -- | Group segments of a stream and fold. Special case of parsing.
142− , groupsOf
143127 , groupsBy
144128 , groupsWhile
145129 , groupsRollingBy
@@ -147,8 +131,8 @@ module Streamly.Internal.Data.Stream.Nesting
147131 -- ** Splitting
148132 -- | A special case of parsing.
149133 , wordsBy
150− , splitOnSeq
151− , splitOnSuffixSeq
134+ , splitOnSeq -- XXX splitOnSeg
135+ , splitOnSuffixSeq -- XXX splitOnSegSuffix, splitOnTrailer
152136
153137 -- XXX Implement these as folds or parsers instead.
154138 , splitOnSuffixSeqAny
@@ -157,9 +141,8 @@ module Streamly.Internal.Data.Stream.Nesting
157141
158142 -- * Transform (Nested Containers)
159143 -- | Opposite to compact in ArrayStream
160− , splitInnerBy
161− , splitInnerBySuffix
162− , intersectBySorted
144+ , splitInnerBy -- XXX innerSplitOn
145+ , splitInnerBySuffix -- XXX innerSplitOnSuffix
163146
164147 -- * Reduce By Streams
165148 , dropPrefix
@@ -609,49 +592,6 @@ mergeFstBy :: -- Monad m =>
609592mergeFstBy _f _m1 _m2 = undefined
610593 -- fromStreamK $ D.mergeFstBy f (toStreamD m1) (toStreamD m2)
611594
612−-------------------------------------------------------------------------------
613−-- Intersection of sorted streams
614−-------------------------------------------------------------------------------
615−
616−-- Assuming the streams are sorted in ascending order
617−{-# INLINE_NORMAL intersectBySorted #-}
618−intersectBySorted :: Monad m
619− => (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
620−intersectBySorted cmp (Stream stepa ta) (Stream stepb tb) =
621− Stream step
622− ( ta -- left stream state
623− , tb -- right stream state
624− , Nothing -- left value
625− , Nothing -- right value
626− )
627−
628− where
629−
630− {-# INLINE_LATE step #-}
631− -- step 1, fetch the first value
632− step gst (sa, sb, Nothing, b) = do
633− r <- stepa gst sa
634− return $ case r of
635− Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
636− Skip sa' -> Skip (sa', sb, Nothing, b)
637− Stop -> Stop
638−
639− -- step 2, fetch the second value
640− step gst (sa, sb, a@(Just _), Nothing) = do
641− r <- stepb gst sb
642− return $ case r of
643− Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
644− Skip sb' -> Skip (sa, sb', a, Nothing)
645− Stop -> Stop
646−
647− -- step 3, compare the two values
648− step _ (sa, sb, Just a, Just b) = do
649− let res = cmp a b
650− return $ case res of
651− GT -> Skip (sa, sb, Just a, Nothing) -- step 2
652− LT -> Skip (sa, sb, Nothing, Just b) -- step 1
653− EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
654−
655595------------------------------------------------------------------------------
656596-- Combine N Streams - unfoldMany
657597------------------------------------------------------------------------------
@@ -3046,6 +2986,8 @@ splitInnerBySuffix isEmpty splitter joiner (Stream step1 state1) =
30462986--
30472987-- Space: @O(1)@
30482988--
2989+-- See also stripPrefix.
2990+--
30492991-- /Unimplemented/
30502992{-# INLINE dropPrefix #-}
30512993dropPrefix ::
@@ -3058,6 +3000,8 @@ dropPrefix = error "Not implemented yet!"
30583000--
30593001-- Space: @O(n)@ where n is the length of the infix.
30603002--
3003+-- See also stripInfix.
3004+--
30613005-- /Unimplemented/
30623006{-# INLINE dropInfix #-}
30633007dropInfix ::
@@ -3070,6 +3014,8 @@ dropInfix = error "Not implemented yet!"
30703014--
30713015-- Space: @O(n)@ where n is the length of the suffix.
30723016--
3017+-- See also stripSuffix.
3018+--
30733019-- /Unimplemented/
30743020{-# INLINE dropSuffix #-}
30753021dropSuffix ::
core/src/Streamly/Internal/Data/Stream/Top.hs+243−115
@@ -8,38 +8,73 @@
88-- Portability : GHC
99--
1010-- Top level module that can depend on all other lower level Stream modules.
11+--
12+-- Design notes:
13+--
14+-- The order of arguments in the join operations should ideally be opposite. It
15+-- should be such that the infinite stream is the last one. The transformation
16+-- should be on the last argument, so if you curry the functions with all other
17+-- arguments we get a @Stream -> Stream@ function. The first stream argument
18+-- may be considered as a config or modifier for the operation.
19+--
20+-- Benefit of changing the order is that we get a more intuitive Stream ->
21+-- Stream transformation after currying all other arguments. The inner loop
22+-- streams become arguments for the transformation, more like local modifiers
23+-- for the global outer stream as the last argument. Thus we can continue using
24+-- transformations on the outer stream in a composed pipeline. Otherwise we can
25+-- use flip to flip the order.
26+--
27+-- The fact that the inner stream can be used in the loop multiple times also
28+-- tells that this is not the real effectful stream, it is more like a pure
29+-- stream or an array. In fact we may consider using an Identity streams as
30+-- inner streams in which case these functions will not look nice.
31+--
32+-- Downsides:
33+--
34+-- * Maybe less intuitive to think about, because we usually think the first
35+-- stream as the outer loop and second as the inner.
36+-- * Zip and merge operations will continue using the opposite order.
37+-- * Need to change the order of cross, crossWith operations as well
38+-- * It will be inconsistent with Data.List. The functions cannot be used as
39+-- intuitive operators.
40+--
41+-- The choice is similar to concatMap vs bind. concatMap is pipeline
42+-- composition friendly but bind is user intuition friendly. Another option is
43+-- to have other functions with a different argument order e.g. flippedCross
44+-- instead of cross.
45+--
46+-- If we change the order we have to make sure that we have a consistent
47+-- convention for set-like and the cross join operations.
1148
1249module Streamly.Internal.Data.Stream.Top
1350 (
14− -- * Transformation
15− -- ** Sampling
51+ -- * Sampling
1652 -- | Value agnostic filtering.
1753 strideFromThen
1854
19− -- * Nesting
20− -- ** Set like operations
21− -- | These are not exactly set operations because streams are not
22− -- necessarily sets, they may have duplicated elements. These operations
23− -- are generic i.e. they work on streams of unconstrained types, therefore,
24− -- they have quadratic performance characterstics. For better performance
25− -- using Set structures see the Streamly.Internal.Data.Stream.Container
26− -- module.
27− , filterInStreamGenericBy
28− , deleteInStreamGenericBy
29− , unionWithStreamGenericBy
30−
31− -- ** Set like operations on sorted streams
32− , filterInStreamAscBy
33− , deleteInStreamAscBy
34− , unionWithStreamAscBy
35−
36− -- ** Join operations
37− , joinInnerGeneric
38−
39− -- * Joins on sorted stream
40− , joinInnerAscBy
41− , joinLeftAscBy
42− , joinOuterAscBy
55+ -- * Straight Joins
56+ -- | These are set-like operations but not exactly set operations because
57+ -- streams are not necessarily sets, they may have duplicated elements.
58+ -- These operations are generic i.e. they work on streams of unconstrained
59+ -- types, therefore, they have quadratic performance characterstics. For
60+ -- better performance using Set or Map structures see the
61+ -- Streamly.Internal.Data.Stream.Container module.
62+ , intersectBy
63+ , deleteFirstsBy
64+ , unionBy
65+
66+ -- Set like operations on sorted streams
67+ , sortedIntersectBy
68+ , sortedDeleteFirstsBy
69+ , sortedUnionBy
70+
71+ -- * Cross Joins
72+ , innerJoin
73+
74+ -- Joins on sorted stream
75+ , innerSortedJoin
76+ , leftSortedJoin
77+ , outerSortedJoin
4378 )
4479where
4580
@@ -48,7 +83,7 @@ where
4883import Control.Monad.IO.Class (MonadIO(..))
4984import Data.IORef (newIORef, readIORef, modifyIORef')
5085import Streamly.Internal.Data.Fold.Type (Fold)
51−import Streamly.Internal.Data.Stream.Type (Stream, cross)
86+import Streamly.Internal.Data.Stream.Type (Stream(..), Step(..), cross)
5287
5388import qualified Data.List as List
5489import qualified Streamly.Internal.Data.Fold as Fold
@@ -110,29 +145,40 @@ strideFromThen offset stride =
110145-- binary search if we have an Ord instance or Ordering returning function. The
111146-- time complexity would then become (m x log n).
112147
113−-- | Like 'cross' but emits only those tuples where @a == b@ using the
114−-- supplied equality predicate.
148+-- | Like 'cross' but emits only those tuples where @a == b@ using the supplied
149+-- equality predicate. This is essentially a @cross intersection@ of two
150+-- streams.
115151--
116152-- Definition:
117153--
118−-- >>> joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
154+-- >>> innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ Stream.cross s1 s2
155+--
156+-- The second (inner) stream must be finite. Moreover, it must be either pure
157+-- or capable of multiple evaluations. If not then the caller should cache it
158+-- in an 'Data.Array.Array', if the type does not have an 'Unbox' instance then
159+-- use the Generic 'Data.Array.Generic.Array'. Convert the array to stream
160+-- before calling this function. Caching may also improve performance if the
161+-- stream is expensive to evaluate.
119162--
120−-- You should almost always prefer @joinInnerOrd@ over 'joinInnerGeneric' if
121−-- possible. @joinInnerOrd@ is an order of magnitude faster but may take more
122−-- space for caching the second stream.
163+-- If you care about performance this function should be your last choice among
164+-- all inner joins. 'Streamly.Internal.Data.Unfold.innerJoin' is a much faster
165+-- fused alternative. 'innerSortedJoin' is a faster alternative when streams
166+-- are sorted. 'innerOrdJoin' is an order of magnitude faster alternative when
167+-- the type has an 'Ord' instance.
123168--
124−-- See 'Streamly.Internal.Data.Unfold.joinInnerGeneric' for a much faster fused
125−-- alternative.
169+-- Note: Conceptually, this is a commutative operation. Result includes all the
170+-- elements from the left and the right stream. The order of streams can be
171+-- changed without affecting results, except for the ordering within the tuple.
126172--
127173-- Time: O(m x n)
128174--
129175-- /Pre-release/
130−{-# INLINE joinInnerGeneric #-}
131−joinInnerGeneric :: Monad m =>
176+{-# INLINE innerJoin #-}
177+innerJoin :: Monad m =>
132178 (a -> b -> Bool) -> Stream m a -> Stream m b -> Stream m (a, b)
133−joinInnerGeneric eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
179+innerJoin eq s1 s2 = Stream.filter (\(a, b) -> a `eq` b) $ cross s1 s2
134180{-
135−joinInnerGeneric eq s1 s2 = do
181+innerJoin eq s1 s2 = do
136182 -- ConcatMap works faster than bind
137183 Stream.concatMap (\a ->
138184 Stream.concatMap (\b ->
@@ -143,44 +189,44 @@ joinInnerGeneric eq s1 s2 = do
143189 ) s1
144190-}
145191
146−-- | A more efficient 'joinInner' for sorted streams.
192+-- | A more efficient 'innerJoin' for sorted streams.
147193--
148194-- Space: O(1)
149195--
150196-- Time: O(m + n)
151197--
152198-- /Unimplemented/
153−{-# INLINE joinInnerAscBy #-}
154−joinInnerAscBy ::
199+{-# INLINE innerSortedJoin #-}
200+innerSortedJoin ::
155201 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, b)
156−joinInnerAscBy = undefined
202+innerSortedJoin = undefined
157203
158−-- | A more efficient 'joinLeft' for sorted streams.
204+-- | A more efficient 'leftJoin' for sorted streams.
159205--
160206-- Space: O(1)
161207--
162208-- Time: O(m + n)
163209--
164210-- /Unimplemented/
165−{-# INLINE joinLeftAscBy #-}
166−joinLeftAscBy :: -- Monad m =>
211+{-# INLINE leftSortedJoin #-}
212+leftSortedJoin :: -- Monad m =>
167213 (a -> b -> Ordering) -> Stream m a -> Stream m b -> Stream m (a, Maybe b)
168−joinLeftAscBy _eq _s1 _s2 = undefined
214+leftSortedJoin _eq _s1 _s2 = undefined
169215
170−-- | A more efficient 'joinOuter' for sorted streams.
216+-- | A more efficient 'outerJoin' for sorted streams.
171217--
172218-- Space: O(1)
173219--
174220-- Time: O(m + n)
175221--
176222-- /Unimplemented/
177−{-# INLINE joinOuterAscBy #-}
178−joinOuterAscBy :: -- Monad m =>
223+{-# INLINE outerSortedJoin #-}
224+outerSortedJoin :: -- Monad m =>
179225 (a -> b -> Ordering)
180226 -> Stream m a
181227 -> Stream m b
182228 -> Stream m (Maybe a, Maybe b)
183−joinOuterAscBy _eq _s1 _s2 = undefined
229+outerSortedJoin _eq _s1 _s2 = undefined
184230
185231------------------------------------------------------------------------------
186232-- Set operations (special joins)
@@ -190,8 +236,8 @@ joinOuterAscBy _eq _s1 _s2 = undefined
190236-- the best would be to use an Array with linear search. If the second stream
191237-- is sorted we can also use a binary search, using Ord constraint.
192238
193−-- | Keep only those elements in the second stream that are present in the
194−-- first stream too. The first stream is folded to a container using the
239+-- | Keep only those elements in the first stream that are present in the
240+-- second stream too. The second stream is folded to a container using the
195241-- supplied fold and then the elements in the container are looked up using the
196242-- supplied lookup function.
197243--
@@ -206,23 +252,30 @@ filterStreamWith :: Monad m =>
206252filterStreamWith fld member s1 s2 =
207253 Stream.concatEffect
208254 $ do
209− xs <- Stream.fold fld s1
210− return $ Stream.filter (`member` xs) s2
255+ xs <- Stream.fold fld s2
256+ return $ Stream.filter (`member` xs) s1
211257
212−-- | 'filterInStreamGenericBy' retains only those elements in the second stream that
213−-- are present in the first stream.
214−--
215−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [1,2,2,4]) (Stream.fromList [2,1,1,3])
216−-- [2,1,1]
217−--
218−-- >>> Stream.fold Fold.toList $ Stream.filterInStreamGenericBy (==) (Stream.fromList [2,1,1,3]) (Stream.fromList [1,2,2,4])
219−-- [1,2,2]
258+-- XXX instead of folding the second stream to a list we could use it directly.
259+-- If the user wants they can generate the stream from an array and also call
260+-- uniq or nub on it. We can provide a convenience Stream -> Stream to cache
261+-- a finite stream in an array and serve it from the cache. The user can decide
262+-- what is best based on the context. They can also choose to use a boxed or
263+-- unboxed array for caching. To force caching we can make the second stream
264+-- monad type Identity. But that may be less flexible. One option is to use
265+-- cachedIntersectBy etc for automatic caching.
266+
267+-- | 'intersectBy' returns a subsequence of the first stream which intersects
268+-- with the second stream. Note that this is not a commutative operation unlike
269+-- a set intersection, because of duplicate elements in the stream the order of
270+-- the streams matters. This is similar to 'Data.List.intersectBy'. Note that
271+-- intersectBy is a special case of 'innerJoin'.
220272--
221−-- Similar to the list intersectBy operation but with the stream argument order
222−-- flipped.
273+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.intersectBy (==) (Stream.fromList s1) (Stream.fromList s2)
274+-- >>> f [1,3,4,4,5]) [2,3,4,5,5]
275+-- [3,4,4,5]
223276--
224−-- The first stream must be finite and must not block. Second stream is
225−-- processed only after the first stream is fully realized.
277+-- First stream can be infinite, the second stream must be finite and must be
278+-- capable of multiple evaluations.
226279--
227280-- Space: O(n) where @n@ is the number of elements in the second stream.
228281--
@@ -230,46 +283,87 @@ filterStreamWith fld member s1 s2 =
230283-- @n@ is the number of elements in the second stream.
231284--
232285-- /Pre-release/
233−{-# INLINE filterInStreamGenericBy #-}
234−filterInStreamGenericBy :: Monad m =>
286+{-# INLINE intersectBy #-}
287+intersectBy :: Monad m =>
235288 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
236−filterInStreamGenericBy eq =
289+intersectBy eq =
237290 -- XXX Use an (unboxed) array instead.
238291 filterStreamWith
239292 (Fold.scanMaybe (Fold.uniqBy eq) Fold.toListRev)
240293 (List.any . eq)
241294
242−-- | Like 'filterInStreamGenericBy' but assumes that the input streams are sorted in
295+-------------------------------------------------------------------------------
296+-- Intersection of sorted streams
297+-------------------------------------------------------------------------------
298+
299+-- XXX The sort order is not important as long both the streams have the same
300+-- sort order. We need to move only in one direction in each stream.
301+-- XXX Fix the argument order to use the same behavior as intersectBy.
302+
303+-- | Like 'intersectBy' but assumes that the input streams are sorted in
243304-- ascending order. To use it on streams sorted in descending order pass an
244305-- inverted comparison function returning GT for less than and LT for greater
245306-- than.
246307--
308+-- Both streams can be infinite.
309+--
247310-- Space: O(1)
248311--
249312-- Time: O(m+n)
250313--
251314-- /Pre-release/
252−{-# INLINE filterInStreamAscBy #-}
253−filterInStreamAscBy :: Monad m =>
315+{-# INLINE_NORMAL sortedIntersectBy #-}
316+sortedIntersectBy :: Monad m =>
254317 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
255−filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
318+sortedIntersectBy cmp (Stream stepa ta) (Stream stepb tb) =
319+ Stream step
320+ ( ta -- left stream state
321+ , tb -- right stream state
322+ , Nothing -- left value
323+ , Nothing -- right value
324+ )
256325
257−-- | Delete all elements of the first stream from the seconds stream. If an
258−-- element occurs multiple times in the first stream as many occurrences of it
259−-- are deleted from the second stream.
260−--
261−-- >>> Stream.fold Fold.toList $ Stream.deleteInStreamGenericBy (==) (Stream.fromList [1,2,3]) (Stream.fromList [1,2,2])
262−-- [2]
326+ where
327+
328+ {-# INLINE_LATE step #-}
329+ -- step 1, fetch the first value
330+ step gst (sa, sb, Nothing, b) = do
331+ r <- stepa gst sa
332+ return $ case r of
333+ Yield a sa' -> Skip (sa', sb, Just a, b) -- step 2/3
334+ Skip sa' -> Skip (sa', sb, Nothing, b)
335+ Stop -> Stop
336+
337+ -- step 2, fetch the second value
338+ step gst (sa, sb, a@(Just _), Nothing) = do
339+ r <- stepb gst sb
340+ return $ case r of
341+ Yield b sb' -> Skip (sa, sb', a, Just b) -- step 3
342+ Skip sb' -> Skip (sa, sb', a, Nothing)
343+ Stop -> Stop
344+
345+ -- step 3, compare the two values
346+ step _ (sa, sb, Just a, Just b) = do
347+ let res = cmp a b
348+ return $ case res of
349+ GT -> Skip (sa, sb, Just a, Nothing) -- step 2
350+ LT -> Skip (sa, sb, Nothing, Just b) -- step 1
351+ EQ -> Yield a (sa, sb, Nothing, Just b) -- step 1
352+
353+-- | Returns a subsequence of the first stream, deleting first occurrences of
354+-- those elements that are present in the second stream. Note that this is not
355+-- a commutative operation. This is similar to the 'Data.List.deleteFirstsBy'.
263356--
264−-- The following laws hold:
357+-- >>> f xs ys = Stream.fold Fold.toList $ Stream.deleteFirstsBy (==) (Stream.fromList xs) (Stream.fromList ys)
358+-- >>> f [1,2,2,3,3,5] [1,2,2,3,4]
359+-- [2,3,5]
265360--
266−-- > deleteInStreamGenericBy (==) s1 (s1 `append` s2) === s2
267−-- > deleteInStreamGenericBy (==) s1 (s1 `interleave` s2) === s2
361+-- The following holds:
268362--
269−-- Same as the list 'Data.List.//' operation but with argument order flipped.
363+-- > deleteFirstsBy (==) (Stream.nub s2 `append` s1) s2 === s1
364+-- > deleteFirstsBy (==) (Stream.nub s2 `interleave` s1) s2 === s1
270365--
271−-- The first stream must be finite and must not block. Second stream is
272−-- processed only after the first stream is fully realized.
366+-- First stream can be infinite, second stream must be finite.
273367--
274368-- Space: O(m) where @m@ is the number of elements in the first stream.
275369--
@@ -277,58 +371,83 @@ filterInStreamAscBy eq s1 s2 = Stream.intersectBySorted eq s2 s1
277371-- @n@ is the number of elements in the second stream.
278372--
279373-- /Pre-release/
280−{-# INLINE deleteInStreamGenericBy #-}
281−deleteInStreamGenericBy :: Monad m =>
374+{-# INLINE deleteFirstsBy #-}
375+deleteFirstsBy :: Monad m =>
282376 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
283−deleteInStreamGenericBy eq s1 s2 =
284− Stream.concatEffect
285− $ do
286− -- This may work well if s1 is small
287− -- If s1 is big we can go through s1, deleting elements from s2 and
288− -- not emitting an element if it was successfully deleted from s2.
289− -- we will need a deleteBy that can return whether the element was
290− -- deleted or not.
291− xs <- Stream.fold Fold.toList s2
292− let f = Fold.foldl' (flip (List.deleteBy eq)) xs
293− fmap Stream.fromList $ Stream.fold f s1
377+deleteFirstsBy eq s2 s1 =
378+ -- XXX s2 can be a sorted mutable array and we can use binary
379+ -- search to find. Mark the element deleted, count the deletions
380+ -- and reconsolidate the array when a min number of elements is
381+ -- deleted.
294382
295−-- | A more efficient 'deleteInStreamGenericBy' for streams sorted in ascending order.
383+ -- XXX Use StreamK or list as second argument instead of Stream to avoid
384+ -- concatEffect?
385+ Stream.concatEffect $ do
386+ xs <- Stream.toList s1
387+ -- It reverses the list but that is fine.
388+ let del x =
389+ List.foldl' (\(ys,res) y ->
390+ if x `eq` y
391+ then (ys, True)
392+ else (x:ys, res)) ([], False)
393+ g (ys,_) x =
394+ let (ys1, deleted) = del x ys
395+ in if deleted
396+ then (ys1, Nothing)
397+ else (ys1, Just x)
398+ in return
399+ $ Stream.catMaybes
400+ $ fmap snd
401+ $ Stream.postscanl' g (xs, Nothing) s2
402+
403+-- | A more efficient 'deleteFirstsBy' for streams sorted in ascending order.
404+--
405+-- Both streams can be infinite.
296406--
297407-- Space: O(1)
298408--
299409-- /Unimplemented/
300−{-# INLINE deleteInStreamAscBy #-}
301−deleteInStreamAscBy :: -- (Monad m) =>
410+{-# INLINE sortedDeleteFirstsBy #-}
411+sortedDeleteFirstsBy :: -- (Monad m) =>
302412 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
303−deleteInStreamAscBy _eq _s1 _s2 = undefined
413+sortedDeleteFirstsBy _eq _s1 _s2 = undefined
304414
305415-- XXX Remove the MonadIO constraint. We can just cache one stream and then
306416-- implement using differenceEqBy.
307417
308−-- | This essentially appends to the second stream all the occurrences of
309−-- elements in the first stream that are not already present in the second
310−-- stream.
418+-- | Returns the first stream appended with those unique elements from the
419+-- second stream that are not already present in the first stream. Note that
420+-- this is not a commutative operation unlike a set union, argument order
421+-- matters. The behavior is similar to the 'Data.List.unionBy'.
311422--
312423-- Equivalent to the following except that @s2@ is evaluated only once:
313424--
314−-- >>> unionWithStreamGenericBy eq s1 s2 = s2 `Stream.append` (Stream.deleteInStreamGenericBy eq s2 s1)
425+-- >>> unionBy eq s1 s2 = s1 `Stream.append` Stream.deleteFirstsBy eq s1 (Stream.nub s2)
315426--
316427-- Example:
317428--
318−-- >>> Stream.fold Fold.toList $ Stream.unionWithStreamGenericBy (==) (Stream.fromList [1,1,2,3]) (Stream.fromList [1,2,2,4])
429+-- >>> f s1 s2 = Stream.fold Fold.toList $ Stream.unionBy (==) (Stream.fromList s1) (Stream.fromList s2)
430+-- >>> f [1,2,2,4] [1,1,2,3,3]
319431-- [1,2,2,4,3]
320432--
433+-- First stream can be infinite, but second stream must be finite. Note that if
434+-- the first stream is infinite the union means just the first stream. Thus
435+-- union is useful only when both streams are finite. See 'sortedUnionBy' where
436+-- union can work on infinite streams if they are sorted.
437+--
321438-- Space: O(n)
322439--
323440-- Time: O(m x n)
324441--
325442-- /Pre-release/
326−{-# INLINE unionWithStreamGenericBy #-}
327−unionWithStreamGenericBy :: MonadIO m =>
443+{-# INLINE unionBy #-}
444+unionBy :: MonadIO m =>
328445 (a -> a -> Bool) -> Stream m a -> Stream m a -> Stream m a
329−unionWithStreamGenericBy eq s1 s2 =
446+unionBy eq s2 s1 =
330447 Stream.concatEffect
331448 $ do
449+ -- XXX use a rewrite rule such that if a list converted to stream
450+ -- is passed to unionBy then this becomes an identity operation.
332451 xs <- Stream.fold Fold.toList s1
333452 -- XXX we can use postscanlMAfter' instead of IORef
334453 ref <- liftIO $ newIORef $! List.nubBy eq xs
@@ -341,12 +460,21 @@ unionWithStreamGenericBy eq s1 s2 =
341460 return $ Stream.fromList xs1
342461 return $ Stream.mapM f s2 `Stream.append` s3
343462
344−-- | A more efficient 'unionWithStreamGenericBy' for sorted streams.
463+-- | A more efficient 'unionBy' for sorted streams.
464+--
465+-- Note that the behavior is different from 'unionBy'. In 'unionBy' we append
466+-- the unique elements from second stream only after exhausting the first one
467+-- whereas in sorted streams we can determine unique elements early even when
468+-- we are going through the first stream. Thus the result is an interleaving of
469+-- the two streams, merging those elements from the second stream that are not
470+-- present in the first.
345471--
346472-- Space: O(1)
347473--
474+-- Both streams can be infinite.
475+--
348476-- /Unimplemented/
349−{-# INLINE unionWithStreamAscBy #-}
350−unionWithStreamAscBy :: -- (Monad m) =>
477+{-# INLINE sortedUnionBy #-}
478+sortedUnionBy :: -- (Monad m) =>
351479 (a -> a -> Ordering) -> Stream m a -> Stream m a -> Stream m a
352−unionWithStreamAscBy _eq _s1 _s2 = undefined
480+sortedUnionBy _eq _s1 _s2 = undefined
core/src/Streamly/Internal/Data/Stream/Transform.hs+52−26
@@ -21,8 +21,6 @@ module Streamly.Internal.Data.Stream.Transform
2121
2222 -- * Mapping
2323 -- | Stateless one-to-one maps.
24− , map
25− , mapM
2624 , sequence
2725
2826 -- * Mapping Effects
@@ -75,7 +73,7 @@ module Streamly.Internal.Data.Stream.Transform
7573 , scanMaybe
7674 , filter
7775 , filterM
78− , deleteBy
76+ , deleteBy -- deleteOnceBy?
7977 , uniqBy
8078 , uniq
8179 , prune
@@ -83,9 +81,8 @@ module Streamly.Internal.Data.Stream.Transform
8381
8482 -- * Trimming
8583 -- | Produce a subset of the stream trimmed at ends.
86− , take
87− , takeWhile
88− , takeWhileM
84+ , initNonEmpty
85+ , tailNonEmpty
8986 , takeWhileLast
9087 , takeWhileAround
9188 , drop
@@ -132,7 +129,6 @@ module Streamly.Internal.Data.Stream.Transform
132129 -- * Searching
133130 , findIndices
134131 , elemIndices
135− , slicesBy
136132
137133 -- * Rolling map
138134 -- | Map using the previous element.
@@ -1074,6 +1070,55 @@ repeated = undefined
10741070-- Trimming
10751071------------------------------------------------------------------------------
10761072
1073+-- | init for non-empty streams, fails for empty stream case.
1074+--
1075+{-# INLINE initNonEmpty #-}
1076+initNonEmpty :: Monad m => Stream m a -> Stream m a
1077+initNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1078+
1079+ where
1080+
1081+ step gst (Nothing, s1) = do
1082+ r <- step1 (adaptState gst) s1
1083+ return $
1084+ case r of
1085+ Yield x s -> Skip (Just x, s)
1086+ Skip s -> Skip (Nothing, s)
1087+ Stop -> error "initNonEmpty: empty Stream"
1088+
1089+ step gst (Just a, s1) = do
1090+ r <- step1 (adaptState gst) s1
1091+ return $
1092+ case r of
1093+ Yield x s -> Yield a (Just x, s)
1094+ Skip s -> Skip (Just a, s)
1095+ Stop -> Stop
1096+
1097+-- | tail for non-empty streams, fails for empty stream case.
1098+--
1099+-- See also 'tail' for a non-partial version of this function..
1100+{-# INLINE tailNonEmpty #-}
1101+tailNonEmpty :: Monad m => Stream m a -> Stream m a
1102+tailNonEmpty (Stream step1 state1) = Stream step (Nothing, state1)
1103+
1104+ where
1105+
1106+ step gst (Nothing, s1) = do
1107+ r <- step1 (adaptState gst) s1
1108+ return $
1109+ case r of
1110+ Yield x s -> Skip (Just x, s)
1111+ Skip s -> Skip (Nothing, s)
1112+ Stop -> error "tailNonEmpty: empty Stream"
1113+
1114+ step gst (Just a, s1) = do
1115+ r <- step1 (adaptState gst) s1
1116+ return $
1117+ case r of
1118+ Yield x s -> Yield x (Just x, s)
1119+ Skip s -> Skip (Just a, s)
1120+ Stop -> Stop
1121+
10771122-- | Take all consecutive elements at the end of the stream for which the
10781123-- predicate is true.
10791124--
@@ -1709,25 +1754,6 @@ findIndices p (Stream step state) = Stream step' (state, 0)
17091754elemIndices :: (Monad m, Eq a) => a -> Stream m a -> Stream m Int
17101755elemIndices a = findIndices (== a)
17111756
1712−{-# INLINE_NORMAL slicesBy #-}
1713−slicesBy :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
1714−slicesBy p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
1715−
1716− where
1717−
1718− {-# INLINE_LATE step #-}
1719− step gst (Just (st, i, len)) = i `seq` len `seq` do
1720− r <- step1 (adaptState gst) st
1721− return
1722− $ case r of
1723− Yield x s ->
1724− if p x
1725− then Yield (i, len + 1) (Just (s, i + len + 1, 0))
1726− else Skip (Just (s, i, len + 1))
1727− Skip s -> Skip (Just (s, i, len))
1728− Stop -> if len == 0 then Stop else Yield (i, len) Nothing
1729− step _ Nothing = return Stop
1730−
17311757------------------------------------------------------------------------------
17321758-- Rolling map
17331759------------------------------------------------------------------------------
core/src/Streamly/Internal/Data/Stream/Transformer.hs+2−1
@@ -11,10 +11,11 @@
1111
1212module Streamly.Internal.Data.Stream.Transformer
1313 (
14+ -- * Fold to Transformer Monad
1415 foldlT
1516 , foldrT
1617
17− -- * Transform Inner Monad
18+ -- * Inner Monad Operations
1819 , liftInner
1920 , runReaderT
2021 , usingReaderT
core/src/Streamly/Internal/Data/Stream/Type.hs+37−3
@@ -1,5 +1,11 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE PatternSynonyms #-}
3+{-# LANGUAGE TypeFamilies #-}
4+-- Must come after TypeFamilies, otherwise it is re-enabled.
5+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
6+{-# LANGUAGE NoMonoLocalBinds #-}
27{-# LANGUAGE UndecidableInstances #-}
8+{-# LANGUAGE ViewPatterns #-}
39
410-- |
511-- Module : Streamly.Internal.Data.Stream.Type
@@ -16,18 +22,18 @@
1622
1723module Streamly.Internal.Data.Stream.Type
1824 (
19− -- * The stream type
25+ -- * Type
2026 Step (..)
2127 -- XXX UnStream is exported to avoid a performance issue in some
2228 -- combinators if we use the pattern synonym "Stream".
2329 , Stream (Stream, UnStream)
2430
25− -- * CrossStream type wrapper
31+ -- * CrossStream
2632 , CrossStream
2733 , unCross
2834 , mkCross
2935
30− -- * Conversion to StreamK
36+ -- * To StreamK
3137 , fromStreamK
3238 , toStreamK
3339
@@ -85,6 +91,7 @@ module Streamly.Internal.Data.Stream.Type
8591
8692 -- * Combining Two Streams
8793 -- ** Zipping
94+ -- | Zip corresponding elements of two streams.
8895 , zipWithM
8996 , zipWith
9097
@@ -100,6 +107,8 @@ module Streamly.Internal.Data.Stream.Type
100107 , unfoldMany
101108
102109 -- * Concat
110+ -- | Generate streams by mapping a stream generator on each element of an
111+ -- input stream, append the resulting streams and flatten.
103112 , concatEffect
104113 , concatMap
105114 , concatMapM
@@ -133,6 +142,7 @@ module Streamly.Internal.Data.Stream.Type
133142 , indexOnSuffix
134143
135144 -- * Multi-stream folds
145+ -- | These should probably be expressed using zipping operations.
136146 , eqBy
137147 , cmpBy
138148
@@ -562,8 +572,12 @@ foldAdd f =
562572-- >>> Stream.foldrM step (return False) s
563573-- True
564574--
575+-- >>> import Control.Monad (join)
576+-- >>> foldrM f z = join $ Stream.foldr f z
577+--
565578{-# INLINE_NORMAL foldrM #-}
566579foldrM :: Monad m => (a -> m b -> m b) -> m b -> Stream m a -> m b
580+-- foldrM f z = join . Streamly.Internal.Data.Stream.StreamD.Type.foldr f z
567581foldrM f z (Stream step state) = go SPEC state
568582 where
569583 {-# INLINE_LATE go #-}
@@ -2062,6 +2076,26 @@ indexOnSuffix predicate =
20622076 (indexerBy (FL.takeEndBy_ predicate FL.length) 1)
20632077 (return (-1, 0))
20642078
2079+-- Alternate implementation
2080+{-# INLINE_NORMAL _indexOnSuffix #-}
2081+_indexOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
2082+_indexOnSuffix p (Stream step1 state1) = Stream step (Just (state1, 0, 0))
2083+
2084+ where
2085+
2086+ {-# INLINE_LATE step #-}
2087+ step gst (Just (st, i, len)) = i `seq` len `seq` do
2088+ r <- step1 (adaptState gst) st
2089+ return
2090+ $ case r of
2091+ Yield x s ->
2092+ if p x
2093+ then Yield (i, len + 1) (Just (s, i + len + 1, 0))
2094+ else Skip (Just (s, i, len + 1))
2095+ Skip s -> Skip (Just (s, i, len))
2096+ Stop -> if len == 0 then Stop else Yield (i, len) Nothing
2097+ step _ Nothing = return Stop
2098+
20652099{-# DEPRECATED sliceOnSuffix "Please use indexOnSuffix instead." #-}
20662100sliceOnSuffix :: Monad m => (a -> Bool) -> Stream m a -> Stream m (Int, Int)
20672101sliceOnSuffix = indexOnSuffix
core/src/Streamly/Internal/Data/StreamK.hs+19−8
@@ -16,16 +16,15 @@ module Streamly.Internal.Data.StreamK
1616 --
1717 -- $setup
1818
19− -- * The stream type
2019 module Streamly.Internal.Data.StreamK.Type
20+ -- * Transformer
2121 , module Streamly.Internal.Data.StreamK.Transformer
2222
23− , StreamK(..)
23+ -- * From containers
2424 , fromList
2525 , fromStream
26− , toStream
2726
28− -- ** Specialized Generation
27+ -- * Specialized Generation
2928 , repeatM
3029 , replicate
3130 , replicateM
@@ -67,13 +66,13 @@ module Streamly.Internal.Data.StreamK
6766 , find
6867 , (!!)
6968
69+ -- ** To Containers
70+ , toList
71+ , toStream
72+
7073 -- ** Map and Fold
7174 , mapM_
7275
73− -- ** Conversions
74− , toList
75− , hoist
76−
7776 -- * Transformation
7877 -- ** By folding (scans)
7978 , scanl'
@@ -100,6 +99,7 @@ module Streamly.Internal.Data.StreamK
10099
101100 -- ** Reordering
102101 , sortBy
102+ , sortOn
103103
104104 -- ** Map and Filter
105105 , mapMaybe
@@ -115,6 +115,9 @@ module Streamly.Internal.Data.StreamK
115115 -- ** Transformation comprehensions
116116 , the
117117
118+ -- ** Transforming Inner Monad
119+ , hoist
120+
118121 -- * Exceptions
119122 , handle
120123
@@ -131,6 +134,7 @@ import Control.Exception (mask_, Exception)
131134import Control.Monad (void, join)
132135import Control.Monad.Catch (MonadCatch)
133136import Control.Monad.IO.Class (MonadIO(..))
137+import Data.Ord (comparing)
134138import Data.Proxy (Proxy(..))
135139import GHC.Types (SPEC(..))
136140import Streamly.Internal.Data.Array.Type (Array(..))
@@ -1717,3 +1721,10 @@ sortBy cmp =
17171721 . Stream.catRights -- its a non-failing backtracking parser
17181722 . Stream.parseMany (fmap (either id id) p)
17191723 . Stream.fromStreamK
1724+
1725+{-# INLINE sortOn #-}
1726+sortOn :: (Monad m, Ord b) => (a -> b) -> StreamK m a -> StreamK m a
1727+sortOn f =
1728+ fmap snd
1729+ . sortBy (comparing fst)
1730+ . fmap (\x -> let y = f x in y `seq` (y, x))
core/src/Streamly/Internal/Data/StreamK/Type.hs+60−11
@@ -1,4 +1,8 @@
11{-# LANGUAGE CPP #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
26{-# LANGUAGE UndecidableInstances #-}
37-- |
48-- Module : Streamly.Internal.Data.StreamK.Type
@@ -93,8 +97,11 @@ module Streamly.Internal.Data.StreamK.Type
9397 -- ** Specific Folds
9498 , drain
9599 , null
100+ , headNonEmpty
96101 , tail
102+ , tailNonEmpty
97103 , init
104+ , initNonEmpty
98105
99106 -- * Mapping
100107 , map
@@ -1807,14 +1814,42 @@ iterateMWith cns step = go
18071814 !next <- s
18081815 foldStreamShared st stp sng yld (return next `cns` go (step next))
18091816
1810−{-# INLINE headPartial #-}
1811−headPartial :: Monad m => StreamK m a -> m a
1812−headPartial = foldrM (\x _ -> return x) (error "head of nil")
1817+-- | head for non-empty streams, fails for empty stream case.
1818+--
1819+{-# INLINE headNonEmpty #-}
1820+headNonEmpty :: Monad m => StreamK m a -> m a
1821+headNonEmpty = foldrM (\x _ -> return x) (error "headNonEmpty: empty stream")
1822+
1823+-- | init for non-empty streams, fails for empty stream case.
1824+--
1825+-- See also 'init' for a non-partial version of this function..
1826+{-# INLINE initNonEmpty #-}
1827+initNonEmpty :: Stream m a -> Stream m a
1828+initNonEmpty = go0
1829+
1830+ where
1831+
1832+ go0 m = mkStream $ \st yld sng stp ->
1833+ let stop = error "initNonEmpty: Empty Stream."
1834+ single _ = stp
1835+ yieldk a r = foldStream st yld sng stp (go1 a r)
1836+ in foldStream st yieldk single stop m
1837+
1838+ go1 a r = mkStream $ \st yld sng stp ->
1839+ let stop = stp
1840+ single _ = sng a
1841+ yieldk a1 r1 = yld a (go1 a1 r1)
1842+ in foldStream st yieldk single stop r
18131843
1814−{-# INLINE tailPartial #-}
1815−tailPartial :: StreamK m a -> StreamK m a
1816−tailPartial m = mkStream $ \st yld sng stp ->
1817− let stop = error "tail of nil"
1844+-- | tail for non-empty streams, fails for empty stream case.
1845+--
1846+-- See also 'tail' for a non-partial version of this function..
1847+--
1848+-- Note: this is same as "drop 1" with error on empty stream.
1849+{-# INLINE tailNonEmpty #-}
1850+tailNonEmpty :: StreamK m a -> StreamK m a
1851+tailNonEmpty m = mkStream $ \st yld sng stp ->
1852+ let stop = error "tailNonEmpty: empty stream"
18181853 single _ = stp
18191854 yieldk _ r = foldStream st yld sng stp r
18201855 in foldStream st yieldk single stop m
@@ -1879,10 +1914,10 @@ mfix f = mkStream $ \st yld sng stp ->
18791914 where
18801915
18811916 -- fix the head element of the stream
1882− xs = fix (f . headPartial)
1917+ xs = fix (f . headNonEmpty)
18831918
18841919 -- now fix the tail recursively
1885− ys = mfix (tailPartial . f)
1920+ ys = mfix (tailNonEmpty . f)
18861921
18871922-------------------------------------------------------------------------------
18881923-- Conversions
@@ -1913,6 +1948,15 @@ uncons m =
19131948 yieldk a r = pure (Just (a, r))
19141949 in foldStream defState yieldk single stop m
19151950
1951+-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1952+-- empty stream case without making this a partial function.
1953+--
1954+-- See tailNonEmpty as well above.
1955+
1956+-- | Same as:
1957+--
1958+-- >>> tail = fmap (fmap snd) StreamK.uncons
1959+--
19161960{-# INLINE tail #-}
19171961tail :: Applicative m => StreamK m a -> m (Maybe (StreamK m a))
19181962tail =
@@ -1921,9 +1965,14 @@ tail =
19211965 yieldk _ r = pure $ Just r
19221966 in foldStream defState yieldk single stop
19231967
1924−-- | Extract all but the last element of the stream, if any.
1968+-- Note that this is not a StreamK -> StreamK because then we cannot handle the
1969+-- empty stream case without making this a partial function.
19251970--
1926−-- Note: This will end up buffering the entire stream.
1971+-- XXX How do we implement unsnoc? Make StreamK a monad and return the
1972+-- remaining stream as a result value in the monad?
1973+
1974+-- | Extract all but the last element of the stream, if any. This will end up
1975+-- evaluating the last element as well to find out that it is last.
19271976--
19281977-- /Pre-release/
19291978{-# INLINE init #-}
core/src/Streamly/Internal/Data/Time/Units.hs−4
@@ -1,7 +1,3 @@
1−{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE PolyKinds #-}
3−{-# LANGUAGE UnboxedTuples #-}
4−
51-- |
62-- Module : Streamly.Internal.Data.Time.Units
73-- Copyright : (c) 2019 Composewell Technologies
core/src/Streamly/Internal/Data/Unbox.hs+4−1
@@ -1,5 +1,8 @@
11{-# LANGUAGE DataKinds #-}
2−{-# LANGUAGE DefaultSignatures #-}
2+{-# LANGUAGE TypeFamilies #-}
3+-- Must come after TypeFamilies, otherwise it is re-enabled.
4+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
5+{-# LANGUAGE NoMonoLocalBinds #-}
36{-# LANGUAGE UnboxedTuples #-}
47{-# LANGUAGE UndecidableInstances #-}
58
core/src/Streamly/Internal/Data/Unfold.hs+6−4
@@ -82,7 +82,7 @@ module Streamly.Internal.Data.Unfold
8282 , dropWhileM
8383
8484 -- ** Cross product
85− , joinInnerGeneric
85+ , innerJoin
8686
8787 -- ** Resource Management
8888 -- | 'bracket' is the most general resource management operation, all other
@@ -677,10 +677,12 @@ dropWhileM f (Unfold step inject) = Unfold step' inject'
677677dropWhile :: Monad m => (b -> Bool) -> Unfold m a b -> Unfold m a b
678678dropWhile f = dropWhileM (return . f)
679679
680−{-# INLINE_NORMAL joinInnerGeneric #-}
681−joinInnerGeneric :: Monad m =>
680+-- | Cross intersection of two unfolds. See
681+-- 'Streamly.Internal.Data.Stream.innerJoin' for more details.
682+{-# INLINE_NORMAL innerJoin #-}
683+innerJoin :: Monad m =>
682684 (b -> c -> Bool) -> Unfold m a b -> Unfold m a c -> Unfold m a (b, c)
683−joinInnerGeneric eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
685+innerJoin eq s1 s2 = filter (\(a, b) -> a `eq` b) $ cross s1 s2
684686
685687------------------------------------------------------------------------------
686688-- Exceptions
core/src/Streamly/Internal/FileSystem/File.hs+2−2
@@ -103,7 +103,7 @@ import qualified Control.Monad.Catch as MC
103103import qualified System.IO as SIO
104104
105105import Streamly.Data.Fold (groupsOf, drain)
106−import Streamly.Internal.Data.Array.Type (Array(..), pinnedWriteNUnsafe)
106+import Streamly.Internal.Data.Array.Type (Array(..))
107107import Streamly.Internal.Data.Fold.Type (Fold(..))
108108import Streamly.Data.Stream (Stream)
109109import Streamly.Internal.Data.Unfold.Type (Unfold(..))
@@ -462,7 +462,7 @@ writeChunks path = Fold step initial extract final
462462writeWith :: (MonadIO m, MonadCatch m)
463463 => Int -> FilePath -> Fold m Word8 ()
464464writeWith n path =
465− groupsOf n (pinnedWriteNUnsafe n) (writeChunks path)
465+ groupsOf n (A.unsafePinnedCreateOf n) (writeChunks path)
466466
467467{-# DEPRECATED writeWithBufferOf "Please use 'writeWith' instead" #-}
468468{-# INLINE writeWithBufferOf #-}
core/src/Streamly/Internal/FileSystem/Handle.hs+14−15
@@ -40,8 +40,8 @@ module Streamly.Internal.FileSystem.Handle
4040
4141 -- * Streams
4242 , read
43− , readWith
44− , readChunksWith
43+ , readWith -- readConcatChunksOf
44+ , readChunksWith -- readChunksOf
4545 , readChunks
4646
4747 -- * Unfolds
@@ -59,10 +59,10 @@ module Streamly.Internal.FileSystem.Handle
5959 -- , writeUtf8ByLines
6060 -- , writeByFrames
6161 -- , writeLines
62− , writeWith
62+ , writeWith -- writeChunksOf
6363 , writeChunks
64− , writeChunksWith
65− , writeMaybesWith
64+ , writeChunksWith -- writeCompactChunksOf
65+ , writeMaybesWith -- writeCompactJustsOf
6666
6767 -- * Refolds
6868 , writer
@@ -131,14 +131,13 @@ import Streamly.Internal.Data.Fold (Fold)
131131import Streamly.Internal.Data.Refold.Type (Refold(..))
132132import Streamly.Internal.Data.Unfold.Type (Unfold(..))
133133import Streamly.Internal.Data.Array.Type
134− (Array(..), pinnedWriteNUnsafe, unsafeFreezeWithShrink, byteLength)
134+ (Array(..), unsafeFreezeWithShrink, byteLength)
135135import Streamly.Internal.Data.Stream.Type (Stream)
136136-- import Streamly.String (encodeUtf8, decodeUtf8, foldLines)
137137import Streamly.Internal.System.IO (defaultChunkSize)
138138
139139import qualified Streamly.Data.Fold as FL
140−import qualified Streamly.Data.Array as A
141−import qualified Streamly.Internal.Data.Array.Type as A
140+import qualified Streamly.Internal.Data.Array as A
142141import qualified Streamly.Internal.Data.MutArray.Type as MArray
143142import qualified Streamly.Internal.Data.Refold.Type as Refold
144143import qualified Streamly.Internal.Data.Fold.Type as FL(refoldMany)
@@ -180,7 +179,7 @@ getChunk :: MonadIO m => Int -> Handle -> m (Array Word8)
180179getChunk size h = liftIO $ do
181180 arr :: MArray.MutArray Word8 <- MArray.pinnedEmptyOf size
182181 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
183− MArray.asPtrUnsafe arr $ \p -> do
182+ MArray.unsafePinnedAsPtr arr $ \p -> do
184183 n <- hGetBufSome h p size
185184 -- XXX shrink only if the diff is significant
186185 return $
@@ -380,12 +379,12 @@ read = A.concat . readChunks
380379{-# INLINABLE putChunk #-}
381380putChunk :: MonadIO m => Handle -> Array a -> m ()
382381putChunk _ arr | byteLength arr == 0 = return ()
383−putChunk h arr = A.asPtrUnsafe arr $ \ptr ->
382+putChunk h arr = A.unsafePinnedAsPtr arr $ \ptr ->
384383 liftIO $ hPutBuf h ptr aLen
385384
386385 where
387386
388− -- XXX We should have the length passed by asPtrUnsafe itself.
387+ -- XXX We should have the length passed by unsafePinnedAsPtr itself.
389388 aLen = A.byteLength arr
390389
391390-------------------------------------------------------------------------------
@@ -490,11 +489,11 @@ writeChunksWithBufferOf = writeChunksWith
490489-- Bytes in the input stream are collected into a buffer until we have a chunk
491490-- of @reqSize@ and then written to the IO device.
492491--
493−-- >>> writeWith n h = Fold.groupsOf n (Array.writeNUnsafe n) (Handle.writeChunks h)
492+-- >>> writeWith n h = Fold.groupsOf n (Array.unsafeCreateOf n) (Handle.writeChunks h)
494493--
495494{-# INLINE writeWith #-}
496495writeWith :: MonadIO m => Int -> Handle -> Fold m Word8 ()
497−writeWith n h = FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks h)
496+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
498497
499498-- | Same as 'writeWith'
500499--
@@ -512,7 +511,7 @@ writeWithBufferOf = writeWith
512511writeMaybesWith :: (MonadIO m )
513512 => Int -> Handle -> Fold m (Maybe Word8) ()
514513writeMaybesWith n h =
515− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
514+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
516515 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
517516 in FL.many writeOnNothing (writeChunks h)
518517
@@ -522,7 +521,7 @@ writeMaybesWith n h =
522521{-# INLINE writerWith #-}
523522writerWith :: MonadIO m => Int -> Refold m Handle Word8 ()
524523writerWith n =
525− FL.refoldMany (FL.take n $ pinnedWriteNUnsafe n) chunkWriter
524+ FL.refoldMany (FL.take n $ A.unsafePinnedCreateOf n) chunkWriter
526525
527526-- | Write a byte stream to a file handle. Accumulates the input in chunks of
528527-- up to 'Streamly.Internal.Data.Array.Type.defaultChunkSize' before writing
core/src/Streamly/Internal/Unicode/Stream.hs+1−1
@@ -1057,7 +1057,7 @@ stripHead = Stream.dropWhile isSpace
10571057-- >>> Stream.fold Fold.toList $ Unicode.lines Fold.toList (Stream.fromList "lines\nthis\nstring\n\n\n")
10581058-- ["lines","this","string","",""]
10591059--
1060−-- > lines = Stream.splitOnSuffix (== '\n')
1060+-- >>> lines f = Stream.foldMany (Fold.takeEndBy_ (== '\n') f)
10611061--
10621062-- /Pre-release/
10631063{-# INLINE lines #-}
core/src/Streamly/Internal/Unicode/String.hs+7−7
@@ -39,14 +39,14 @@ module Streamly.Internal.Unicode.String
3939 --
4040 -- $setup
4141
42− str
42+ str
4343 ) where
4444
4545
4646import Control.Applicative (Alternative(..))
4747import Control.Exception (displayException)
4848import Data.Functor.Identity (runIdentity)
49−import Streamly.Internal.Data.Parser (Parser)
49+import Streamly.Internal.Data.Parser (Parser, ParseError)
5050
5151import Language.Haskell.TH
5252import Language.Haskell.TH.Quote
@@ -106,9 +106,12 @@ strSegmentExp (StrVar name) = do
106106strExp :: [StrSegment] -> Q Exp
107107strExp xs = appE [| concat |] $ listE $ map strSegmentExp xs
108108
109+parseStr :: String -> Either ParseError [StrSegment]
110+parseStr = runIdentity . Stream.parse strParser . Stream.fromList
111+
109112expandVars :: String -> Q Exp
110−expandVars ln =
111− case runIdentity $ Stream.parse strParser (Stream.fromList ln) of
113+expandVars input =
114+ case parseStr input of
112115 Left e ->
113116 fail $ "str QuasiQuoter parse error: " ++ displayException e
114117 Right x ->
@@ -141,9 +144,6 @@ expandVars ln =
141144-- :}
142145-- "hello world!"
143146--
144−-- Bugs: because of a bug in parsers, a lone # at the end of input gets
145−-- removed.
146−--
147147str :: QuasiQuoter
148148str =
149149 QuasiQuoter
core/streamly-core.cabal+13−9
@@ -116,6 +116,7 @@ extra-doc-files:
116116 docs/*.md
117117 docs/ApiChangelogs/0.1.0.txt
118118 docs/ApiChangelogs/0.1.0-0.2.0.txt
119+ docs/ApiChangelogs/0.2.0-0.2.2.txt
119120
120121source-repository head
121122 type: git
@@ -217,8 +218,6 @@ common compile-options
217218common default-extensions
218219 default-extensions:
219220 BangPatterns
220− CApiFFI
221− CPP
222221 ConstraintKinds
223222 DeriveDataTypeable
224223 DeriveGeneric
@@ -229,24 +228,29 @@ common default-extensions
229228 GeneralizedNewtypeDeriving
230229 InstanceSigs
231230 KindSignatures
232− LambdaCase
233− MagicHash
234231 MultiParamTypeClasses
235− PatternSynonyms
236232 RankNTypes
237− RecordWildCards
238233 ScopedTypeVariables
239234 StandaloneDeriving
240235 TupleSections
241236 TypeApplications
242− TypeFamilies
243237 TypeOperators
244− ViewPatterns
245238
239+ -- Not GHC2021
240+ CApiFFI
241+ CPP
242+ DefaultSignatures
243+ LambdaCase
244+ MagicHash
245+ RecordWildCards
246+
247+ -- TypeFamilies is required by IsList, IsMap type classes and
248+ -- Unbox generic deriving code.
249+ -- TypeFamilies
246250 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
247251 -- regressions. Disable it. This must come after TypeFamilies,
248252 -- otherwise TypeFamilies will enable it again.
249− NoMonoLocalBinds
253+ -- NoMonoLocalBinds
250254
251255 -- UndecidableInstances -- Does not show any perf impact
252256 -- UnboxedTuples -- interferes with (#.)
docs/Developer/optimization-guidelines.md+12
@@ -191,6 +191,18 @@ the stages of a loop, it is often not useful to inline the whole loop
191191itself, in fact we may have to occasionally use a `NOINLINE` so that the
192192compiler does not inline it.
193193
194+We need to be careful about NOINLINE on polymorphic functions. When
195+NOINLINE is used, the function cannot be specialized causing huge
196+performance degradation. To avoid that we can either make the
197+function monomorphic or sometimes using `NOINLINE [0]` can do the
198+trick. `NOINLINE [0]` blocks inlining in earlier phases but lets it
199+specialize in the last phase, inlining in the last phase is left to
200+the compiler. `INLINE [0]` may sound the same as `NOINLINE [0]`,
201+however, it behaves differently because we ask the compiler to INLINE
202+it compulsorily and it may not give us the desired results. The
203+Data.Fold.writeLastN benchmark is one such case where `NOINLINE [0]`
204+provides much better performance than `INLINE [0]`.
205+
194206### NoSpecConstr
195207
196208It is not always useful to specialize function calls for all constructors, some
docs/User/Project/README.md+1
@@ -1,6 +1,7 @@
11# [Streamly][]: Idiomatic Haskell with C-Like Performance
22
33[![Gitter chat](https://badges.gitter.im/composewell/gitter.svg)](https://gitter.im/composewell/streamly)
4+[![Hackage](https://img.shields.io/hackage/v/streamly-core.svg?style=flat)](https://hackage.haskell.org/package/streamly-core)
45[![Hackage](https://img.shields.io/hackage/v/streamly.svg?style=flat)](https://hackage.haskell.org/package/streamly)
56
67## Upgrading to 0.9.0+
src/Streamly/Internal/Data/IsMap/HashMap.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-orphans #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/Expand.hs+5
@@ -1,3 +1,8 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
5+
16{-# OPTIONS_GHC -Wno-deprecations #-}
27
38-- |
src/Streamly/Internal/Data/Stream/IsStream/Top.hs+1−1
@@ -649,7 +649,7 @@ intersectBySorted :: (IsStream t, Monad m) =>
649649 (a -> a -> Ordering) -> t m a -> t m a -> t m a
650650intersectBySorted eq s1 =
651651 IsStream.fromStreamD
652− . StreamD.intersectBySorted eq (IsStream.toStreamD s1)
652+ . StreamD.sortedIntersectBy eq (IsStream.toStreamD s1)
653653 . IsStream.toStreamD
654654
655655-- Roughly joinLeft s1 s2 = s1 `difference` s2 + s1 `intersection` s2
src/Streamly/Internal/Data/Stream/Serial.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38{-# OPTIONS_GHC -Wno-redundant-constraints #-}
49
src/Streamly/Internal/Data/Stream/Zip.hs+5
@@ -1,4 +1,9 @@
1+{-# LANGUAGE TypeFamilies #-}
2+-- Must come after TypeFamilies, otherwise it is re-enabled.
3+-- MonoLocalBinds enabled by TypeFamilies causes perf regressions in general.
4+{-# LANGUAGE NoMonoLocalBinds #-}
15{-# LANGUAGE UndecidableInstances #-}
6+
27{-# OPTIONS_GHC -Wno-deprecations #-}
38
49-- |
src/Streamly/Internal/FileSystem/Event.hs+3
@@ -20,6 +20,9 @@
2020-- * "Streamly.Internal.FileSystem.Event.Linux"
2121-- * "Streamly.Internal.FileSystem.Event.Windows"
2222
23+-- XXX Need to ensure that the signatures of the exported APIs are same on all
24+-- platforms.
25+
2326module Streamly.Internal.FileSystem.Event
2427 (
2528 -- * Creating a Watch
src/Streamly/Internal/FileSystem/Event/Darwin.hs+1−1
@@ -451,7 +451,7 @@ openWatch Config{..} paths = do
451451
452452 withPathName :: Array Word8 -> (PathName -> IO a) -> IO a
453453 withPathName arr act = do
454− A.asPtrUnsafe arr $ \ptr ->
454+ A.unsafePinnedAsPtr arr $ \ptr ->
455455 let pname = PathName (castPtr ptr) (fromIntegral (A.length arr))
456456 in act pname
457457
src/Streamly/Internal/FileSystem/Event/Linux.hs+2−2
@@ -188,7 +188,7 @@ import qualified Streamly.FileSystem.Handle as FH
188188import qualified Streamly.Unicode.Stream as U
189189
190190import qualified Streamly.Internal.Data.Array as A
191− ( fromStream, asCStringUnsafe, asPtrUnsafe
191+ ( fromStream, asCStringUnsafe, unsafePinnedAsPtr
192192 , getSliceUnsafe, read
193193 )
194194import qualified Streamly.Internal.FileSystem.Dir as Dir (readDirs)
@@ -809,7 +809,7 @@ readOneEvent :: Config -> Watch -> Parser Word8 IO Event
809809readOneEvent cfg wt@(Watch _ wdMap) = do
810810 let headerLen = sizeOf (undefined :: CInt) + 12
811811 arr <- PR.takeEQ headerLen (A.writeN headerLen)
812− (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.asPtrUnsafe arr readHeader
812+ (ewd, eflags, cookie, pathLen) <- PR.fromEffect $ A.unsafePinnedAsPtr arr readHeader
813813 -- XXX need the "initial" in parsers to return a step type so that "take 0"
814814 -- can return without an input. otherwise if pathLen is 0 we will keep
815815 -- waiting to read one more char before we return this event.
src/Streamly/Internal/FileSystem/FD.hs+5−5
@@ -130,7 +130,7 @@ import qualified GHC.IO.Device as RawIO
130130import Streamly.Data.Array (Array, Unbox)
131131import Streamly.Data.Stream (Stream)
132132
133−import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, asPtrUnsafe)
133+import Streamly.Internal.Data.Array (byteLength, unsafeFreeze, unsafePinnedAsPtr)
134134import Streamly.Internal.System.IO (defaultChunkSize)
135135
136136#if !defined(mingw32_HOST_OS)
@@ -147,7 +147,7 @@ import qualified Streamly.Internal.System.IOVec.Type as RawIO
147147import qualified Streamly.Data.Array as A
148148import qualified Streamly.Data.Fold as FL
149149import qualified Streamly.Internal.Data.MutArray as MArray
150− (MutArray(..), asPtrUnsafe, pinnedNewBytes)
150+ (MutArray(..), unsafePinnedAsPtr, pinnedNewBytes)
151151import qualified Streamly.Internal.Data.Array.Stream as AS
152152import qualified Streamly.Internal.Data.Stream as S
153153import qualified Streamly.Internal.Data.Stream as D
@@ -220,7 +220,7 @@ readArrayUpto :: Int -> Handle -> IO (Array Word8)
220220readArrayUpto size (Handle fd) = do
221221 arr <- MArray.pinnedNewBytes size
222222 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
223− MArray.asPtrUnsafe arr $ \p -> do
223+ MArray.unsafePinnedAsPtr arr $ \p -> do
224224 -- n <- hGetBufSome h p size
225225#if MIN_VERSION_base(4,15,0)
226226 n <- RawIO.read fd p 0 size
@@ -244,7 +244,7 @@ readArrayUpto size (Handle fd) = do
244244writeArray :: Unbox a => Handle -> Array a -> IO ()
245245writeArray _ arr | A.length arr == 0 = return ()
246246writeArray (Handle fd) arr =
247− asPtrUnsafe arr $ \p ->
247+ unsafePinnedAsPtr arr $ \p ->
248248 -- RawIO.writeAll fd (castPtr p) aLen
249249#if MIN_VERSION_base(4,15,0)
250250 RawIO.write fd (castPtr p) 0 aLen
@@ -270,7 +270,7 @@ writeArray (Handle fd) arr =
270270writeIOVec :: Handle -> Array RawIO.IOVec -> IO ()
271271writeIOVec _ iov | A.length iov == 0 = return ()
272272writeIOVec (Handle fd) iov =
273− asPtrUnsafe iov $ \p ->
273+ unsafePinnedAsPtr iov $ \p ->
274274 RawIO.writevAll fd p (A.length iov)
275275-}
276276#endif
src/Streamly/Internal/Network/Inet/TCP.hs+3−3
@@ -116,7 +116,6 @@ import Prelude hiding (read)
116116import Streamly.Internal.Control.Concurrent (MonadAsync)
117117import Streamly.Internal.Control.ForkLifted (fork)
118118import Streamly.Data.Array (Array)
119−import Streamly.Internal.Data.Array (pinnedWriteNUnsafe)
120119import Streamly.Internal.Data.Fold ( Fold(..) )
121120import Streamly.Data.Stream (Stream)
122121import Streamly.Internal.Data.Tuple.Strict (Tuple'(..))
@@ -132,7 +131,8 @@ import qualified Streamly.Data.Array as A
132131import qualified Streamly.Data.Fold as FL
133132import qualified Streamly.Data.Stream as S
134133import qualified Streamly.Data.Unfold as UF
135−import qualified Streamly.Internal.Data.Array as A (pinnedChunksOf)
134+import qualified Streamly.Internal.Data.Array as A
135+ (pinnedChunksOf, unsafePinnedCreateOf)
136136import qualified Streamly.Internal.Data.Unfold as UF (bracketIO)
137137import qualified Streamly.Internal.Data.Fold as FL (Step(..), reduce)
138138
@@ -435,7 +435,7 @@ writeWithBufferOf
435435 -> PortNumber
436436 -> Fold m Word8 ()
437437writeWithBufferOf n addr port =
438− FL.groupsOf n (pinnedWriteNUnsafe n) (writeChunks addr port)
438+ FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks addr port)
439439
440440-- | Write a stream to the supplied IPv4 host address and port number.
441441--
src/Streamly/Internal/Network/Socket.hs+7−7
@@ -98,10 +98,10 @@ import qualified Streamly.Data.Fold as FL
9898import qualified Streamly.Data.Stream as S
9999import qualified Streamly.Data.Unfold as UF
100100import qualified Streamly.Internal.Data.Array as A
101− ( unsafeFreeze, asPtrUnsafe, byteLength, pinnedChunksOf,
102− pinnedWriteN, pinnedWriteNUnsafe, lCompactGE )
101+ ( unsafeFreeze, unsafePinnedAsPtr, byteLength, pinnedChunksOf,
102+ pinnedCreateOf, unsafePinnedCreateOf, lCompactGE )
103103import qualified Streamly.Internal.Data.MutArray as MArray
104− (MutArray(..), asPtrUnsafe, pinnedEmptyOf)
104+ (MutArray(..), unsafePinnedAsPtr, pinnedEmptyOf)
105105import qualified Streamly.Internal.Data.Stream as S (fromStreamK, Stream(..), Step(..))
106106import qualified Streamly.Internal.Data.StreamK as K (mkStream)
107107
@@ -263,7 +263,7 @@ readArrayUptoWith
263263readArrayUptoWith f size h = do
264264 arr <- MArray.pinnedEmptyOf size
265265 -- ptr <- mallocPlainForeignPtrAlignedBytes size (alignment (undefined :: Word8))
266− MArray.asPtrUnsafe arr $ \p -> do
266+ MArray.unsafePinnedAsPtr arr $ \p -> do
267267 n <- f h p size
268268 let v = A.unsafeFreeze
269269 $ arr { MArray.arrEnd = n, MArray.arrBound = size }
@@ -311,7 +311,7 @@ writeArrayWith :: Unbox a
311311 -> Array a
312312 -> IO ()
313313writeArrayWith _ _ arr | A.length arr == 0 = return ()
314−writeArrayWith f h arr = A.asPtrUnsafe arr $ \ptr -> f h (castPtr ptr) aLen
314+writeArrayWith f h arr = A.unsafePinnedAsPtr arr $ \ptr -> f h (castPtr ptr) aLen
315315
316316 where
317317
@@ -505,7 +505,7 @@ putBytesWith n h m = putChunks h $ A.pinnedChunksOf n m
505505--
506506{-# INLINE writeWith #-}
507507writeWith :: MonadIO m => Int -> Socket -> Fold m Word8 ()
508−writeWith n h = FL.groupsOf n (A.pinnedWriteNUnsafe n) (writeChunks h)
508+writeWith n h = FL.groupsOf n (A.unsafePinnedCreateOf n) (writeChunks h)
509509
510510-- | Same as 'writeWith'
511511--
@@ -523,7 +523,7 @@ writeWithBufferOf = writeWith
523523writeMaybesWith :: (MonadIO m )
524524 => Int -> Socket -> Fold m (Maybe Word8) ()
525525writeMaybesWith n h =
526− let writeNJusts = FL.lmap fromJust $ A.pinnedWriteN n
526+ let writeNJusts = FL.lmap fromJust $ A.pinnedCreateOf n
527527 writeOnNothing = FL.takeEndBy_ isNothing writeNJusts
528528 in FL.many writeOnNothing (writeChunks h)
529529
streamly.cabal+10−10
@@ -294,8 +294,6 @@ common compile-options
294294common default-extensions
295295 default-extensions:
296296 BangPatterns
297− CApiFFI
298− CPP
299297 ConstraintKinds
300298 DeriveDataTypeable
301299 DeriveGeneric
@@ -306,23 +304,25 @@ common default-extensions
306304 GeneralizedNewtypeDeriving
307305 InstanceSigs
308306 KindSignatures
309− LambdaCase
310− MagicHash
311307 MultiParamTypeClasses
312− PatternSynonyms
313308 RankNTypes
314− RecordWildCards
315309 ScopedTypeVariables
316310 TupleSections
317311 TypeApplications
318− TypeFamilies
319312 TypeOperators
320− ViewPatterns
321313
314+ -- Not GHC2021
315+ CApiFFI
316+ CPP
317+ LambdaCase
318+ MagicHash
319+ RecordWildCards
320+
321+ -- TypeFamilies -- required by IsHashMap type class
322322 -- MonoLocalBinds, enabled by TypeFamilies, causes performance
323323 -- regressions. Disable it. This must come after TypeFamilies,
324324 -- otherwise TypeFamilies will enable it again.
325− NoMonoLocalBinds
325+ -- NoMonoLocalBinds
326326
327327 -- UndecidableInstances -- Does not show any perf impact
328328 -- UnboxedTuples -- interferes with (#.)
@@ -513,7 +513,7 @@ library
513513 , template-haskell >= 2.14 && < 2.22
514514
515515 -- The core streamly package
516− , streamly-core >= 0.2.0 && < 0.2.2
516+ , streamly-core == 0.2.2
517517
518518 , hashable >= 1.3 && < 1.5
519519 , unordered-containers >= 0.2 && < 0.3
test/Streamly/Test/Data/Array.hs+2−2
@@ -185,7 +185,7 @@ testUnsafeIndxedFromList inp =
185185testAsPtrUnsafeMA :: IO ()
186186testAsPtrUnsafeMA = do
187187 arr <- MA.fromList ([0 .. 99] :: [Int])
188− MA.asPtrUnsafe arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
188+ MA.unsafePinnedAsPtr arr (getList (0 :: Int)) `shouldReturn` [0 .. 99]
189189
190190 where
191191
@@ -229,7 +229,7 @@ main =
229229 prop "read . write === id" testFoldUnfold
230230 prop "fromList" testFromList
231231 prop "foldMany with writeNUnsafe concats to original"
232− (foldManyWith (\n -> Fold.take n (A.writeNUnsafe n)))
232+ (foldManyWith (\n -> Fold.take n (A.unsafeCreateOf n)))
233233 describe "unsafeSlice" $ do
234234 it "partial" $ unsafeSlice 2 4 [1..10]
235235 it "none" $ unsafeSlice 10 0 [1..10]