The problem we have now is that everybody works with OptionSet
as if these constraints were required:
extension OptionSet where Self == Self.Element {
extension OptionSet where Self.RawValue : FixedWidthInteger {
They're not. But that's where all of the default extensions are defined, so they might as well be. And this giant thread illustrates that making a type a "collection" of itself is too hard to work with. OptionSet
's Element
should not be itself; it should be another type which represents a single option.
Without macros, we can still all stop conflating Option
s and Set
s thereof.
var shippingOptions = ShippingOption.Set.secondDayPriority
shippingOptions.formUnion(.init(.standard))
[ShippingOption.secondDay, .priority, .standard] == .init(shippingOptions) // true
enum ShippingOption: BitFlag<Int> {
case nextDay, secondDay
// this bit is cursed, don't use it
case priority = 3, standard
}
extension BitFlagRepresentableOptionSet<ShippingOption> {
// A static property should always return the enclosing type, unless named with a different type.
// The type of this is ShippingOption.Set. It belongs here, not in `ShippingOption`.
static let secondDayPriority = Self([ShippingOption.secondDay, .priority])
}
It's slightly uglier with more rawValues
and .init()
s sprinkled about, but consistent with sets of other enumerations. Some methods would be improved by copying the defaults from where Self == Self.Element
to:
extension OptionSet where
Element: RawRepresentable,
Element.RawValue: RawRepresentable<RawValue>
BitFlagRepresentable.Set
public extension RawRepresentable
where RawValue: RawRepresentable, RawValue.RawValue: FixedWidthInteger {
typealias Set = BitFlagRepresentableOptionSet<Self>
}
public struct BitFlagRepresentableOptionSet<BitFlagRepresentable: RawRepresentable>: OptionSet
where
BitFlagRepresentable.RawValue: RawRepresentable,
BitFlagRepresentable.RawValue.RawValue: FixedWidthInteger {
public typealias RawValue = BitFlagRepresentable.RawValue.RawValue
public init(rawValue: RawValue) {
self.rawValue = rawValue
}
public let rawValue: RawValue
}
// MARK: - public
public extension BitFlagRepresentableOptionSet {
init(_ bitFlagRepresentable: BitFlagRepresentable) {
self.init(rawValue: bitFlagRepresentable.rawValue.rawValue)
}
}
// MARK: - Array
public extension Array where
Element: RawRepresentable,
Element.RawValue: FixedWidthInteger & _ExpressibleByBuiltinIntegerLiteral {
init<OptionSet: Swift.OptionSet>(_ optionSet: OptionSet) where
OptionSet.RawValue == Element.RawValue {
self =
optionSet.rawValue == 0 ? []
: (
optionSet.rawValue.trailingZeroBitCount
..<
OptionSet.RawValue.bitWidth - optionSet.rawValue.leadingZeroBitCount
).compactMap {
.init(rawValue: optionSet.rawValue & 1 << $0)
}
}
}
public extension Array where
Element: RawRepresentable,
Element.RawValue: RawRepresentable,
Element.RawValue.RawValue: FixedWidthInteger & _ExpressibleByBuiltinIntegerLiteral {
init<OptionSet: Swift.OptionSet>(_ optionSet: OptionSet)
where OptionSet.RawValue == Element.RawValue.RawValue {
self = [BitFlag](optionSet).compactMap {
Element.RawValue(rawValue: $0.rawValue).flatMap(Element.init)
}
}
}
@michelf @davedelong Those spellings are confusing, but the language provides no safe way to do better. The relevant missing feature is the ability to use method syntax for setters, and its near-equivalent, named subscripts.
var style: NSWindow.StyleMask = []
style.contains[.borderless] = true
style.contains[.closable].toggle()
style.contains[.resizable] = style.contains(.closable)
public extension SetAlgebra {
var contains: ValueSubscript<Self, Element, Bool> {
mutating get {
.init(
&self,
get: Self.contains,
set: { set, element, newValue in
if newValue {
set.insert(element)
} else {
set.remove(element)
}
}
)
}
}
}
ValueSubscript is probably unsafe but I don't know how to make it fail.
/// An emulation of the missing Swift feature of named subscripts.
/// - Note: Argument labels are not supported.
public struct ValueSubscript<Root, Index, Value> {
public typealias Pointer = UnsafeMutablePointer<Root>
public typealias Get = (Root) -> (Index) -> Value
public typealias Set = (inout Root, Index, Value) -> Void
public var pointer: Pointer
public var get: Get
public var set: Set
}
public extension ValueSubscript {
init(
_ pointer: Pointer,
get: @escaping Get,
set: @escaping Set
) {
self.pointer = pointer
self.get = get
self.set = set
}
subscript(index: Index) -> Value {
get { get(pointer.pointee)(index) }
nonmutating set { set(&pointer.pointee, index, newValue) }
}
}