On Sat, Nov 11, 2017 at 12:15 AM, Brent Royal-Gordon via swift-evolution < swift-evolut...@swift.org> wrote:
> > 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 > } > } > Nit: if you want to call it `ValueEnumerable`, then this should be `DefaultValueCollection`. 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. 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.
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev