From a6d94905f2847ffdca4e8b701c290e8667258814 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 11:50:22 -0500 Subject: [PATCH 1/6] Update Sequence/Collection polarity --- .../Evolution/StringProcessingAlgorithms.md | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index edefbd19b..de80e4936 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -234,7 +234,7 @@ extension Collection where Element: Equatable { /// - Parameter other: A sequence to search for within this collection. /// - Returns: `true` if the collection contains the specified sequence, /// otherwise `false`. - public func contains(_ other: S) -> Bool + public func contains(_ other: C) -> Bool where S.Element == Element } @@ -281,7 +281,7 @@ extension Collection where SubSequence == Self { /// - Parameter predicate: A closure that takes an element of the sequence /// as its argument and returns a Boolean value indicating whether the /// element should be removed from the collection. - public mutating func trimPrefix(while predicate: (Element) throws -> Bool) + public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows } extension RangeReplaceableCollection { @@ -290,7 +290,7 @@ extension RangeReplaceableCollection { /// - Parameter predicate: A closure that takes an element of the sequence /// as its argument and returns a Boolean value indicating whether the /// element should be removed from the collection. - public mutating func trimPrefix(while predicate: (Element) throws -> Bool) + public mutating func trimPrefix(while predicate: (Element) throws -> Bool) rethrows } extension Collection where Element: Equatable { @@ -299,21 +299,21 @@ extension Collection where Element: Equatable { /// - Parameter prefix: The collection to remove from this collection. /// - Returns: A collection containing the elements that does not match /// `prefix` from the start. - public func trimmingPrefix(_ prefix: Prefix) -> SubSequence + public func trimmingPrefix(_ prefix: Prefix) -> SubSequence where Prefix.Element == Element } extension Collection where SubSequence == Self, Element: Equatable { /// Removes the initial elements that matches `prefix` from the start. /// - Parameter prefix: The collection to remove from this collection. - public mutating func trimPrefix(_ prefix: Prefix) + public mutating func trimPrefix(_ prefix: Prefix) where Prefix.Element == Element } extension RangeReplaceableCollection where Element: Equatable { /// Removes the initial elements that matches `prefix` from the start. /// - Parameter prefix: The collection to remove from this collection. - public mutating func trimPrefix(_ prefix: Prefix) + public mutating func trimPrefix(_ prefix: Prefix) where Prefix.Element == Element } @@ -344,8 +344,8 @@ extension Collection where Element: Equatable { /// - Parameter sequence: The sequence to search for. /// - Returns: A range in the collection of the first occurrence of `sequence`. /// Returns nil if `sequence` is not found. - public func firstRange(of sequence: S) -> Range? - where S.Element == Element + public func firstRange(of other: C) -> Range? + where C.Element == Element } extension BidirectionalCollection where Element: Comparable { @@ -354,8 +354,8 @@ extension BidirectionalCollection where Element: Comparable { /// - Parameter other: The sequence to search for. /// - Returns: A range in the collection of the first occurrence of `sequence`. /// Returns `nil` if `sequence` is not found. - public func firstRange(of other: S) -> Range? - where S.Element == Element + public func firstRange(of other: C) -> Range? + where C.Element == Element } extension BidirectionalCollection where SubSequence == Substring { @@ -377,8 +377,8 @@ extension Collection where Element: Equatable { /// - Parameter other: The sequence to search for. /// - Returns: A collection of ranges of all occurrences of `other`. Returns /// an empty collection if `other` is not found. - public func ranges(of other: S) -> some Collection> - where S.Element == Element + public func ranges(of other: C) -> some Collection> + where C.Element == Element } extension BidirectionalCollection where SubSequence == Substring { @@ -438,12 +438,12 @@ extension RangeReplaceableCollection where Element: Equatable { /// to replace. Default is `Int.max`. /// - Returns: A new collection in which all occurrences of `other` in /// `subrange` of the collection are replaced by `replacement`. - public func replacing( - _ other: S, + public func replacing( + _ other: C, with replacement: Replacement, subrange: Range, maxReplacements: Int = .max - ) -> Self where S.Element == Element, Replacement.Element == Element + ) -> Self where C.Element == Element, Replacement.Element == Element /// Returns a new collection in which all occurrences of a target sequence /// are replaced by another collection. @@ -454,11 +454,11 @@ extension RangeReplaceableCollection where Element: Equatable { /// to replace. Default is `Int.max`. /// - Returns: A new collection in which all occurrences of `other` in /// `subrange` of the collection are replaced by `replacement`. - public func replacing( - _ other: S, + public func replacing( + _ other: C, with replacement: Replacement, maxReplacements: Int = .max - ) -> Self where S.Element == Element, Replacement.Element == Element + ) -> Self where C.Element == Element, Replacement.Element == Element /// Replaces all occurrences of a target sequence with a given collection /// - Parameters: @@ -466,11 +466,11 @@ extension RangeReplaceableCollection where Element: Equatable { /// - replacement: The new elements to add to the collection. /// - maxReplacements: A number specifying how many occurrences of `other` /// to replace. Default is `Int.max`. - public mutating func replace( - _ other: S, + public mutating func replace( + _ other: C, with replacement: Replacement, maxReplacements: Int = .max - ) where S.Element == Element, Replacement.Element == Element + ) where C.Element == Element, Replacement.Element == Element } extension RangeReplaceableCollection where SubSequence == Substring { @@ -578,8 +578,8 @@ extension Collection where Element: Equatable { /// - Parameter separator: The element to be split upon. /// - Returns: A collection of subsequences, split from this collection's /// elements. - public func split(by separator: S) -> some Collection - where S.Element == Element + public func split(by separator: C) -> some Collection + where C.Element == Element } extension BidirectionalCollection where SubSequence == Substring { From 0df5133b51b8d54136f12ec763eba8ab3f94e423 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 12:25:56 -0500 Subject: [PATCH 2/6] Add a note about new generics features --- .../Evolution/StringProcessingAlgorithms.md | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index de80e4936..6b16cd069 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -592,9 +592,23 @@ extension BidirectionalCollection where SubSequence == Substring { } ``` +**Note:** We plan to adopt the new generics features enabled by [SE-0346][] for these proposed methods when the standard library adopts primary associated types, [pending a forthcoming proposal][stdlib-pitch]. For example, the first method in the _Replacement_ section above would instead be: +```swift +extension RangeReplaceableCollection where Element: Equatable { + /// Returns a new collection in which all occurrences of a target sequence + /// are replaced by another collection. + public func replacing( + _ other: some Collection, + with replacement: some Collection, + subrange: Range, + maxReplacements: Int = .max + ) -> Self +} +``` - +[SE-0346]: https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md +[stdlib-pitch]: https://forums.swift.org/t/pitch-primary-associated-types-in-the-standard-library/56426 ## Alternatives considered From e03fefc67b0d88364503612bf129d2aaeefec311 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 12:26:16 -0500 Subject: [PATCH 3/6] Update split methods, catch some inconsistencies --- .../Evolution/StringProcessingAlgorithms.md | 48 ++++++++++++++----- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index 6b16cd069..20fd8c994 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -399,17 +399,17 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: The regex to search for. /// - Returns: The first match of `regex` in the collection, or `nil` if /// there isn't a match. - public func firstMatch(of regex: R) -> RegexMatch? + public func firstMatch(of regex: R) -> Regex.Match? /// Match a regex in its entirety. /// - Parameter r: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. - public func wholeMatch(of r: R) -> Regex.Match? + public func wholeMatch(of regex: R) -> Regex.Match? /// Match part of the regex, starting at the beginning. /// - Parameter r: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. - public func prefixMatch(of r: R) -> Regex.Match? + public func prefixMatch(of regex: R) -> Regex.Match? } ``` @@ -574,21 +574,45 @@ extension RangeReplaceableCollection where SubSequence == Substring { ```swift extension Collection where Element: Equatable { /// Returns the longest possible subsequences of the collection, in order, - /// around elements equal to the given separator. - /// - Parameter separator: The element to be split upon. + /// around elements equal to the given separator collection. + /// + /// - Parameters: + /// - separator: A collection of elements to be split upon. + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of separator + /// sequences in the collection and for each instance of separator + /// sequences at the start or end of the collection. If `true`, only + /// nonempty subsequences are returned. /// - Returns: A collection of subsequences, split from this collection's - /// elements. - public func split(by separator: C) -> some Collection - where C.Element == Element + /// elements. + public func split( + by separator: C, + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true + ) -> some Collection where C.Element == Element } extension BidirectionalCollection where SubSequence == Substring { /// Returns the longest possible subsequences of the collection, in order, - /// around elements equal to the given separator. - /// - Parameter separator: A regex describing elements to be split upon. + /// around subsequence that match the given separator regex. + /// + /// - Parameters: + /// - separator: A regex to be split upon. + /// - maxSplits: The maximum number of times to split the collection, + /// or one less than the number of subsequences to return. + /// - omittingEmptySubsequences: If `false`, an empty subsequence is + /// returned in the result for each consecutive pair of matches + /// and for each match at the start or end of the collection. If + /// `true`, only nonempty subsequences are returned. /// - Returns: A collection of substrings, split from this collection's - /// elements. - public func split(by separator: R) -> some Collection + /// elements. + public func split( + by separator: R, + maxSplits: Int = Int.max, + omittingEmptySubsequences: Bool = true + ) -> some Collection } ``` From 0c057e2ac7811f24a2129b6b8071cc8a9c1bbd79 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 12:34:52 -0500 Subject: [PATCH 4/6] Adopt opaque parameter types where possible today Also includes some generic naming updates and typo fixes. --- .../Evolution/StringProcessingAlgorithms.md | 48 +++++++++---------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index 20fd8c994..3976e4e8b 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -14,7 +14,7 @@ This proposal is part of a larger [regex-powered string processing initiative](h ## Motivation -A number of common string processing APIs are missing from the Swift standard library. While most of the desired functionalities can be accomplished through a series of API calls, every gap adds a burden to developers doing frequent or complex string processing. For example, here's one approach to find the number of occurrences a substring ("banana") within a string: +A number of common string processing APIs are missing from the Swift standard library. While most of the desired functionalities can be accomplished through a series of API calls, every gap adds a burden to developers doing frequent or complex string processing. For example, here's one approach to find the number of occurrences of a substring ("banana") within a string: ```swift let str = "A banana a day keeps the doctor away. I love bananas; banana are my favorite fruit." @@ -216,10 +216,10 @@ Matching and extracting a localized currency amount, such as `"$3,020.85"`, can ```swift let regex = Regex { - capture(.localizedCurreny(code: "USD")) + Capture(.localizedCurrency(code: "USD")) } ``` - + @@ -244,7 +244,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: A regex to search for within this collection. /// - Returns: `true` if the regex was found in the collection, otherwise /// `false`. - public func contains(_ regex: R) -> Bool + public func contains(_ regex: some RegexComponent) -> Bool } ``` @@ -257,7 +257,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: A regex to compare to this sequence. /// - Returns: `true` if the initial elements of the sequence matches the /// beginning of `regex`; otherwise, `false`. - public func starts(with regex: R) -> Bool + public func starts(with regex: some RegexComponent) -> Bool } ``` @@ -323,7 +323,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: The regex to remove from this collection. /// - Returns: A new subsequence containing the elements of the collection /// that does not match `prefix` from the start. - public func trimmingPrefix(_ regex: R) -> SubSequence + public func trimmingPrefix(_ regex: some RegexComponent) -> SubSequence } extension RangeReplaceableCollection @@ -331,7 +331,7 @@ extension RangeReplaceableCollection { /// Removes the initial elements that matches the given regex. /// - Parameter regex: The regex to remove from this collection. - public mutating func trimPrefix(_ regex: R) + public mutating func trimPrefix(_ regex: some RegexComponent) } ``` @@ -364,7 +364,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: The regex to search for. /// - Returns: A range in the collection of the first occurrence of `regex`. /// Returns `nil` if `regex` is not found. - public func firstRange(of regex: R) -> Range? + public func firstRange(of regex: some RegexComponent) -> Range? } ``` @@ -387,7 +387,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: The regex to search for. /// - Returns: A collection or ranges in the receiver of all occurrences of /// `regex`. Returns an empty collection if `regex` is not found. - public func ranges(of regex: R) -> some Collection> + public func ranges(of regex: some RegexComponent) -> some Collection> } ``` @@ -399,17 +399,17 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Parameter regex: The regex to search for. /// - Returns: The first match of `regex` in the collection, or `nil` if /// there isn't a match. - public func firstMatch(of regex: R) -> Regex.Match? + public func firstMatch(of regex: R) -> Regex.Match? /// Match a regex in its entirety. /// - Parameter r: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. - public func wholeMatch(of regex: R) -> Regex.Match? + public func wholeMatch(of regex: R) -> Regex.Match? /// Match part of the regex, starting at the beginning. /// - Parameter r: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. - public func prefixMatch(of regex: R) -> Regex.Match? + public func prefixMatch(of regex: R) -> Regex.Match? } ``` @@ -420,7 +420,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// Returns a collection containing all matches of the specified regex. /// - Parameter regex: The regex to search for. /// - Returns: A collection of matches of `regex`. - public func matches(of regex: R) -> some Collection> + public func matches(of regex: R) -> some Collection.Match> } ``` @@ -484,8 +484,8 @@ extension RangeReplaceableCollection where SubSequence == Substring { /// sequence matching `regex` to replace. Default is `Int.max`. /// - Returns: A new collection in which all occurrences of subsequence /// matching `regex` in `subrange` are replaced by `replacement`. - public func replacing( - _ r: R, + public func replacing( + _ r: some RegexComponent, with replacement: Replacement, subrange: Range, maxReplacements: Int = .max @@ -500,8 +500,8 @@ extension RangeReplaceableCollection where SubSequence == Substring { /// sequence matching `regex` to replace. Default is `Int.max`. /// - Returns: A new collection in which all occurrences of subsequence /// matching `regex` are replaced by `replacement`. - public func replacing( - _ r: R, + public func replacing( + _ r: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max ) -> Self where Replacement.Element == Element @@ -513,8 +513,8 @@ extension RangeReplaceableCollection where SubSequence == Substring { /// - replacement: The new elements to add to the collection. /// - maxReplacements: A number specifying how many occurrences of the /// sequence matching `regex` to replace. Default is `Int.max`. - public mutating func replace( - _ r: R, + public mutating func replace( + _ r: some RegexComponent, with replacement: Replacement, maxReplacements: Int = .max ) where Replacement.Element == Element @@ -534,7 +534,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { _ regex: R, subrange: Range, maxReplacements: Int = .max, - with replacement: (RegexMatch) throws -> Replacement + with replacement: (Regex.Match) throws -> Replacement ) rethrows -> Self where Replacement.Element == Element /// Returns a new collection in which all occurrences of a sequence matching @@ -550,7 +550,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { public func replacing( _ regex: R, maxReplacements: Int = .max, - with replacement: (RegexMatch) throws -> Replacement + with replacement: (Regex.Match) throws -> Replacement ) rethrows -> Self where Replacement.Element == Element /// Replaces all occurrences of the sequence matching the given regex with @@ -564,7 +564,7 @@ extension RangeReplaceableCollection where SubSequence == Substring { public mutating func replace( _ regex: R, maxReplacements: Int = .max, - with replacement: (RegexMatch) throws -> Replacement + with replacement: (Regex.Match) throws -> Replacement ) rethrows where Replacement.Element == Element } ``` @@ -608,8 +608,8 @@ extension BidirectionalCollection where SubSequence == Substring { /// `true`, only nonempty subsequences are returned. /// - Returns: A collection of substrings, split from this collection's /// elements. - public func split( - by separator: R, + public func split( + by separator: some RegexComponent, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true ) -> some Collection From 3010ca7a92a048b774276b0ef91e076a1757df2e Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 12:49:18 -0500 Subject: [PATCH 5/6] Update documentation parameters --- Documentation/Evolution/StringProcessingAlgorithms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index 3976e4e8b..9cb31c75b 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -402,12 +402,12 @@ extension BidirectionalCollection where SubSequence == Substring { public func firstMatch(of regex: R) -> Regex.Match? /// Match a regex in its entirety. - /// - Parameter r: The regex to match against. + /// - Parameter regex: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. public func wholeMatch(of regex: R) -> Regex.Match? /// Match part of the regex, starting at the beginning. - /// - Parameter r: The regex to match against. + /// - Parameter regex: The regex to match against. /// - Returns: The match if there is one, or `nil` if none. public func prefixMatch(of regex: R) -> Regex.Match? } From 21e68dbe0dfd69f317b3eda782fea0f72e33b2d7 Mon Sep 17 00:00:00 2001 From: Nate Cook Date: Fri, 22 Apr 2022 16:32:30 -0500 Subject: [PATCH 6/6] split(by:) -> split(separator:) --- Documentation/Evolution/StringProcessingAlgorithms.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/Evolution/StringProcessingAlgorithms.md b/Documentation/Evolution/StringProcessingAlgorithms.md index 9cb31c75b..0af2e2f5d 100644 --- a/Documentation/Evolution/StringProcessingAlgorithms.md +++ b/Documentation/Evolution/StringProcessingAlgorithms.md @@ -588,7 +588,7 @@ extension Collection where Element: Equatable { /// - Returns: A collection of subsequences, split from this collection's /// elements. public func split( - by separator: C, + separator: C, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true ) -> some Collection where C.Element == Element @@ -609,7 +609,7 @@ extension BidirectionalCollection where SubSequence == Substring { /// - Returns: A collection of substrings, split from this collection's /// elements. public func split( - by separator: some RegexComponent, + separator: some RegexComponent, maxSplits: Int = Int.max, omittingEmptySubsequences: Bool = true ) -> some Collection