on master help sign in
update doc, hide some combinators, rename split combinators
fdec798Harendra Kumar
src/Streamly/Fold.hs+180−147
@@ -89,11 +89,11 @@ module Streamly.Fold
89 -- applications, they buffer all the input in GC memory which can be
89 -- applications, they buffer all the input in GC memory which can be
90 -- detrimental to performance if the input is large.
90 -- detrimental to performance if the input is large.
91
91
92− , toStream -- experimental
92+ -- , toStream -- experimental
93− , toStreamRev -- experimental
93+ -- , toStreamRev -- experimental
94
94
95 , toList
95 , toList
96− , toListRev -- experimental
96+ -- , toListRev -- experimental
97
97
98 -- ** Partial Folds
98 -- ** Partial Folds
99 -- , drainN
99 -- , drainN
@@ -158,9 +158,9 @@ module Streamly.Fold
158 -- , lprescanl'
158 -- , lprescanl'
159 -- , lprescanlM'
159 -- , lprescanlM'
160
160
161− -- ** Filtering
161+ -- -- ** Filtering
162− , lfilter
162+ -- , lfilter
163− , lfilterM
163+ -- , lfilterM
164 -- , ldeleteBy
164 -- , ldeleteBy
165 -- , luniq
165 -- , luniq
166
166
@@ -198,17 +198,17 @@ module Streamly.Fold
198 -- ** Breaking
198 -- ** Breaking
199
199
200 -- By chunks
200 -- By chunks
201− , splitAt
201+ -- , splitAt -- spanN
202− -- , splitIn -- time
202+ -- , splitIn -- sessionN
203
203
204 -- By elements
204 -- By elements
205− , span
205+ -- , span -- spanWhile
206− , break -- breakPre
206+ -- , break -- breakBefore
207− -- , breakPost
207+ -- , breakAfter
208 -- , breakOn
208 -- , breakOn
209 -- , breakAround
209 -- , breakAround
210 -- , spanBy
210 -- , spanBy
211− -- , spanRollingBy
211+ -- , spanByRolling
212
212
213 -- By sequences
213 -- By sequences
214 -- breakOnSeq
214 -- breakOnSeq
@@ -225,40 +225,45 @@ module Streamly.Fold
225
225
226 -- , groupScan
226 -- , groupScan
227
227
228− -- *** By Chunks
228+ -- *** Chunks
229 , chunksOf
229 , chunksOf
230 , sessionsOf
230 , sessionsOf
231
231
232 -- , lchunksOf
232 -- , lchunksOf
233 -- , lsessionsOf
233 -- , lsessionsOf
234
234
235− -- *** By Elements
235+ -- *** Using Element Separators
236− , splitBy
236+ -- On == Dropping the separator
237− , splitSuffixBy
238− , splitSuffixBy'
239− -- , splitPrefixBy
240− , wordsBy
241−
242− -- *** By Sequences
243 , splitOn
237 , splitOn
244− , splitSuffixOn
238+ , splitOnSuffix
245− -- , splitPrefixOn
239+ -- , splitOnPrefix
246− -- , wordsOn
240+
241+ -- By == Keeping the separator
242+ -- , splitBy
243+ -- , splitBySuffix
244+ -- , splitByPrefix
245+ , wordsBy -- stripAndCompactBy
246+
247+ -- -- *** Using Sequence Separators
248+ -- , splitOnSeq
249+ -- , splitOnSuffixSeq
250+ -- , splitOnPrefixSeq
247
251
248 -- Keeping the delimiters
252 -- Keeping the delimiters
249− , splitOn'
253+ -- , splitBySeq
250− , splitSuffixOn'
254+ -- , splitBySeqSuffix
251− -- , splitPrefixOn'
255+ -- , splitBySeqPrefix
256+ -- , wordsBySeq
252
257
253− -- Splitting by multiple sequences
258+ -- Splitting using multiple sequence separators
254− -- , splitOnAny
259+ -- , splitOnAnySeq
255− -- , splitSuffixOnAny
260+ -- , splitOnAnySuffixSeq
256− -- , splitPrefixOnAny
261+ -- , splitOnAnyPrefixSeq
257
262
258 -- ** Grouping
263 -- ** Grouping
259 , groups
264 , groups
260 , groupsBy
265 , groupsBy
261− , groupsRollingBy
266+ , groupsByRolling
262
267
263 -- * Distributing
268 -- * Distributing
264 -- |
269 -- |
@@ -296,19 +301,26 @@ module Streamly.Fold
296
301
297 -- * Partitioning
302 -- * Partitioning
298 -- |
303 -- |
299− -- Direct items in the input stream to different folds using a function to
304+ -- Direct items in the input stream to different folds using a binary
300− -- select the fold. This is useful to demultiplex the input stream.
305+ -- fold selector.
306+
301 -- , partitionByM
307 -- , partitionByM
302 -- , partitionBy
308 -- , partitionBy
303 , partition
309 , partition
304
310
305 -- * Demultiplexing
311 -- * Demultiplexing
312+ -- | Direct values in the input stream to different folds using an n-ary
313+ -- fold selector.
314+
306 , demux
315 , demux
307 -- , demuxWith
316 -- , demuxWith
308 , demux_
317 , demux_
309 -- , demuxWith_
318 -- , demuxWith_
310
319
311 -- * Classifying
320 -- * Classifying
321+ -- | In an input stream of key value pairs fold values for different keys
322+ -- in individual output buckets using the given fold.
323+
312 , classify
324 , classify
313 -- , classifyWith
325 -- , classifyWith
314
326
@@ -318,7 +330,7 @@ module Streamly.Fold
318 -- , unzipWith
330 -- , unzipWith
319 -- , unzipWithM
331 -- , unzipWithM
320
332
321− -- * Nested Folds
333+ -- -- * Nested Folds
322 -- , concatMap
334 -- , concatMap
323 -- , chunksOf
335 -- , chunksOf
324 -- , duplicate -- experimental
336 -- , duplicate -- experimental
@@ -403,6 +415,7 @@ import qualified Streamly.Prelude as S
403import qualified Streamly.Streams.StreamD as D
415import qualified Streamly.Streams.StreamD as D
404import qualified Streamly.Streams.StreamK as K
416import qualified Streamly.Streams.StreamK as K
405import qualified Streamly.Streams.Prelude as P
417import qualified Streamly.Streams.Prelude as P
418+import qualified Streamly.Streams.Parallel as Par
406
419
407-- $termination
420-- $termination
408--
421--
@@ -1210,8 +1223,11 @@ sessionsOf
1210 :: (IsStream t, MonadAsync m)
1223 :: (IsStream t, MonadAsync m)
1211 => Double -> Fold m a b -> t m a -> t m b
1224 => Double -> Fold m a b -> t m a -> t m b
1212sessionsOf n f xs =
1225sessionsOf n f xs =
1213− splitSuffixBy' isNothing (lcatMaybes f)
1226+ splitBySuffix isNothing (lcatMaybes f)
1214− (S.intersperseByTime n (return Nothing) (S.map Just xs))
1227+ (intersperseByTime n (return Nothing) (S.map Just xs))
1228+ where
1229+ intersperseByTime n f xs = xs `Par.parallelEndByFirst` S.repeatM timed
1230+ where timed = liftIO (threadDelay (round $ n * 1000000)) >> f
1215
1231
1216------------------------------------------------------------------------------
1232------------------------------------------------------------------------------
1217-- Element Aware APIs
1233-- Element Aware APIs
@@ -1355,7 +1371,8 @@ _spanRollingBy cmp (Fold stepL initialL extractL) (Fold stepR initialR extractR)
1355-- first group, if @a \`cmp` b@ is 'True' then @b@ is also assigned to the same
1371-- first group, if @a \`cmp` b@ is 'True' then @b@ is also assigned to the same
1356-- group. If @a \`cmp` c@ is 'True' then @c@ is also assigned to the same
1372-- group. If @a \`cmp` c@ is 'True' then @c@ is also assigned to the same
1357-- group and so on. When the comparison fails a new group is started. Each
1373-- group and so on. When the comparison fails a new group is started. Each
1358−-- group is folded using the fold @f@.
1374+-- group is folded using the fold @f@ and the result of the fold is emitted in
1375+-- the output stream.
1359--
1376--
1360-- >>> S.toList $ FL.groupsBy (>) FL.toList $ S.fromList [1,3,7,0,2,5]
1377-- >>> S.toList $ FL.groupsBy (>) FL.toList $ S.fromList [1,3,7,0,2,5]
1361-- > [[1,3,7],[0,2,5]]
1378-- > [[1,3,7],[0,2,5]]
@@ -1371,31 +1388,31 @@ groupsBy
1371groupsBy cmp f m = D.fromStreamD $ D.groupsBy cmp f (D.toStreamD m)
1388groupsBy cmp f m = D.fromStreamD $ D.groupsBy cmp f (D.toStreamD m)
1372
1389
1373-- | Unlike @groupsBy@ this function performs a rolling comparison of two
1390-- | Unlike @groupsBy@ this function performs a rolling comparison of two
1374−-- successive elements in the input stream. @groupsRollingBy cmp f $ S.fromList
1391+-- successive elements in the input stream. @groupsByRolling cmp f $ S.fromList
1375-- [a,b,c,...]@ assigns the element @a@ to the first group, if @a \`cmp` b@ is
1392-- [a,b,c,...]@ assigns the element @a@ to the first group, if @a \`cmp` b@ is
1376-- 'True' then @b@ is also assigned to the same group. If @b \`cmp` c@ is
1393-- 'True' then @b@ is also assigned to the same group. If @b \`cmp` c@ is
1377-- 'True' then @c@ is also assigned to the same group and so on. When the
1394-- 'True' then @c@ is also assigned to the same group and so on. When the
1378-- comparison fails a new group is started. Each group is folded using the fold
1395-- comparison fails a new group is started. Each group is folded using the fold
1379-- @f@.
1396-- @f@.
1380--
1397--
1381−-- >>> S.toList $ FL.groupsRollingBy (\a b -> a + 1 == b) FL.toList $ S.fromList [1,2,3,7,8,9]
1398+-- >>> S.toList $ FL.groupsByRolling (\a b -> a + 1 == b) FL.toList $ S.fromList [1,2,3,7,8,9]
1382-- > [[1,2,3],[7,8,9]]
1399-- > [[1,2,3],[7,8,9]]
1383--
1400--
1384-- @since 0.7.0
1401-- @since 0.7.0
1385−{-# INLINE groupsRollingBy #-}
1402+{-# INLINE groupsByRolling #-}
1386−groupsRollingBy
1403+groupsByRolling
1387 :: (IsStream t, Monad m)
1404 :: (IsStream t, Monad m)
1388 => (a -> a -> Bool)
1405 => (a -> a -> Bool)
1389 -> Fold m a b
1406 -> Fold m a b
1390 -> t m a
1407 -> t m a
1391 -> t m b
1408 -> t m b
1392−groupsRollingBy cmp f m = D.fromStreamD $ D.groupsRollingBy cmp f (D.toStreamD m)
1409+groupsByRolling cmp f m = D.fromStreamD $ D.groupsRollingBy cmp f (D.toStreamD m)
1393
1410
1394-- |
1411-- |
1395-- > groups = groupsBy (==)
1412-- > groups = groupsBy (==)
1396−-- > groups = groupsRollingBy (==)
1413+-- > groups = groupsByRolling (==)
1397--
1414--
1398−-- Groups a contiguous span of equal elements together in one group.
1415+-- Groups contiguous spans of equal elements together in individual groups.
1399--
1416--
1400-- >>> S.toList $ FL.groups FL.toList $ S.fromList [1,1,2,2]
1417-- >>> S.toList $ FL.groups FL.toList $ S.fromList [1,1,2,2]
1401-- > [[1,1],[2,2]]
1418-- > [[1,1],[2,2]]
@@ -1435,92 +1452,104 @@ breakOn pat f m = undefined
1435-- as argument.
1452-- as argument.
1436
1453
1437-- | Split a stream on separator elements determined by a predicate, dropping
1454-- | Split a stream on separator elements determined by a predicate, dropping
1438−-- the separator. Separators are not considered part of stream segments on
1455+-- the separator. Separator is considered as infixed between two segments, if
1439−-- either side of it instead they are treated as infixed between two stream
1456+-- one side of the separator is missing then it is parsed as an empty stream.
1440−-- segments. For example, with @.@ as separator, @"a.b.c"@ would be parsed as
1457+-- With '-' representing elements and '.' as separator, 'splitOn' splits as
1441−-- @["a","b","c"]@. When @.@ is in leading or trailing position it is still
1458+-- follows:
1442−-- considered as infixed, treating the first or the last segment as empty. For
1459+--
1443−-- example, @".a."@ would be parsed as @["","a",""]@. This operation is
1460+-- @
1461+-- "--.--" => "--" "--"
1462+-- "--." => "--" ""
1463+-- ".--" => "" "--"
1464+-- @
1465+--
1444−-- opposite of 'intercalate'.
1466+-- @splitOn (== x)@ is an inverse of @intercalate (S.yield x)@
1445--
1467--
1446-- Let's use the following definition for illustration:
1468-- Let's use the following definition for illustration:
1447--
1469--
1448−-- > splitBy_ p xs = S.toList $ FL.splitBy p (FL.toList) (S.fromList xs)
1470+-- > splitOn' p xs = S.toList $ FL.splitOn p (FL.toList) (S.fromList xs)
1449--
1471--
1450−-- >>> splitBy_ (== '.') ""
1472+-- >>> splitOn' (== '.') ""
1451-- [""]
1473-- [""]
1452--
1474--
1453−-- >>> splitBy_ (== '.') "."
1475+-- >>> splitOn' (== '.') "."
1454-- ["",""]
1476-- ["",""]
1455--
1477--
1456−-- >>> splitBy_ (== '.') ".a"
1478+-- >>> splitOn' (== '.') ".a"
1457-- > ["","a"]
1479-- > ["","a"]
1458--
1480--
1459−-- >>> splitBy_ (== '.') "a."
1481+-- >>> splitOn' (== '.') "a."
1460-- > ["a",""]
1482-- > ["a",""]
1461--
1483--
1462−-- >>> splitBy_ (== '.') "a.b"
1484+-- >>> splitOn' (== '.') "a.b"
1463-- > ["a","b"]
1485-- > ["a","b"]
1464--
1486--
1465−-- >>> splitBy_ (== '.') "a..b"
1487+-- >>> splitOn' (== '.') "a..b"
1466-- > ["a","","b"]
1488-- > ["a","","b"]
1467--
1489--
1490+-- @since 0.7.0
1491+
1468−-- This can be considered as an n-fold version of 'break' where we apply
1492+-- This can be considered as an n-fold version of 'breakOn' where we apply
1469−-- 'break' successively on the input stream, dropping the first element
1493+-- 'breakOn' successively on the input stream, dropping the first element
1470-- of the second segment after each break.
1494-- of the second segment after each break.
1471--
1495--
1472−-- @since 0.7.0
1473−{-# INLINE splitBy #-}
1496+{-# INLINE splitOn #-}
1474−splitBy
1497+splitOn
1475 :: (IsStream t, Monad m)
1498 :: (IsStream t, Monad m)
1476 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1499 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1477−splitBy predicate f m =
1500+splitOn predicate f m =
1478 D.fromStreamD $ D.splitBy predicate f (D.toStreamD m)
1501 D.fromStreamD $ D.splitBy predicate f (D.toStreamD m)
1479
1502
1480−-- | Like 'splitBy' but the separator is treated as part of the previous
1503+-- | Like 'splitOn' but the separator is considered as suffixed to the segments
1481−-- stream segment (suffix). Therefore, when the separator is in trailing
1504+-- in the stream. A missing suffix at the end is allowed. A separator at the
1482−-- position, no empty segment is considered to follow it. For example, @"a.b."@
1505+-- beginning is parsed as empty segment. With '-' representing elements and
1483−-- would be parsed as @["a","b"]@ instead of @["a","b",""]@ as in the case of
1506+-- '.' as separator, 'splitOnSuffix' splits as follows:
1484−-- 'splitBy'.
1507+--
1508+-- @
1509+-- "--.--." => "--" "--"
1510+-- "--.--" => "--" "--"
1511+-- ".--." => "" "--"
1512+-- @
1485--
1513--
1486−-- > splitSuffixBy_ p xs = S.toList $ FL.splitSuffixBy p (FL.toList) (S.fromList xs)
1514+-- > splitOnSuffix' p xs = S.toList $ FL.splitSuffixBy p (FL.toList) (S.fromList xs)
1487--
1515--
1488−-- >>> splitSuffixBy_ (== '.') ""
1516+-- >>> splitOnSuffix' (== '.') ""
1489-- []
1517-- []
1490--
1518--
1491−-- >>> splitSuffixBy_ (== '.') "."
1519+-- >>> splitOnSuffix' (== '.') "."
1492-- [""]
1520-- [""]
1493--
1521--
1494−-- >>> splitSuffixBy_ (== '.') "a"
1522+-- >>> splitOnSuffix' (== '.') "a"
1495-- ["a"]
1523-- ["a"]
1496--
1524--
1497−-- >>> splitSuffixBy_ (== '.') ".a"
1525+-- >>> splitOnSuffix' (== '.') ".a"
1498-- > ["","a"]
1526-- > ["","a"]
1499--
1527--
1500−-- >>> splitSuffixBy_ (== '.') "a."
1528+-- >>> splitOnSuffix' (== '.') "a."
1501-- > ["a"]
1529-- > ["a"]
1502--
1530--
1503−-- >>> splitSuffixBy_ (== '.') "a.b"
1531+-- >>> splitOnSuffix' (== '.') "a.b"
1504-- > ["a","b"]
1532-- > ["a","b"]
1505--
1533--
1506−-- >>> splitSuffixBy_ (== '.') "a.b."
1534+-- >>> splitOnSuffix' (== '.') "a.b."
1507-- > ["a","b"]
1535-- > ["a","b"]
1508--
1536--
1509−-- >>> splitSuffixBy_ (== '.') "a..b.."
1537+-- >>> splitOnSuffix' (== '.') "a..b.."
1510-- > ["a","","b",""]
1538-- > ["a","","b",""]
1511--
1539--
1512−-- > lines = splitSuffixBy (== '\n')
1540+-- > lines = splitOnSuffix (== '\n')
1513--
1541--
1542+-- @since 0.7.0
1543+
1514-- This can be considered as an n-fold version of 'breakPost' where we apply
1544-- This can be considered as an n-fold version of 'breakPost' where we apply
1515-- 'breakPost' successively on the input stream, dropping the first element
1545-- 'breakPost' successively on the input stream, dropping the first element
1516-- of the second segment after each break.
1546-- of the second segment after each break.
1517--
1547--
1518−-- @since 0.7.0
1548+{-# INLINE splitOnSuffix #-}
1519−{-# INLINE splitSuffixBy #-}
1549+splitOnSuffix
1520−splitSuffixBy
1521 :: (IsStream t, Monad m)
1550 :: (IsStream t, Monad m)
1522 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1551 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1523−splitSuffixBy predicate f m =
1552+splitOnSuffix predicate f m =
1524 D.fromStreamD $ D.splitSuffixBy predicate f (D.toStreamD m)
1553 D.fromStreamD $ D.splitSuffixBy predicate f (D.toStreamD m)
1525
1554
1526-- | Like 'splitBy' but ignores repeated separators or separators in leading
1555-- | Like 'splitBy' but ignores repeated separators or separators in leading
@@ -1549,49 +1578,45 @@ wordsBy
1549wordsBy predicate f m =
1578wordsBy predicate f m =
1550 D.fromStreamD $ D.wordsBy predicate f (D.toStreamD m)
1579 D.fromStreamD $ D.wordsBy predicate f (D.toStreamD m)
1551
1580
1552−-- XXX we should express this using the Splitter config.
1581+-- | Like 'splitOnSuffix' but keeps the suffix attached to the resulting
1582+-- splits.
1553--
1583--
1554−-- We can get splitSuffixBy' by appending the suffix to the output segments
1584+-- > splitBySuffix' p xs = S.toList $ FL.splitBySuffix p (FL.toList) (S.fromList xs)
1555−-- produced by splitSuffixBy. However, it may add an additional suffix if the last
1556−-- fragment did not have a suffix in the first place.
1557−
1558−-- | Like 'splitSuffixBy' but keeps the suffix in the splits.
1559--
1585--
1560−-- > splitSuffixBy'_ p xs = S.toList $ FL.splitSuffixBy' p (FL.toList) (S.fromList xs)
1586+-- >>> splitBySuffix' (== '.') ""
1561−--
1562−-- >>> splitSuffixBy'_ (== '.') ""
1563-- []
1587-- []
1564--
1588--
1565−-- >>> splitSuffixBy'_ (== '.') "."
1589+-- >>> splitBySuffix' (== '.') "."
1566-- ["."]
1590-- ["."]
1567--
1591--
1568−-- >>> splitSuffixBy'_ (== '.') "a"
1592+-- >>> splitBySuffix' (== '.') "a"
1569-- ["a"]
1593-- ["a"]
1570--
1594--
1571−-- >>> splitSuffixBy'_ (== '.') ".a"
1595+-- >>> splitBySuffix' (== '.') ".a"
1572-- > [".","a"]
1596-- > [".","a"]
1573--
1597--
1574−-- >>> splitSuffixBy'_ (== '.') "a."
1598+-- >>> splitBySuffix' (== '.') "a."
1575-- > ["a."]
1599-- > ["a."]
1576--
1600--
1577−-- >>> splitSuffixBy'_ (== '.') "a.b"
1601+-- >>> splitBySuffix' (== '.') "a.b"
1578-- > ["a.","b"]
1602-- > ["a.","b"]
1579--
1603--
1580−-- >>> splitSuffixBy'_ (== '.') "a.b."
1604+-- >>> splitBySuffix' (== '.') "a.b."
1581-- > ["a.","b."]
1605-- > ["a.","b."]
1582--
1606--
1583−-- >>> splitSuffixBy'_ (== '.') "a..b.."
1607+-- >>> splitBySuffix' (== '.') "a..b.."
1584-- > ["a.",".","b.","."]
1608-- > ["a.",".","b.","."]
1585--
1609--
1610+-- @since 0.7.0
1611+
1586-- This can be considered as an n-fold version of 'breakPost' where we apply
1612-- This can be considered as an n-fold version of 'breakPost' where we apply
1587-- 'breakPost' successively on the input stream.
1613-- 'breakPost' successively on the input stream.
1588--
1614--
1589−-- @since 0.7.0
1615+{-# INLINE splitBySuffix #-}
1590−{-# INLINE splitSuffixBy' #-}
1616+splitBySuffix
1591−splitSuffixBy'
1592 :: (IsStream t, Monad m)
1617 :: (IsStream t, Monad m)
1593 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1618 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1594−splitSuffixBy' predicate f m =
1619+splitBySuffix predicate f m =
1595 D.fromStreamD $ D.splitSuffixBy' predicate f (D.toStreamD m)
1620 D.fromStreamD $ D.splitSuffixBy' predicate f (D.toStreamD m)
1596
1621
1597------------------------------------------------------------------------------
1622------------------------------------------------------------------------------
@@ -1624,43 +1649,43 @@ splitSuffixBy' predicate f m =
1624-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]
1649-- >>> splitList [1,2,3,3,4] [1,2,3,3,4]
1625-- > [[],[]]
1650-- > [[],[]]
1626
1651
1627−-- | Split the stream on both sides of a separator sequence, dropping the
1652+-- | Like 'splitOn' but the separator is a sequence of elements instead of a
1628−-- separator.
1653+-- single element.
1629--
1654--
1630-- For illustration, let's define a function that operates on pure lists:
1655-- For illustration, let's define a function that operates on pure lists:
1631--
1656--
1632-- @
1657-- @
1633−-- splitOn_ pat xs = S.toList $ FL.splitOn (A.fromList pat) (FL.toList) (S.fromList xs)
1658+-- splitOnSeq' pat xs = S.toList $ FL.splitOnSeq (A.fromList pat) (FL.toList) (S.fromList xs)
1634-- @
1659-- @
1635--
1660--
1636−-- >>> splitOn_ "" "hello"
1661+-- >>> splitOnSeq' "" "hello"
1637-- > ["h","e","l","l","o"]
1662-- > ["h","e","l","l","o"]
1638--
1663--
1639−-- >>> splitOn_ "hello" ""
1664+-- >>> splitOnSeq' "hello" ""
1640-- > [""]
1665-- > [""]
1641--
1666--
1642−-- >>> splitOn_ "hello" "hello"
1667+-- >>> splitOnSeq' "hello" "hello"
1643-- > ["",""]
1668-- > ["",""]
1644--
1669--
1645−-- >>> splitOn_ "x" "hello"
1670+-- >>> splitOnSeq' "x" "hello"
1646-- > ["hello"]
1671-- > ["hello"]
1647--
1672--
1648−-- >>> splitOn_ "h" "hello"
1673+-- >>> splitOnSeq' "h" "hello"
1649-- > ["","ello"]
1674-- > ["","ello"]
1650--
1675--
1651−-- >>> splitOn_ "o" "hello"
1676+-- >>> splitOnSeq' "o" "hello"
1652-- > ["hell",""]
1677-- > ["hell",""]
1653--
1678--
1654−-- >>> splitOn_ "e" "hello"
1679+-- >>> splitOnSeq' "e" "hello"
1655-- > ["h","llo"]
1680-- > ["h","llo"]
1656--
1681--
1657−-- >>> splitOn_ "l" "hello"
1682+-- >>> splitOnSeq' "l" "hello"
1658-- > ["he","","o"]
1683-- > ["he","","o"]
1659--
1684--
1660−-- >>> splitOn_ "ll" "hello"
1685+-- >>> splitOnSeq' "ll" "hello"
1661-- > ["he","o"]
1686-- > ["he","o"]
1662--
1687--
1663−-- 'splitOn' is an inverse of 'intercalate'. The following law always holds:
1688+-- 'splitOnSeq' is an inverse of 'intercalate'. The following law always holds:
1664--
1689--
1665-- > intercalate . splitOn == id
1690-- > intercalate . splitOn == id
1666--
1691--
@@ -1669,16 +1694,18 @@ splitSuffixBy' predicate f m =
1669--
1694--
1670-- > splitOn . intercalate == id
1695-- > splitOn . intercalate == id
1671--
1696--
1672−-- The following law always holds:
1673−--
1674−-- > concat . splitOn . intercalate == concat
1675−--
1676-- @since 0.7.0
1697-- @since 0.7.0
1698+
1699+-- XXX We can use a polymorphic vector implemented by Array# to represent the
1700+-- sequence, that way we can avoid the Storable constraint. If we still need
1701+-- Storable Array for performance, we can use a separate splitOnArray API for
1702+-- that. We can also have an API where the sequence itself is a lazy stream, so
1703+-- that we can search files in files for example.
1677−{-# INLINE splitOn #-}
1704+{-# INLINE splitOnSeq #-}
1678−splitOn
1705+splitOnSeq
1679 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1706 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1680 => Array a -> Fold m a b -> t m a -> t m b
1707 => Array a -> Fold m a b -> t m a -> t m b
1681−splitOn patt f m = D.fromStreamD $ D.splitOn patt f (D.toStreamD m)
1708+splitOnSeq patt f m = D.fromStreamD $ D.splitOn patt f (D.toStreamD m)
1682
1709
1683{-
1710{-
1684-- This can be implemented easily using Rabin Karp
1711-- This can be implemented easily using Rabin Karp
@@ -1722,11 +1749,11 @@ splitOnAny subseq f m = undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.to
1722-- > lines = splitSuffixOn "\n"
1749-- > lines = splitSuffixOn "\n"
1723--
1750--
1724-- @since 0.7.0
1751-- @since 0.7.0
1725−{-# INLINE splitSuffixOn #-}
1752+{-# INLINE splitOnSuffixSeq #-}
1726−splitSuffixOn
1753+splitOnSuffixSeq
1727 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1754 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1728 => Array a -> Fold m a b -> t m a -> t m b
1755 => Array a -> Fold m a b -> t m a -> t m b
1729−splitSuffixOn patt f m =
1756+splitOnSuffixSeq patt f m =
1730 D.fromStreamD $ D.splitSuffixOn False patt f (D.toStreamD m)
1757 D.fromStreamD $ D.splitSuffixOn False patt f (D.toStreamD m)
1731
1758
1732{-
1759{-
@@ -1741,7 +1768,7 @@ wordsOn subseq f m = undefined -- D.fromStreamD $ D.wordsOn f subseq (D.toStream
1741
1768
1742-- XXX use a non-monadic intersperse to remove the MonadAsync constraint.
1769-- XXX use a non-monadic intersperse to remove the MonadAsync constraint.
1743--
1770--
1744−-- | Like 'splitOn' but splits the separator as well, as an infix token.
1771+-- | Like 'splitOnSeq' but splits the separator as well, as an infix token.
1745--
1772--
1746-- > splitOn'_ pat xs = S.toList $ FL.splitOn' (A.fromList pat) (FL.toList) (S.fromList xs)
1773-- > splitOn'_ pat xs = S.toList $ FL.splitOn' (A.fromList pat) (FL.toList) (S.fromList xs)
1747--
1774--
@@ -1773,11 +1800,11 @@ wordsOn subseq f m = undefined -- D.fromStreamD $ D.wordsOn f subseq (D.toStream
1773-- > ["he","ll","o"]
1800-- > ["he","ll","o"]
1774--
1801--
1775-- @since 0.7.0
1802-- @since 0.7.0
1776−{-# INLINE splitOn' #-}
1803+{-# INLINE splitBySeq #-}
1777−splitOn'
1804+splitBySeq
1778 :: (IsStream t, MonadAsync m, Storable a, Enum a, Eq a)
1805 :: (IsStream t, MonadAsync m, Storable a, Enum a, Eq a)
1779 => Array a -> Fold m a b -> t m a -> t m b
1806 => Array a -> Fold m a b -> t m a -> t m b
1780−splitOn' patt f m = S.intersperseM (foldl' f (A.read patt)) $ splitOn patt f m
1807+splitBySeq patt f m = S.intersperseM (foldl' f (A.read patt)) $ splitOnSeq patt f m
1781
1808
1782-- | Like 'splitSuffixOn' but keeps the suffix intact in the splits.
1809-- | Like 'splitSuffixOn' but keeps the suffix intact in the splits.
1783--
1810--
@@ -1808,11 +1835,11 @@ splitOn' patt f m = S.intersperseM (foldl' f (A.read patt)) $ splitOn patt f m
1808-- > ["a.",".","b.","."]
1835-- > ["a.",".","b.","."]
1809--
1836--
1810-- @since 0.7.0
1837-- @since 0.7.0
1811−{-# INLINE splitSuffixOn' #-}
1838+{-# INLINE splitBySuffixSeq #-}
1812−splitSuffixOn'
1839+splitBySuffixSeq
1813 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1840 :: (IsStream t, MonadIO m, Storable a, Enum a, Eq a)
1814 => Array a -> Fold m a b -> t m a -> t m b
1841 => Array a -> Fold m a b -> t m a -> t m b
1815−splitSuffixOn' patt f m =
1842+splitBySuffixSeq patt f m =
1816 D.fromStreamD $ D.splitSuffixOn True patt f (D.toStreamD m)
1843 D.fromStreamD $ D.splitSuffixOn True patt f (D.toStreamD m)
1817
1844
1818{-
1845{-
@@ -1882,7 +1909,8 @@ foldCons (Fold stepL beginL doneL) (Fold stepR beginR doneR) =
1882 done (Tuple' xL xR) = (:) <$> (doneL xL) <*> (doneR xR)
1909 done (Tuple' xL xR) = (:) <$> (doneL xL) <*> (doneR xR)
1883
1910
1884-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large
1911-- XXX use "List" instead of "[]"?, use Array for output to scale it to a large
1885−-- number of consumers?
1912+-- number of consumers? For polymorphic case a vector could be helpful. For
1913+-- Storables we can use arrays. Will need separate APIs for those.
1886--
1914--
1887-- | Distribute one copy of the stream to each fold and collect the results in
1915-- | Distribute one copy of the stream to each fold and collect the results in
1888-- a container.
1916-- a container.
@@ -2010,11 +2038,15 @@ partition = partitionBy id
2010-- partitionN fs = Fold step begin done
2038-- partitionN fs = Fold step begin done
2011-}
2039-}
2012
2040
2013−-- Demultiplex an input element into a number of typed variants. We want to
2041+-- TODO Demultiplex an input element into a number of typed variants. We want
2014−-- statically restrict the target values within a set of predefined types, an
2042+-- to statically restrict the target values within a set of predefined types,
2015−-- enumeration of a GADT. We also want to make sure that the Map contains only
2043+-- an enumeration of a GADT. We also want to make sure that the Map contains
2016−-- those types and the full set of those types. Instead of Map it should
2044+-- only those types and the full set of those types.
2045+--
2017−-- probably be a lookup-table using an array and not in GC memory.
2046+-- TODO Instead of the input Map it should probably be a lookup-table using an
2047+-- array and not in GC memory. The same applies to the output Map as well.
2048+-- However, that would only be helpful if we have a very large data structure,
2049+-- need to measure and see how it scales.
2018--
2050--
2019-- This is the consumer side dual of the producer side 'mux' operation (XXX to
2051-- This is the consumer side dual of the producer side 'mux' operation (XXX to
2020-- be implemented).
2052-- be implemented).
@@ -2131,9 +2163,9 @@ demuxWith_ f kv = Fold step initial extract
2131demux_ :: (Monad m, Ord k) => Map k (Fold m a ()) -> Fold m (k, a) ()
2163demux_ :: (Monad m, Ord k) => Map k (Fold m a ()) -> Fold m (k, a) ()
2132demux_ fs = demuxWith_ fst (Map.map (lmap snd) fs)
2164demux_ fs = demuxWith_ fst (Map.map (lmap snd) fs)
2133
2165
2134−-- XXX instead of a Map we could yield the results as a pure stream as they
2166+-- TODO If the data is large we may need a map/hashmap in pinned memory instead
2135−-- complete. We could then concatMap the fold results to implement the
2167+-- of a regular Map. That may require a serializable constraint though. We can
2136−-- windowing combinators.
2168+-- have another API for that.
2137--
2169--
2138-- | Split the input stream based on a key field and fold each split using the
2170-- | Split the input stream based on a key field and fold each split using the
2139-- given fold. Useful for map/reduce, bucketizing the input in different bins
2171-- given fold. Useful for map/reduce, bucketizing the input in different bins
@@ -2175,11 +2207,12 @@ classifyWith f (Fold step initial extract) = Fold step' initial' extract'
2175-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
2207-- fromList [(\"ONE",[1.1,1.0]),(\"TWO",[2.2,2.0])]
2176-- @
2208-- @
2177--
2209--
2210+-- @since 0.7.0
2211+
2178-- Same as:
2212-- Same as:
2179--
2213--
2180-- > classify fld = classifyWith fst (lmap snd fld)
2214-- > classify fld = classifyWith fst (lmap snd fld)
2181--
2215--
2182−-- @since 0.7.0
2183{-# INLINE classify #-}
2216{-# INLINE classify #-}
2184classify :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
2217classify :: (Monad m, Ord k) => Fold m a b -> Fold m (k, a) (Map k b)
2185classify fld = classifyWith fst (lmap snd fld)
2218classify fld = classifyWith fst (lmap snd fld)
@@ -2219,9 +2252,9 @@ unzipWith f = unzipWithM (return . f)
2219--
2252--
2220-- @
2253-- @
2221--
2254--
2222−-- |-------Fold a x--------|
2255+-- |-------Fold m a x--------|
2223−-- -----Stream m x----(a,b)--| |----m (x,y)
2256+-- ---------stream of (a,b)--| |----m (x,y)
2224−-- |-------Fold b y--------|
2257+-- |-------Fold m b y--------|
2225--
2258--
2226-- @
2259-- @
2227--
2260--
src/Streamly/String.hs+2−2
@@ -171,7 +171,7 @@ stripStart = S.dropWhile isSpace
171-- ["lines", "this", "string", "", ""]
171-- ["lines", "this", "string", "", ""]
172{-# INLINE foldLines #-}
172{-# INLINE foldLines #-}
173foldLines :: (Monad m, IsStream t) => t m Char -> Fold m Char b -> t m b
173foldLines :: (Monad m, IsStream t) => t m Char -> Fold m Char b -> t m b
174−foldLines = flip (FL.splitSuffixBy (== '\n'))
174+foldLines = flip (FL.splitOnSuffix (== '\n'))
175
175
176-- | Fold each word of the stream using the supplied Fold
176-- | Fold each word of the stream using the supplied Fold
177-- and stream the result.
177-- and stream the result.
@@ -204,7 +204,7 @@ isSpace c
204-- 'foldLines' instead.
204-- 'foldLines' instead.
205{-# INLINE lines #-}
205{-# INLINE lines #-}
206lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
206lines :: (MonadIO m, IsStream t) => t m Char -> t m (Array Char)
207−lines = FL.splitSuffixBy (== '\n') toArray
207+lines = FL.splitOnSuffix (== '\n') toArray
208
208
209-- | Break a string up into a list of strings, which were delimited
209-- | Break a string up into a list of strings, which were delimited
210-- by characters representing white space.
210-- by characters representing white space.
test/Prop.hs+24−13
@@ -24,7 +24,7 @@ import GHC.Word (Word8)
24import Test.Hspec.QuickCheck
24import Test.Hspec.QuickCheck
25import Test.QuickCheck
25import Test.QuickCheck
26 (counterexample, Property, withMaxSuccess, forAll, choose, Gen,
26 (counterexample, Property, withMaxSuccess, forAll, choose, Gen,
27− arbitrary, elements, frequency, listOf, listOf1, vectorOf, suchThat)
27+ arbitrary, elements, frequency, listOf) --, listOf1, vectorOf, suchThat)
28import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
28import Test.QuickCheck.Monadic (run, monadicIO, monitor, assert, PropertyM)
29
29
30import Test.Hspec as H
30import Test.Hspec as H
@@ -493,7 +493,7 @@ transformCombineOpsCommon constr desc eq t = do
493
493
494 -- reordering
494 -- reordering
495 prop (desc <> " reverse") $ transform reverse t S.reverse
495 prop (desc <> " reverse") $ transform reverse t S.reverse
496− prop (desc <> " reverse'") $ transform reverse t S.reverse'
496+ -- prop (desc <> " reverse'") $ transform reverse t S.reverse'
497
497
498 -- inserting
498 -- inserting
499 prop (desc <> " intersperseM") $
499 prop (desc <> " intersperseM") $
@@ -510,56 +510,66 @@ transformCombineOpsCommon constr desc eq t = do
510 t (S.concatMap (const (S.fromList [1..n])))
510 t (S.concatMap (const (S.fromList [1..n])))
511
511
512toListFL :: Monad m => FL.Fold m a [a]
512toListFL :: Monad m => FL.Fold m a [a]
513−toListFL = fmap reverse FL.toListRev
513+toListFL = FL.toList
514
514
515groupSplitOps :: String -> Spec
515groupSplitOps :: String -> Spec
516groupSplitOps desc = do
516groupSplitOps desc = do
517 -- splitting
517 -- splitting
518 -- XXX add tests with multichar separators too
518 -- XXX add tests with multichar separators too
519
519
520+{-
520− prop (desc <> " intercalate . splitOn == id (nil separator)") $
521+ prop (desc <> " intercalate . splitOnSeq == id (nil separator)") $
521 forAll listWithZeroes $ \xs -> do
522 forAll listWithZeroes $ \xs -> do
522 withMaxSuccess maxTestCount $
523 withMaxSuccess maxTestCount $
523 monadicIO $ do
524 monadicIO $ do
524− ys <- S.toList $ FL.splitOn [] toListFL (S.fromList xs)
525+ ys <- S.toList $ FL.splitOnSeq [] toListFL (S.fromList xs)
525 listEquals (==) (intercalate [] ys) xs
526 listEquals (==) (intercalate [] ys) xs
526
527
527− prop (desc <> " intercalate . splitOn == id (single element separator)") $
528+ prop (desc <> " intercalate . splitOnSeq == id (single element separator)") $
528 forAll listWithZeroes $ \xs -> do
529 forAll listWithZeroes $ \xs -> do
529 withMaxSuccess maxTestCount $
530 withMaxSuccess maxTestCount $
530 monadicIO $ do
531 monadicIO $ do
531− ys <- S.toList $ FL.splitOn [0] toListFL (S.fromList xs)
532+ ys <- S.toList $ FL.splitOnSeq [0] toListFL (S.fromList xs)
532 listEquals (==) (intercalate [0] ys) xs
533 listEquals (==) (intercalate [0] ys) xs
533
534
534− prop (desc <> " concat . splitOn . intercalate == concat (nil separator/possibly empty list)") $
535+ prop (desc <> " concat . splitOnSeq . intercalate == concat (nil separator/possibly empty list)") $
535 forAll listsWithoutZeroes $ \xss -> do
536 forAll listsWithoutZeroes $ \xss -> do
536 withMaxSuccess maxTestCount $
537 withMaxSuccess maxTestCount $
537 monadicIO $ do
538 monadicIO $ do
538 let xs = intercalate [] xss
539 let xs = intercalate [] xss
539− ys <- S.toList $ FL.splitOn [0] toListFL (S.fromList xs)
540+ ys <- S.toList $ FL.splitOnSeq [0] toListFL (S.fromList xs)
540 listEquals (==) (concat ys) (concat xss)
541 listEquals (==) (concat ys) (concat xss)
541
542
542− prop (desc <> " concat . splitOn . intercalate == concat (non-nil separator/possibly empty list)") $
543+ prop (desc <> " concat . splitOnSeq . intercalate == concat (non-nil separator/possibly empty list)") $
543 forAll listsWithoutZeroes $ \xss -> do
544 forAll listsWithoutZeroes $ \xss -> do
544 withMaxSuccess maxTestCount $
545 withMaxSuccess maxTestCount $
545 monadicIO $ do
546 monadicIO $ do
546 let xs = intercalate [0] xss
547 let xs = intercalate [0] xss
547− ys <- S.toList $ FL.splitOn [0] toListFL (S.fromList xs)
548+ ys <- S.toList $ FL.splitOnSeq [0] toListFL (S.fromList xs)
548 listEquals (==) (concat ys) (concat xss)
549 listEquals (==) (concat ys) (concat xss)
549
550
550− prop (desc <> " splitOn . intercalate == id (exclusive separator/non-empty list)") $
551+ prop (desc <> " splitOnSeq . intercalate == id (exclusive separator/non-empty list)") $
551 forAll listsWithoutZeroes1 $ \xss -> do
552 forAll listsWithoutZeroes1 $ \xss -> do
552 withMaxSuccess maxTestCount $
553 withMaxSuccess maxTestCount $
553 monadicIO $ do
554 monadicIO $ do
554 let xs = intercalate [0] xss
555 let xs = intercalate [0] xss
555− ys <- S.toList $ FL.splitOn [0] toListFL (S.fromList xs)
556+ ys <- S.toList $ FL.splitOnSeq [0] toListFL (S.fromList xs)
556 listEquals (==) ys xss
557 listEquals (==) ys xss
558+-}
559+
560+ prop (desc <> " intercalate [x] . splitOn (== x) == id") $
561+ forAll listWithZeroes $ \xs -> do
562+ withMaxSuccess maxTestCount $
563+ monadicIO $ do
564+ ys <- S.toList $ FL.splitOn (== 0) toListFL (S.fromList xs)
565+ listEquals (==) (intercalate [0] ys) xs
557
566
558 where
567 where
559
568
560 listWithZeroes :: Gen [Int]
569 listWithZeroes :: Gen [Int]
561 listWithZeroes = listOf $ frequency [(3, arbitrary), (1, elements [0])]
570 listWithZeroes = listOf $ frequency [(3, arbitrary), (1, elements [0])]
562
571
572+{-
563 listWithoutZeroes = vectorOf 4 $ suchThat arbitrary (/= 0)
573 listWithoutZeroes = vectorOf 4 $ suchThat arbitrary (/= 0)
564
574
565 listsWithoutZeroes :: Gen [[Int]]
575 listsWithoutZeroes :: Gen [[Int]]
@@ -567,6 +577,7 @@ groupSplitOps desc = do
567
577
568 listsWithoutZeroes1 :: Gen [[Int]]
578 listsWithoutZeroes1 :: Gen [[Int]]
569 listsWithoutZeroes1 = listOf1 listWithoutZeroes
579 listsWithoutZeroes1 = listOf1 listWithoutZeroes
580+-}
570
581
571-- transformation tests that can only work reliably for ordered streams i.e.
582-- transformation tests that can only work reliably for ordered streams i.e.
572-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it
583-- Serial, Ahead and Zip. For example if we use "take 1" on an async stream, it