on master help sign in
Update docs of splitting combinators

Add a template/signature for splitOnPrefix function.

commit39ec0b664862cdfbb77d5f7bb9c4e703c3dfec1d authorHarendra Kumar <harendra@composewell.com> dateSun 7 Feb 2021, 23:03 (6 years ago) parent00d3cb5
1 file changed+149−64
src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs+149−64
src/Streamly/Internal/Data/Stream/IsStream/Nesting.hs+149−64
@@ -173,7 +173,7 @@ module Streamly.Internal.Data.Stream.IsStream.Nesting
173173 -- -- *** Using Element Separators
174174 , splitOn
175175 , splitOnSuffix
176− -- , splitOnPrefix
176+ , splitOnPrefix
177177
178178 -- , splitBy
179179 , splitWithSuffix
@@ -903,19 +903,16 @@ iterateMapLeftsWith combine f = iterateMapWith combine (either f (const K.nil))
903903--
904904-- XXX We need takeGE/takeBetween to implement "some" using "many".
905905
906−-- | Apply a 'Fold' repeatedly on a stream and emit the parsed values in the
907−-- output stream.
908−--
909−-- This is the streaming dual of the 'Streamly.Internal.Data.Fold.many'
910−-- parse combinator.
906+-- | Like 'foldMany1' but appends empty fold output if the fold and stream
907+-- termination aligns:
911908--
912909-- >>> f = Fold.takeLE 2 Fold.sum
910+-- >>> Stream.toList $ Stream.foldMany f $ Stream.fromList []
911+-- > [0]
912+-- >>> Stream.toList $ Stream.foldMany f $ Stream.fromList [1..9]
913+-- > [3,7,11,15,9]
913914-- >>> Stream.toList $ Stream.foldMany f $ Stream.fromList [1..10]
914−-- > [3,7,11,15,19]
915−--
916−-- >>> f = Fold.sliceEndWith Fold.toList
917−-- >>> Stream.toList $ Stream.foldMany f $ Stream.fromList "hello\nworld"
918−-- > ["hello\n","world"]
915+-- > [3,7,11,15,19,0]
919916--
920917-- /Internal/
921918--
@@ -927,6 +924,25 @@ foldMany
927924 -> t m b
928925foldMany f m = D.fromStreamD $ D.foldMany f (D.toStreamD m)
929926
927+-- | Apply a 'Fold' repeatedly on a stream and emit the fold outputs in the
928+-- output stream.
929+--
930+-- To sum every two contiguous elements in a stream:
931+--
932+-- >>> f = Fold.takeLE 2 Fold.sum
933+-- >>> Stream.toList $ Stream.foldMany1 f $ Stream.fromList [1..10]
934+-- > [3,7,11,15,19]
935+--
936+-- On an empty stream the output is empty:
937+--
938+-- >>> Stream.toList $ Stream.foldMany1 f $ Stream.fromList []
939+-- > []
940+--
941+-- Note @foldMany1 (takeLE 0)@ would result in an infinite loop in a non-empty
942+-- stream.
943+--
944+-- /Internal/
945+--
930946{-# INLINE foldMany1 #-}
931947foldMany1
932948 :: (IsStream t, Monad m)
@@ -988,6 +1004,13 @@ foldIterate _f _i _m = undefined
9881004-- >>> S.toList $ S.parseMany (PR.line FL.toList) $ S.fromList "hello\nworld"
9891005-- > ["hello\n","world"]
9901006--
1007+-- @
1008+-- foldMany1 f = parseMany (fromFold f)
1009+-- @
1010+--
1011+-- Known Issues: When the parser fails there is no way to get the remaining
1012+-- stream.
1013+--
9911014-- /Internal/
9921015--
9931016{-# INLINE parseMany #-}
@@ -1201,34 +1224,32 @@ groups = groupsBy (==)
12011224-- Splitting - by a predicate
12021225------------------------------------------------------------------------------
12031226
1204−-- TODO: Use a Splitter configuration similar to the "split" package to make it
1205−-- possible to express all splitting combinations. In general, we can have
1206−-- infix/suffix/prefix/condensing of separators, dropping both leading/trailing
1207−-- separators. We can have a single split operation taking the splitter config
1208−-- as argument.
1209−
1210−-- | Split on an infixed separator element, dropping the separator. Splits the
1211−-- stream on separator elements determined by the supplied predicate, separator
1212−-- is considered as infixed between two segments, if one side of the separator
1213−-- is missing then it is parsed as an empty stream. The supplied 'Fold' is
1214−-- applied on the split segments. With '-' representing non-separator elements
1215−-- and '.' as separator, 'splitOn' splits as follows:
1227+-- In general we can use deintercalate for splitting. Then we can also use
1228+-- uniqBy to condense the separators. One way to generalize splitting is to
1229+-- output:
12161230--
1217−-- @
1218−-- "--.--" => "--" "--"
1219−-- "--." => "--" ""
1220−-- ".--" => "" "--"
1221−-- @
1231+-- data Segment a b = Empty | Segment b | Separator a
12221232--
1223−-- @splitOn (== x)@ is an inverse of @intercalate (S.yield x)@
1233+-- XXX splitOn and splitOnSuffix have a different behavior on an empty stream,
1234+-- is that desirable?
1235+
1236+-- | Split on an infixed separator element, dropping the separator. The
1237+-- supplied 'Fold' is applied on the split segments. Splits the stream on
1238+-- separator elements determined by the supplied predicate, separator is
1239+-- considered as infixed between two segments:
12241240--
1225−-- Let's use the following definition for illustration:
1241+-- >>> splitOn' p xs = S.toList $ S.splitOn p FL.toList (S.fromList xs)
1242+-- >>> splitOn' (== '.') "a.b"
1243+-- > ["a","b"]
12261244--
1227−-- > splitOn' p xs = S.toList $ S.splitOn p (FL.toList) (S.fromList xs)
1245+-- An empty stream is folded to the default value of the fold:
12281246--
12291247-- >>> splitOn' (== '.') ""
12301248-- [""]
12311249--
1250+-- If one or both sides of the separator are missing then the empty segment on
1251+-- that side is folded to the default output of the fold:
1252+--
12321253-- >>> splitOn' (== '.') "."
12331254-- ["",""]
12341255--
@@ -1238,69 +1259,85 @@ groups = groupsBy (==)
12381259-- >>> splitOn' (== '.') "a."
12391260-- > ["a",""]
12401261--
1241−-- >>> splitOn' (== '.') "a.b"
1242−-- > ["a","b"]
1243−--
12441262-- >>> splitOn' (== '.') "a..b"
12451263-- > ["a","","b"]
12461264--
1265+-- splitOn is an inverse of intercalating single element:
1266+--
1267+-- @Stream.intercalate (Stream.yield '.') Unfold.fromList . Stream.splitOn (== '.') Fold.toList === id@
1268+--
1269+-- Assuming the input stream does not contain the separator:
1270+--
1271+-- @Stream.splitOn (== '.') Fold.toList . Stream.intercalate (Stream.yield '.') Unfold.fromList === id@
1272+--
12471273-- @since 0.7.0
12481274
1249−-- This can be considered as an n-fold version of 'breakOn' where we apply
1250−-- 'breakOn' successively on the input stream, dropping the first element
1251−-- of the second segment after each break.
1252−--
12531275{-# INLINE splitOn #-}
12541276splitOn
12551277 :: (IsStream t, Monad m)
12561278 => (a -> Bool) -> Fold m a b -> t m a -> t m b
1257−splitOn predicate f = foldMany (FL.sliceSepBy predicate f)
1279+splitOn predicate f =
1280+ -- We can express the infix splitting in terms of optional suffix split
1281+ -- fold. After applying a suffix split fold repeatedly if the last segment
1282+ -- ends with a suffix then we need to return the default output of the fold
1283+ -- after that to make it an infix split.
1284+ --
1285+ -- Alternately, we can also express it using an optional prefix split fold.
1286+ -- If the first segment starts with a prefix then we need to emit the
1287+ -- default output of the fold before that to make it an infix split, and
1288+ -- then apply prefix split fold repeatedly.
1289+ --
1290+ -- Since a suffix split fold can be easily expressed using a
1291+ -- non-backtracking fold, we use that.
1292+ foldMany (FL.sliceSepBy predicate f)
12581293
1259−-- | Like 'splitOn' but the separator is considered as suffixed to the segments
1260−-- in the stream. A missing suffix at the end is allowed. A separator at the
1261−-- beginning is parsed as empty segment. With '-' representing elements and
1262−-- '.' as separator, 'splitOnSuffix' splits as follows:
1294+-- | Split on a suffixed separator element, dropping the separator. The
1295+-- supplied 'Fold' is applied on the split segments.
12631296--
1264−-- @
1265−-- "--.--." => "--" "--"
1266−-- "--.--" => "--" "--"
1267−-- ".--." => "" "--"
1268−-- @
1297+-- > splitOnSuffix' p xs = S.toList $ S.splitOnSuffix p (FL.toList) (S.fromList xs)
1298+-- >>> splitOnSuffix' (== '.') "a.b."
1299+-- > ["a","b"]
12691300--
1270−-- > splitOnSuffix' p xs = S.toList $ S.splitSuffixBy p (FL.toList) (S.fromList xs)
1301+-- >>> splitOnSuffix' (== '.') "a."
1302+-- > ["a"]
1303+--
1304+-- An empty stream results in an empty output stream:
12711305--
12721306-- >>> splitOnSuffix' (== '.') ""
12731307-- []
12741308--
1309+-- An empty segment consisting of only a suffix is folded to the default output
1310+-- of the fold:
1311+--
12751312-- >>> splitOnSuffix' (== '.') "."
12761313-- [""]
12771314--
1315+-- >>> splitOnSuffix' (== '.') "a..b.."
1316+-- > ["a","","b",""]
1317+--
1318+-- A suffix is optional at the end of the stream:
1319+--
12781320-- >>> splitOnSuffix' (== '.') "a"
12791321-- ["a"]
12801322--
12811323-- >>> splitOnSuffix' (== '.') ".a"
12821324-- > ["","a"]
12831325--
1284−-- >>> splitOnSuffix' (== '.') "a."
1285−-- > ["a"]
1286−--
12871326-- >>> splitOnSuffix' (== '.') "a.b"
12881327-- > ["a","b"]
12891328--
1290−-- >>> splitOnSuffix' (== '.') "a.b."
1291−-- > ["a","b"]
1329+-- > lines = splitOnSuffix (== '\n')
12921330--
1293−-- >>> splitOnSuffix' (== '.') "a..b.."
1294−-- > ["a","","b",""]
1331+-- 'splitOnSuffix' is an inverse of 'intercalateSuffix' with a single element:
12951332--
1296−-- > lines = splitOnSuffix (== '\n')
1333+-- @Stream.intercalateSuffix (Stream.yield '.') Unfold.fromList . Stream.splitOnSuffix (== '.') Fold.toList === id@
1334+--
1335+-- Assuming the input stream does not contain the separator:
1336+--
1337+-- @Stream.splitOnSuffix (== '.') Fold.toList . Stream.intercalateSuffix (Stream.yield '.') Unfold.fromList === id@
12971338--
12981339-- @since 0.7.0
12991340
1300−-- This can be considered as an n-fold version of 'breakPost' where we apply
1301−-- 'breakPost' successively on the input stream, dropping the first element
1302−-- of the second segment after each break.
1303−--
13041341{-# INLINE splitOnSuffix #-}
13051342splitOnSuffix
13061343 :: (IsStream t, Monad m)
@@ -1308,6 +1345,53 @@ splitOnSuffix
13081345splitOnSuffix predicate f m =
13091346 D.fromStreamD $ D.foldMany1 (FL.sliceSepBy predicate f) (D.toStreamD m)
13101347
1348+-- | Split on a prefixed separator element, dropping the separator. The
1349+-- supplied 'Fold' is applied on the split segments.
1350+--
1351+-- >>> splitOnPrefix' p xs = S.toList $ S.splitOnPrefix p (FL.toList) (S.fromList xs)
1352+-- >>> splitOnPrefix' (== '.') ".a.b"
1353+-- > ["a","b"]
1354+--
1355+-- An empty stream results in an empty output stream:
1356+-- >>> splitOnPrefix' (== '.') ""
1357+-- []
1358+--
1359+-- An empty segment consisting of only a prefix is folded to the default output
1360+-- of the fold:
1361+--
1362+-- >>> splitOnPrefix' (== '.') "."
1363+-- [""]
1364+--
1365+-- >>> splitOnPrefix' (== '.') ".a.b."
1366+-- > ["a","b",""]
1367+--
1368+-- >>> splitOnPrefix' (== '.') ".a..b"
1369+-- > ["a","","b"]
1370+--
1371+-- A prefix is optional at the beginning of the stream:
1372+--
1373+-- >>> splitOnPrefix' (== '.') "a"
1374+-- ["a"]
1375+--
1376+-- >>> splitOnPrefix' (== '.') "a.b"
1377+-- > ["a","b"]
1378+--
1379+-- 'splitOnPrefix' is an inverse of 'intercalatePrefix' with a single element:
1380+--
1381+-- @Stream.intercalatePrefix (Stream.yield '.') Unfold.fromList . Stream.splitOnPrefix (== '.') Fold.toList === id@
1382+--
1383+-- Assuming the input stream does not contain the separator:
1384+--
1385+-- @Stream.splitOnPrefix (== '.') Fold.toList . Stream.intercalatePrefix (Stream.yield '.') Unfold.fromList === id@
1386+--
1387+-- /Unimplemented/
1388+
1389+{-# INLINE splitOnPrefix #-}
1390+splitOnPrefix :: -- (IsStream t, MonadCatch m) =>
1391+ (a -> Bool) -> Fold m a b -> t m a -> t m b
1392+splitOnPrefix _predicate _f = undefined
1393+ -- parseMany (Parser.sliceBeginBy predicate f)
1394+
13111395-- | Like 'splitOn' after stripping leading, trailing, and repeated separators.
13121396-- Therefore, @".a..b."@ with '.' as the separator would be parsed as
13131397-- @["a","b"]@. In other words, its like parsing words from whitespace
@@ -1368,9 +1452,6 @@ wordsBy predicate f m =
13681452--
13691453-- @since 0.7.0
13701454
1371−-- This can be considered as an n-fold version of 'breakPost' where we apply
1372−-- 'breakPost' successively on the input stream.
1373−--
13741455{-# INLINE splitWithSuffix #-}
13751456splitWithSuffix
13761457 :: (IsStream t, Monad m)
@@ -1419,6 +1500,10 @@ splitOnAny subseq f m = undefined -- D.fromStreamD $ D.splitOnAny f subseq (D.to
14191500-}
14201501
14211502-- XXX use a non-monadic intersperse to remove the MonadAsync constraint.
1503+-- XXX Use two folds, one ring buffer fold for separator sequence and the other
1504+-- split consumer fold. The input is fed to the ring fold first and the
1505+-- rejected input is fed to the split fold. If the separator matches, the ring
1506+-- fold would consume all.
14221507--
14231508-- | Like 'splitOnSeq' but splits the separator as well, as an infix token.
14241509--