> 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-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to