> On Apr 15, 2016, at 9:00 PM, Jacob Bandes-Storch via swift-evolution > <swift-evolution@swift.org> wrote: > > This discussion is about a proposal for API to enumerate/count all possible > values of a type, particularly enums. The goal is to address potential issues > with an old proposal, so it can go up for review soon. > > Core Team / Standard Library Team feedback would be particularly welcome > here, because we want this feature to mesh well with the goals & future > directions that are already in mind for Swift. If any details the core team > would like to see are missing from the proposal, please let us know now. > > Background reading: > > • 2015-12-08: "List of all Enum values (for simple enums)" — > http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064> > • 2015-12-21: "Proposal: Enum 'count' functionality" > http://thread.gmane.org/gmane.comp.lang.swift.evolution/644 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/644> > • 2016-01-17: "Draft Proposal: count property for enum types" > http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678> > • 2016-01-18: "Pre-proposal: CaseEnumerable protocol (derived collection > of enum cases)" at > http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701> > • 2016-01-20: My subsequent proposal PR #114: > https://github.com/apple/swift-evolution/pull/114 > <https://github.com/apple/swift-evolution/pull/114> > > A lot has happened since then: > > • 2016-03-03: "[Manifesto] Completing Generics" > http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484> > • 2016-03-03: "[Accepted with modifications] SE-0023 API Design > Guidelines" http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585 > <http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585> & > http://apple.github.io/swift-internals/api-design-guidelines/ > <http://apple.github.io/swift-internals/api-design-guidelines/> > • 2016-03-09: Brent's proposal PR #199: > https://github.com/apple/swift-evolution/pull/199 > <https://github.com/apple/swift-evolution/pull/199> > > Brent brought up some great points in his proposal, but ultimately closed the > PR in anticipation of further discussion. I'm sorry I haven't had much time > to put into this until now, but I'd like us to get the discussion going again. > > I believe the community is in agreement about the following: > > • The "allValues" behavior should be provided by conformance to some > protocol, named ValueEnumerable or ValuesEnumerable or similar. > • The compiler should derive an allValues implementation for "simple" > enums (those without associated values). > > There are a couple things which we must still decide: > > ### Should the ValueEnumerable protocol expose the allValues property, or > should it be an empty protocol like ErrorType (ErrorProtocol)? If exposed, > what is its type?
# General Remarks My 2c is that if this is to go in the standard library, it should be done “right”, which would be more like this version of it: protocol ValueEnumerable { associatedtype ValueCollection : Collection where ValueCollection.Iterator.Element == Self static var allValues: ValueCollection } …and that this concept should simply *wait* for that feature to be available before going into the standard library. The reason I say this is simply b/c it sounds like this proposal wants to be able to support more than simple enumerations, in which case having some flexibility in the representation seems appropriate. Consider e.g.: struct AxisPolicy3D<Policy:protocol<Equatable,ValueEnumerable>> { var x: Policy var y: Policy var z: Policy } extension AxisPolicy3D : ValueEnumerable { static let allValues: ValueCollection = product(Policy.allValues,Policy.allValues,Policy.allValues).lazy.map() { (x,y,z) in AxisPolicy3D(x: x, y: y, z: z) } } …and similar, wherein the cost of *requiring* an array here could become rather large. But I have a couple general concerns here: # Resiliency My understanding is that the design for resiliency vis-a-vis enumerations is meant to allow enumerations to have cases added in future revisions (and perhaps also private cases? I didn’t follow resiliency closely). If that’s right, and this protocol is supposed to go into the standard library, it might also need to address such issues. I have no help to offer and would love to be wrong about this point. # Scope (or: Beyond Simple Enumerations?) On the one hand, I don’t see any reason *not* to design the protocol so that it *could* be adopted by types other than simple enumerations. On the other hand, I think the value of having this in place for simple-enumerations is huge, and I’m more than a bit skeptical how often it’ll actually make sense for anything other than simple-enumerations—it’s easy to come with other types that could support it, I just think they’re somewhat rare in practice! To me, this leaves me thinking that trying to support non-simple enumerations is a nice-to-have, but something I’d be willing to drop if it posed any real difficulties. # Ordering I have mixed feelings here. For comparable values I think it’d be nice if `allValues` was ordered, but I think that gets in the way of synthesis if you also support types other than simple-enumerations; I am admittedly skeptical about trying to support such types, but there you have it. If you are willing to go there, you can include something like protocol ComparableValueEnumerable : Comparable, ValueEnumerable { associatedtype OrderedValueCollection: Collection where OrderedValueCollection.Iterator.Element == Self static var allValuesInOrder: OrderedValueCollection { get } } …which would be the same as the basic protocol but guarantees the values are enumerated in order. For compiler-synthesized enumerations for suitable types this could simply return `allValues`. # Synthesis Related to the above, compiler synthesis is great, but given the lack of similar synthesis for other types I wouldn’t want to require—or expect—too much here. EG if limited to simple enumerations I could easily see the compiler’s synthesis rule be: - user supplied `allValues`? go with that! - otherwise: - define `allValues` to be equivalent-to `Range<RawValue>.lazy.map() { Self(rawValue: $0)! }` when possible (*) - otherwise, define `allValues` as an array …as something along those lines seems reasonable to implement and to understand as a user. (*) “Possible” is something like `RawRepresentable`, the `RawValue` type is `Comparable`, the enumeration cases occupy a contiguous range of `RawValue` values, and (trickier!) if comparable, `Comparable` ordering is the same as the ordering derived-from the raw values. # Other Remarks I see the `CaseEnumerable` discussion in the other discussion. It’s certainly related, but it’s something with enough independent utility I wouldn’t want it to get “lost” in this topic (especially since I think this topic is a great feature for the language, but one that may be awhile coming). > > If allValues were exposed as part of the protocol, then the generic > constraint <T: ValueEnumerable> could be used meaningfully, i.e. you could > write/use "T.allValues". > > On the other hand, the limitations of the current generics system don't allow > "associatedtype ValueCollection: Collection where > ValueCollection.Iterator.Element == Self". Doug's Completing Generics > manifesto included "Arbitrary requirements in protocols", under the category > of "Minor extensions", which would remove this limitation. If this gets > implemented, I think it makes a lot of sense to use it here. > > Until then, though, we'd have to pick a concrete type for the collection. > Brent proposed that it be an Array, "static var allValues: [Self]". > > The biggest reason I didn't expose allValues on the protocol was that I > figured we'd want to allow for efficient implementations which wouldn't > require allocating storage for *all* the values (just the endpoints, for > instance), but could still count and iterate over them. > > Another question on the subject of exposing the property as a protocol > requirement: What should the diagnostics look like if it's missing? Maybe > something like this: > > struct MyType: ValueEnumerable { } > // error: type 'MyType' does not conform to protocol 'ValueEnumerable' > // note: protocol requires property 'allValues' with type '[MyType]' > // note: implementation of allValues cannot be automatically derived for > a non-enum type > > ### Should allValues implementations be derived for Comparable enums? What if > the sorted order does/doesn't match the source order? > > Brent has suggested the semantics of allValues should be such that for > Comparable types, allValues is guaranteed to be ordered. If that were the > case, we might not want to require the compiler to derive a ValueEnumerable > implementation, since the source order may not match the Comparable-sorted > order, and verifying this could overly complicate things. (I think I'm in > agreement here: having the values be ordered is a good implementation of the > principle of least surprise.) > > > Thoughts welcome. > > Jacob > _______________________________________________ > swift-evolution mailing list > swift-evolution@swift.org > https://lists.swift.org/mailman/listinfo/swift-evolution
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution