<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 Nov 6, 2017, at 2:54 AM, Jacob Bandes-Storch via swift-dev <<a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a>> wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class=""><div class="">Over a year ago, we discussed adding a magic "allValues"/"allCases" static property on enums with a compiler-derived implementation. The original <a href="https://github.com/apple/swift-evolution/pull/114" class="">proposal PR</a> has been reopened for Swift 5 after languishing for a while, and I'd like to revisit it and make some changes before it goes up for formal review.</div></div><div class=""><br class=""></div><div class="">Prior discussion: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html" class="">https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html</a> (good luck finding the rest of the thread if you weren't on the list at the time...)</div><div class=""><br class=""></div><div class="">[cc'd swift-dev for importer/availability-related topics below.]</div><div class=""><br class=""></div><div class=""><b class="">**Naming**</b><br class=""></div><div class=""><b class=""><br class=""></b></div><div class="">Given the complexity gap between a simple enumeration of cases and full support for non-enum types and associated values (which we don't intend to support with this proposal), I think it might be a good idea to adopt the names <b class="">CaseEnumerable/allCases</b> instead of ValueEnumerable/allValues.</div><div class=""><br class=""></div><div class="">The original proposal didn't expose allValues as a requirement, for fear of unduly restricting its type. However, if the protocol's scope is more limited, <b class="">static var allCases</b> can be exposed as a requirement since the implementations are not likely to be complex. Furthermore...</div><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">**Generics**</b></div><div class=""><br class=""></div><div class="">Since <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md" class="">SE-0142</a> was implemented in Swift 4, we now have more expressive options for the protocol requirements:</div><div class=""><br class=""></div><div class=""> // 1 - array only</div><div class=""> protocol CaseEnumerable {</div><div class=""> static var allCases: [Self] { get }</div><div class=""> }</div><div class=""><br class=""></div><div class=""> // 2 - any sequence</div><div class=""><div class=""> protocol CaseEnumerable {</div><div class=""> associatedtype <b class="">CaseSequence</b>: Sequence where CaseSequence.Element == Self</div><div class=""> static var <b class="">allCases</b>: CaseSequence { get }</div><div class=""> }</div></div><div class=""><br class=""></div><div class=""> // 3 - any collection</div><div class=""><div class=""> protocol CaseEnumerable {</div><div class=""> associatedtype <b class="">CaseCollection</b>: Collection where CaseCollection.Element == Self</div><div class=""> static var <b class="">allCases</b>: CaseCollection { get }</div><div class=""> }</div></div><div class=""><br class=""></div><div class="">This restricts the CaseEnumerable protocol to be used as a generic constraint, but that'd be true even with a plain array because of the Self type.</div><div class=""><br class=""></div><div 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.</div></div></div></blockquote><div><br class=""></div><div>Generalizing the result type is probably a good thing to do before we finalize the user-facing component of this. I wrote the interface with an Array at the time because it was easy and because the first implementation of this tried to directly synthesize an array literal expression.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""><br class=""></div><div class=""><b class="">**Implementation strategy and edge cases**</b></div><div class=""><div class=""><br class=""></div><div class=""><a href="https://twitter.com/CodaFi_/status/920132464001024001" class="">Last year</a>, Robert Widmann put together an implementation of CaseEnumerable: <a href="https://github.com/apple/swift/compare/master...CodaFi:ace-attorney" class="">https://github.com/apple/swift/compare/master...CodaFi:ace-attorney</a> </div><div class="">I'd love to hear from anyone more familiar with the code whether there's anything we'd want to change about this approach.</div></div><div class=""><br class=""></div><div class="">A few tricky situations have been brought to my attention:</div><div class=""><br class=""></div><div class="">- Enums <b class="">imported from C/Obj-C</b> headers. Doug Gregor writes: <i class="">"<span style="font-size:12.8px" class="">The autogenerated allValues would only be able to </span><span class="gmail-il" style="font-size:12.8px">list</span><span style="font-size:12.8px" class=""> the </span><span class="gmail-il" style="font-size:12.8px">enum</span><span style="font-size:12.8px" class=""> </span><span class="gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px" class=""> it knows about from the header it was compiled with. If the library changes to add </span><span class="gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px" class=""> in the future (which, for example, Apple frameworks tend to do), those wouldn’t be captured in allValues.</span>"</i></div><div class=""><br class=""></div><div class="">My understanding of the runtime/importer is very shallow, but with the current metadata-based strategy, I suspect imported enums couldn't be supported at all, or if they could, the metadata would be generated at import time rather than loaded dynamically from the library, which naturally wouldn't behave the same way when you drop in an upgraded version of the library. Is that correct?</div></div></div></blockquote><div><br class=""></div><div>Exactly. Today, we don’t write out metadata for foreign enumerations and it would be interesting to see how that would look if we decide to do so in the future. Either way, leaving it up to the authors of Objective-C/C to create a Swift wrapper to get access to this protocol is a little unfortunate, but not that big a deal ultimately.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">- Enums with <b class="">availability</b> annotations on some cases. Doug Gregor writes: <i class="">"<span style="font-size:12.8px" class="">if I have a case that’s only available on macOS 10.12 and newer, it probably shouldn’t show up if I use allValues when running on macOS 10.11.</span>"</i></div><div class=""><br class=""></div><div class=""><div class="">If we fetch cases from the enum metadata, does this "just work" since the metadata will be coming from whichever version of the library is loaded at runtime? If not, is it at least <i class="">possible</i> to extract availability info from the metadata? Finally, if not, should we try to synthesize an implementation that uses #available checks, or just refuse to synthesize allCases?</div></div></div></div></blockquote><br class=""><div>Up front, I want to point out that using a metadata-based approach will still enumerate unavailable cases.</div><div><br class=""></div><div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""><span style="color: #ba2da2" class="">enum</span> Foo {</div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">case</span> bar</div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">case</span> baz</div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">@available</span>(*, unavailable)</div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class=""> <span style="color: #ba2da2" class="">case</span> quux</div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class="">}</div><div style="margin: 0px; line-height: normal; background-color: rgb(255, 255, 255); min-height: 14px;" class=""><br class=""></div><div style="margin: 0px; line-height: normal; font-family: Menlo; background-color: rgb(255, 255, 255);" class="">print(unsafeBitCast(Int8(<span style="color: #272ad8" class="">2</span>), to: Foo.<span style="color: #ba2da2" class="">self</span>))</div><div class=""><br class=""></div></div><div>Whether or not this is desirable is a point that will need to be fleshed out. My original implementation did take conditional availability into account and tried to group together similarly available cases - but (modulo how weird that was) that implementation also assumed that “allCases” meant that the resulting collection excluded unavailable cases. I don’t lean one way or the other on this issue but I would like it settled in the proposal. On the one hand, it will now be possible to synthesize unavailable enum cases, on the other things like switches over enums with unavailable cases must still handle the unavailable case.</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class="">- Should it be possible to add a CaseEnumerable conformance in an <b class="">extension</b>? My thinking is: we want to make sure the metadata is coming from the module that defines the enum, so we could restrict autogeneration of allCases to that same module. (That is, it wouldn't be possible to synthesize allCases for a CaseEnumerable extension on an enum from another module.) Although, it may be that I am missing something and this restriction isn't actually necessary. The question to answer is: in exactly which circumstances can the implementation be synthesized?</div></div></div></blockquote><div><br class=""></div><div>The metadata is there across framework boundaries. </div><div><br class=""></div><div>From an aesthetic POV, I’m partial to Slava’s idea that it shouldn’t ever be synthesized (because I’m lazy, go figure) which makes the protocol opt-in. But this also means users may have to go through multiple rounds of “why doesn't X framework/overlay export CaseEnumerable implementations” with authors. I think a good middle ground is to make conformance opt-in then teach Mirror to recognize when it’s reflecting a metatype for a simple enumeration to provide a getter for its cases.</div><div><br class=""></div><div>~Robert Widmann</div><br class=""><blockquote type="cite" class=""><div class=""><div dir="ltr" class=""><div class=""><br class=""></div><div class=""><br class=""></div><div class="">Looking forward to hearing everyone's thoughts,</div><div class=""><div class="gmail_signature"><div dir="ltr" class=""><div class="">Jacob<br class=""></div></div></div></div>
</div>
_______________________________________________<br class="">swift-dev mailing list<br class=""><a href="mailto:swift-dev@swift.org" class="">swift-dev@swift.org</a><br class="">https://lists.swift.org/mailman/listinfo/swift-dev<br class=""></div></blockquote></div><br class=""></body></html>