swiftlang / swiftlang/swift-experimental-string-processing

When passing a collection to `Set.contains(_:)`, forward to `Set.isSuperset(of:)`

Open
#796 4 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Swift
Stars
308
Forks
52
Avg merge
12h 45m
Merged PRs (30d)
2

Description

Motivation

There is a confusing behavior of Set.contains(_:) when passing a collection that contains more than one element:

let s: Set = [1, 2, 3]
s.contains([1]) // true
s.contains([1] as Set) // true
s.contains([1, 2]) // false 🤨
s.contains([1, 2] as Set) // false 🤨
s.contains([1, 2, 3]) // false 🤨
s.contains([1, 2, 3] as Set) // false 🤨

I think this confusion is introduced by a Collection extension in the _StringProcessing module:

extension Collection where Self.Element : Equatable {

    /// Returns a Boolean value indicating whether the collection contains the
    /// given sequence.
    /// - Parameter other: A sequence to search for within this collection.
    /// - Returns: `true` if the collection contains the specified sequence,
    /// otherwise `false`.
    @available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
    public func contains<C>(_ other: C) -> Bool where C : Collection, Self.Element == C.Element
}

I understand that this behavior is caused by the Set internally storing its elements in a random order (as viewed from outside), so checking whether “[…] the collection contains the given sequence” is not really a useful thing to do on a Set, making this method pretty much useless there.

Proposed solution

To make this method useful on Set, it could simply forward to Set.isSuperset(of:), which is what I would have expected in the first place:

s.isSuperset(of: [1, 2]) // true
s.isSuperset(of: [1, 2] as Set) // true
s.isSuperset(of: [1, 2, 3]) // true
s.isSuperset(of: [1, 2, 3] as Set) // true

While maybe not strictly correct in the sense of the documentation that “[…] the collection contains the given sequence” because the order of elements as passed is irrelevant, I think it is more useful because the behavior is at least deterministic.

Alternatives considered

None really. Do nothing and keep the behavior as-is, I guess.

Additional information

I posted on the Swift forum about this and received only one reply that agreed with the confusing behavior.

Here’s the thread: Passing a Collection to Set.contains(_:) leads to confusing results

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the Collection.contains(_:) extension in the StringProcessing module and compare its sequence semantics with Set.isSuperset(of:). Confirm the desired behavior for single- and multiple-element collections, then add coverage showing that Set.contains(:) behaves deterministically for these cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
swift
Domain
api
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.