> Personally I like the flexibility provided by the associatedtype, but I also
> recognize it won't be incredibly useful for enums — more so if we wanted to
> provide e.g. UInt8.allValues, whose ideal implementation might be "return
> 0...UInt8.max". So I could see allowing allValues to be any sequence or
> collection, but flexibility for allCases might be less important. Others
> should weigh in here.
I think we should allow any `Collection` (option 3), and I think we should have
a `DefaultCaseCollection<Enum>` type in the standard library which encapsulates
the interaction with the runtime.
A `Collection` is good because `Sequence` doesn't provide all of the guarantees
we want—`allValues` should never be single-pass and it should always be
possible to resume iteration at an earlier point, which `Sequence` doesn't
guarantee. (It probably makes sense to use `BidirectionalCollection`, actually;
I'm less sure about `RandomAccessCollection`.) At the same time, an `Array`
ties us to all sorts of things that are unnecessary at best and harmful at
worst, like a heap allocation. It also forces us into integer indices, which
may be suboptimal for certain use cases (particularly if we later want to
support associated values). And it prevents us from making types like integers
conform, which I think would be a good idea.
Meanwhile, encapsulating the runtime machinery in `DefaultCaseCollection` gives
users an escape hatch: If the enum you want to use doesn't conform to
`ValueEnumerable`, but you're certain it's compatible, you can construct a
`DefaultCaseCollection` for it. `DefaultCaseCollection` can be a
`RandomAccessCollection` with `Int` indices, making it convenient to use, but
at the same time, it's *not* an array, so it doesn't have to allocate storage
or think about `NSArray` bridging. And it minimizes the complexity of what the
compiler needs to synthesize.
public protocol ValueEnumerable {
associatedtype AllValues: BidirectionalCollection where
AllValues.Element == Self
static var allValues: AllValues { get }
}
// The compiler automatically does `typealias AllValues =
DefaultCaseCollection<Self>` if the
// conformance is on the original declaration, the type is compatible
(e.g. no associated values),
// and a different type is neither explicitly specified nor inferred.
That will cause this default
// implementation to be used:
extension ValueEnumerable where AllValues ==
DefaultCaseCollection<Self> {
public static var allValues: DefaultCaseCollection<Self> {
return DefaultCaseCollection(unsafeForEnum: Self.self)
}
}
public struct DefaultCaseCollection<Enum>: RandomAccessCollection {
public var startIndex: Int { return 0 }
public let endIndex: Int
public init(unsafeForEnum _: Enum.Type) {
endIndex = _countCaseValues(Enum.self)
}
public subscript(i: Int) -> Enum {
precondition(indices.contains(i), "Case index out of
range")
return Builtin.reinterpretCast(i) as Enum
}
}
--
Brent Royal-Gordon
Architechies
_______________________________________________
swift-dev mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-dev