swiftlang / swift

The Swift Programming Language
https://swift.org
Apache License 2.0
67.52k stars 10.35k forks source link

Need a new syntax candy for OptionSet to check whether it contains an non-empty component. #68539

Open ShikiSuen opened 1 year ago

ShikiSuen commented 1 year ago

Motivation This is to solve any confusions provided by OptionSet.contains() in case that the given parameter is empty. See: https://github.com/apple/swift/issues/68537

By default, OptionSet.contains([]) always return true regardless whether itself is empty. However, this only makes sense in mathematics.

Solution

  1. State how OptionSet.contains() behaves with empty parameters in documentation.

  2. Add the following method.

extension OptionSet where Self == Self.Element {
  /// Returns a Boolean value that indicates whether a given element is a
  /// member of the option set.
  ///
  /// This example uses the `containsEntity(_:)` method to check whether
  /// next-day shipping is in the `availableOptions` instance.
  ///
  ///     let availableOptions = ShippingOptions.express
  ///     if availableOptions.containsEntity(.nextDay) {
  ///         print("Next day shipping available")
  ///     }
  ///     // Prints "Next day shipping available"
  ///
  /// - Parameter member: The element to look for in the option set.
  /// - Remark: This is different than contains() when dealing with
  ///   an empty parameter: In such case, if `self` is empty, then this
  ///   returns `true`; If `self` is not empty, then this returns `false`.
  /// - Returns: `true` if the option set contains `member`; otherwise,
  ///   `false`. See "Remark" section for important intel regarding the
  ///   difference from `.contains()`.
  @inlinable public func containsEntity(_ member: Self) -> Bool {
    guard member.isEmpty else { return contains(member) }
    return isEmpty
  }
}
stephentyrone commented 1 year ago

My initial reaction is that this API does not carry its weight as sugar. Can you supply a couple examples of use sites to explain how you would like to use it and why it's better than other alternatives?

ShikiSuen commented 1 year ago

@stephentyrone The following example will throw wrong result if the incoming NSEvent has no device-independent modifier flags:

import AppKit

func handle(event: NSEvent, sender: Any?) -> Bool {
    let mustBeAnyOfTheseFlags: NSEvent.ModifierFlags = [.shift, .option]
    return mustBeAnyOfTheseFlags.contains(event.modifierFlags.intersection(.deviceIndependentFlagsMask))
}

This is the occasion when .containsEntity() does its job.

AnthonyLatsis commented 1 year ago

Do you mind if I extract the documentation hole into a new issue since it can be dealt with separately?

ShikiSuen commented 1 year ago

@AnthonyLatsis I don't mind.