Is a set a sequence?

“Only a handful” seems like a gross exaggeration, though perhaps you have large hands. If we return to your proposed criteria, being whether someone can think of a reasonable use for an API, then here are the parts of Sequence that I had no trouble thinking of a use for during a quick scan of the documentation:

// Creating an Iterator
func makeIterator() // and use in for-in loops

// Finding Elements
func contains(Self.Element)
func contains(where: (Self.Element) -> Bool)
func first(where: (Self.Element) -> Bool)
func min()
func min(by: (Self.Element, Self.Element) -> Bool)
func max()
func max(by: (Self.Element, Self.Element) -> Bool)

// Selecting Elements
func filter((Self.Element) -> Bool)
func prefix(Int)
func suffix(Int)

// Excluding Elements
func dropFirst()
func dropFirst(Int)
func dropLast()
func dropLast(Int)

// Transforming a Sequence
func map<T>((Self.Element) -> T)
func compactMap<ElementOfResult>((Self.Element) -> ElementOfResult?)
func flatMap<SegmentOfResult>((Self.Element) -> SegmentOfResult)
func reduce<Result>(Result, (Result, Self.Element) -> Result)
func reduce<Result>(into: Result, (inout Result, Self.Element) -> ())
var lazy: LazySequence<Self>

// Iterating Over a Sequence's Elements
func forEach((Self.Element) -> Void)
var underestimatedCount: Int

// Sorting Elements
func sorted()
func sorted(by: (Self.Element, Self.Element) -> Bool)

// Splitting and Joining Elements
func joined()
func joined(separator: String)
func joined<Separator>(separator: Separator)

and here are the ones I couldn't immediately think of a use for (though perhaps someone else could):

func prefix(while: (Self.Element) -> Bool)
func drop(while: (Self.Element) -> Bool)
func enumerated()
func reversed()
func split(separator: Self.Element, maxSplits: Int, omittingEmptySubsequences: Bool)
func split(maxSplits: Int, omittingEmptySubsequences: Bool, whereSeparator: (Self.Element) -> Bool)
func elementsEqual<OtherSequence>(OtherSequence)
func elementsEqual<OtherSequence>(OtherSequence, by: (Self.Element, OtherSequence.Element) -> Bool)
func starts<PossiblePrefix>(with: PossiblePrefix)
func starts<PossiblePrefix>(with: PossiblePrefix, by: (Self.Element, Self.Element) -> Bool)
func lexicographicallyPrecedes<OtherSequence>(OtherSequence)
func lexicographicallyPrecedes<OtherSequence>(OtherSequence, by: (Self.Element, Self.Element) -> Bool)

of which elementsEqual is the only one that seems obviously harmful to me, and the only one that I've seen evidence of harm.

I've posted many reasons why it may not be desirable, useful or worthwhile, and I'm starting to feel like they've been mostly ignored. Primarily, I've said I'm sympathetic to the view that the Sequence and Collection conformances should have been tucked away in a view on Set and Dictionary, but I don't really understand the desire to split the protocol hierarchy, leaving much of the order-dependent functionality (e.g. for-in loops) available on Set and Dictionary, while removing others. If someone thinks of a type with a different useful/useless divide to the one that I've posted above, should Sequence be split into 4 different protocols?

The rich history of String-as-Collection in Swift shows that even the violation of semantics/reasonable expectations may not be sufficient to split the protocol hierarchy. String even had a quarantined Collection of characters view that has parallels to a possible solution here. Pragmatism won out in that case, because it was considered too onerous to write .characters everywhere, even though generic algorithms can now break badly on some Strings. The situation here seems a lot less serious than that to me, since I don't believe Set or Dictionary violate any semantics/reasonable expectations of Sequence or Collection, and I can't think of any generic algorithm that would break badly when given one (though everyone accepts that you may write an algorithm that is not particular useful).

2 Likes