<div dir="ltr">On Sat, Nov 11, 2017 at 12:15 AM, Brent Royal-Gordon via swift-evolution <span dir="ltr"><<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>></span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><span class="">> 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.<br>
<br>
<br>
</span>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.<br>
<br>
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.<br>
<br>
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.<br>
<br>
public protocol ValueEnumerable {<br>
associatedtype AllValues: BidirectionalCollection where AllValues.Element == Self<br>
static var allValues: AllValues { get }<br>
}<br>
<br>
// The compiler automatically does `typealias AllValues = DefaultCaseCollection<Self>` if the<br>
// conformance is on the original declaration, the type is compatible (e.g. no associated values),<br>
// and a different type is neither explicitly specified nor inferred. That will cause this default<br>
// implementation to be used:<br>
extension ValueEnumerable where AllValues == DefaultCaseCollection<Self> {<br>
public static var allValues: DefaultCaseCollection<Self> {<br>
return DefaultCaseCollection(<wbr>unsafeForEnum: Self.self)<br>
}<br>
}<br>
<br>
public struct DefaultCaseCollection<Enum>: RandomAccessCollection {<br>
public var startIndex: Int { return 0 }<br>
public let endIndex: Int<br>
<br>
public init(unsafeForEnum _: Enum.Type) {<br>
endIndex = _countCaseValues(Enum.self)<br>
}<br>
<br>
public subscript(i: Int) -> Enum {<br>
precondition(indices.contains(<wbr>i), "Case index out of range")<br>
return Builtin.reinterpretCast(i) as Enum<br>
}<br>
}<br></blockquote><div><br></div><div>Nit: if you want to call it `ValueEnumerable`, then this should be `DefaultValueCollection`.</div><div><br></div><div>More generally though, I can see the appeal of allowing `Int` to conform to `ValueEnumerable`, but I've got a feeling that this is headed rapidly in the direction of overengineering a feature without a good rationale for the additional complexity. The stated use case is to enumerate the cases of an enum, and any additional complexity above that should stand on its own merit. I disagree quite vehemently that protocols should be "as general as possible"; rather, they exist to enable useful generic algorithms and should be as _useful_ as possible. There is a happy medium beyond which overengineering the design makes a protocol markedly less usable/approachable for the sake of enabling rare functionality.</div><div><br></div><div>Along the lines of user ergonomics, I would advocate for as many enums as possible to conform without explicit opt-in. It's true that we are moving away from such magical designs, and for good reason, but the gain here of enums Just Working(TM) for such a long-demanded feature has, I would argue, more benefits than drawbacks. To my mind, the feature is a lot like `RawRepresentable` in several ways, and it would be defensible for an equal amount of magic to be enabled for it.</div><div><br></div></div></div></div>