<html><head><meta http-equiv="Content-Type" content="text/html charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space;" class=""><br class=""><div><blockquote type="cite" class=""><div class="">On Apr 15, 2016, at 9:00 PM, Jacob Bandes-Storch via swift-evolution &lt;<a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="">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.</div><div class=""><br class=""></div><div class="">Core Team / Standard Library Team feedback would be particularly welcome here, because we want this feature to mesh well with the goals &amp; 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.</div><div class=""><br class=""></div><div class="">Background reading:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; • 2015-12-08: "List of all Enum values (for simple enums)" —&nbsp;<a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/10064</a></div><div class="">&nbsp; &nbsp; • 2015-12-21: "Proposal: Enum 'count' functionality"&nbsp;<a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/644" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/644</a></div><div class="">&nbsp; &nbsp; • 2016-01-17: "Draft Proposal: count property for enum types"&nbsp;<a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/3678</a></div><div class="">&nbsp; &nbsp; • 2016-01-18: "Pre-proposal: CaseEnumerable protocol (derived<span style="white-space:pre-wrap" class="">&nbsp;</span>collection of enum cases)" at <a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/3701</a></div><div class="">&nbsp; &nbsp; • 2016-01-20: My subsequent proposal PR #114:&nbsp;<a href="https://github.com/apple/swift-evolution/pull/114" target="_blank" class="">https://github.com/apple/swift-evolution/pull/114</a></div><div class=""><br class=""></div><div class="">A lot has happened since then:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; • 2016-03-03: "[Manifesto] Completing Generics" <a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/8484</a></div><div class="">&nbsp; &nbsp; • 2016-03-03: "[Accepted with modifications] SE-0023 API Design<span style="white-space:pre-wrap" class="">&nbsp;</span>Guidelines"&nbsp;<a href="http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585" target="_blank" class="">http://thread.gmane.org/gmane.comp.lang.swift.evolution/8585</a> &amp; <a href="http://apple.github.io/swift-internals/api-design-guidelines/" target="_blank" class="">http://apple.github.io/swift-internals/api-design-guidelines/</a></div><div class="">&nbsp; &nbsp; • 2016-03-09: Brent's proposal PR #199:&nbsp;<a href="https://github.com/apple/swift-evolution/pull/199" target="_blank" class="">https://github.com/apple/swift-evolution/pull/199</a></div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">I believe the community is in agreement about the following:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; • The "allValues" behavior should be provided by conformance to some protocol, named ValueEnumerable or ValuesEnumerable or similar.</div><div class="">&nbsp; &nbsp; • The compiler should derive an allValues implementation for "simple" enums (those without associated values).</div><div class=""><br class=""></div><div class="">There are a couple things which we must still decide:</div><div class=""><br class=""></div><div class=""><b class="">### Should the ValueEnumerable protocol expose the allValues property, or should it be an empty protocol like ErrorType (ErrorProtocol)? If exposed, what is its type?</b></div></div></div></blockquote><div><br class=""></div># General Remarks<br class=""><div><br class=""></div><div>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:</div><div><br class=""></div><div>protocol ValueEnumerable {</div><div>&nbsp; associatedtype ValueCollection : Collection where ValueCollection.Iterator.Element == Self</div><div>&nbsp; static var allValues: ValueCollection</div><div>}</div><div><br class=""></div><div>…and that this concept should simply *wait* for that feature to be available before going into the standard library.&nbsp;</div><div><br class=""></div><div>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.:</div><div><br class=""></div><div>&nbsp; struct AxisPolicy3D&lt;Policy:protocol&lt;Equatable,ValueEnumerable&gt;&gt; {</div><div>&nbsp; &nbsp; var x: Policy</div><div>&nbsp; &nbsp; var y: Policy</div><div>&nbsp; &nbsp; var z: Policy</div><div>&nbsp; }</div><div><br class=""></div><div>&nbsp; extension AxisPolicy3D : ValueEnumerable {</div><div><br class=""></div><div>&nbsp; &nbsp; static let allValues: ValueCollection = product(Policy.allValues,Policy.allValues,Policy.allValues).lazy.map() {&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; (x,y,z)&nbsp;</div><div>&nbsp; &nbsp; &nbsp; &nbsp; in</div><div>&nbsp; &nbsp; &nbsp; &nbsp;&nbsp;AxisPolicy3D(x: x, y: y, z: z)</div><div>&nbsp; &nbsp; }</div><div><br class=""></div><div>&nbsp; }</div><div><br class=""></div><div>…and similar, wherein the cost of *requiring* an array here could become rather large.</div><div><br class=""></div><div>But I have a couple general concerns here:</div><div><br class=""></div><div># Resiliency&nbsp;</div><div><br class=""></div><div>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).&nbsp;</div><div><br class=""></div><div>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.</div><div><br class=""></div><div># Scope (or: Beyond Simple Enumerations?)</div><div><br class=""></div><div>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.&nbsp;</div><div><br class=""></div><div>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!&nbsp;</div><div><br class=""></div><div>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.</div><div><br class=""></div><div># Ordering</div><div><br class=""></div><div>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.</div><div><br class=""></div><div>If you are willing to go there, you can include something like&nbsp;</div><div><br class=""></div><div>&nbsp; protocol ComparableValueEnumerable : Comparable, ValueEnumerable {</div><div><br class=""></div><div>&nbsp; &nbsp; associatedtype OrderedValueCollection: Collection where OrderedValueCollection.Iterator.Element == Self</div><div>&nbsp; &nbsp; static var allValuesInOrder: OrderedValueCollection { get }</div><div>&nbsp; }</div><div><br class=""></div><div>…which would be the same as the basic protocol but guarantees the values are enumerated in order.</div><div><br class=""></div><div>For compiler-synthesized enumerations for suitable types this could simply return `allValues`.</div><div><br class=""></div><div># Synthesis</div><div><br class=""></div><div>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.</div><div><br class=""></div><div>EG if limited to simple enumerations I could easily see the compiler’s synthesis rule be:</div><div><br class=""></div><div>- user supplied `allValues`? go with that!</div><div>- otherwise:</div><div>&nbsp; - define `allValues` to be equivalent-to `Range&lt;RawValue&gt;.lazy.map() { Self(rawValue: $0)! }` when possible (*)</div><div>&nbsp; - otherwise, define `allValues` as an array</div><div><br class=""></div><div>…as something along those lines seems reasonable to implement and to understand as a user.</div><div><br class=""></div><div>(*) “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.</div><div><br class=""></div><div># Other Remarks</div><div><br class=""></div><div>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).&nbsp;</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">If allValues were exposed as part of the protocol, then the generic constraint &lt;T: ValueEnumerable&gt; could be used meaningfully, i.e. you could write/use "T.allValues".<br class=""></div><div class=""><br class=""></div><div class="">On the other hand, the limitations of the current generics system don't allow "<b class="">associatedtype ValueCollection: Collection where ValueCollection.Iterator.Element == Self</b>". Doug's&nbsp;<i class="">Completing Generics</i>&nbsp;manifesto included "<i class="">Arbitrary requirements in protocols</i>", 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.</div><div class=""><br class=""></div><div class="">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]".</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">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:</div><div class=""><br class=""></div><div class=""></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp; struct MyType: ValueEnumerable { }</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp; // error: type 'MyType' does not conform to protocol 'ValueEnumerable'</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp; // note: protocol requires property 'allValues' with type '[MyType]'</font></div><div class=""><font face="monospace, monospace" class="">&nbsp; &nbsp; // <b class="">note: implementation of allValues cannot be automatically derived for a non-enum type</b></font></div><div class=""><br class=""></div><div class=""><b class="">### Should allValues implementations be derived for Comparable enums? What if the sorted order does/doesn't match the source order?</b></div><div class=""><br class=""></div><div class="">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.)</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Thoughts welcome.</div><br clear="all" class=""><div class=""><div class=""><div dir="ltr" class=""><div class="">Jacob<br class=""></div></div></div></div>
</div>
_______________________________________________<br class="">swift-evolution mailing list<br class=""><a href="mailto:swift-evolution@swift.org" class="">swift-evolution@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-evolution<br class=""></div></blockquote></div><br class=""></body></html>