<div dir="ltr"><br><div class="gmail_extra"><br><div class="gmail_quote">On Mon, Nov 6, 2017 at 6:54 PM, Jacob Bandes-Storch via swift-evolution <span dir="ltr">&lt;<a href="mailto:swift-evolution@swift.org" target="_blank">swift-evolution@swift.org</a>&gt;</span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div><div>Over a year ago, we discussed adding a magic &quot;allValues&quot;/&quot;allCases&quot; static property on enums with a compiler-derived implementation. The original <a href="https://github.com/apple/swift-evolution/pull/114" target="_blank">proposal PR</a> has been reopened for Swift 5 after languishing for a while, and I&#39;d like to revisit it and make some changes before it goes up for formal review.</div></div><div><br></div><div>Prior discussion: <a href="https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160411/015098.html" target="_blank">https://lists.<wbr>swift.org/pipermail/swift-<wbr>evolution/Week-of-Mon-<wbr>20160411/015098.html</a> (good luck finding the rest of the thread if you weren&#39;t on the list at the time...)</div><div><br></div><div>[cc&#39;d swift-dev for importer/availability-related topics below.]</div><div><br></div><div><b>**Naming**</b><br></div><div><b><br></b></div><div>Given the complexity gap between a simple enumeration of cases and full support for non-enum types and associated values (which we don&#39;t intend to support with this proposal), I think it might be a good idea to adopt the names <b>CaseEnumerable/allCases</b> instead of ValueEnumerable/allValues.</div><div><br></div><div>The original proposal didn&#39;t expose allValues as a requirement, for fear of unduly restricting its type. However, if the protocol&#39;s scope is more limited, <b>static var allCases</b> can be exposed as a requirement since the implementations are not likely to be complex. Furthermore...</div><div><br></div><div><br></div><div><b>**Generics**</b></div><div><br></div><div>Since <a href="https://github.com/apple/swift-evolution/blob/master/proposals/0142-associated-types-constraints.md" target="_blank">SE-0142</a> was implemented in Swift 4, we now have more expressive options for the protocol requirements:</div><div><br></div><div>  // 1 - array only</div><div>  protocol CaseEnumerable {</div><div>    static var allCases: [Self] { get }</div><div>  }</div><div><br></div><div>  // 2 - any sequence</div><div><div>  protocol CaseEnumerable {</div><div>    associatedtype <b>CaseSequence</b>: Sequence where CaseSequence.Element == Self</div><div>    static var <b>allCases</b>: CaseSequence { get }</div><div>  }</div></div><div><br></div><div>  // 3 - any collection</div><div><div>  protocol CaseEnumerable {</div><div>    associatedtype <b>CaseCollection</b>: Collection where CaseCollection.Element == Self</div><div>    static var <b>allCases</b>: CaseCollection { get }</div><div>  }</div></div><div><br></div><div>This restricts the CaseEnumerable protocol to be used as a generic constraint, but that&#39;d be true even with a plain array because of the Self type.</div><div><br></div><div>Personally I like the flexibility provided by the associatedtype, but I also recognize it won&#39;t be incredibly useful for enums — more so if we wanted to provide e.g. UInt8.allValues, whose ideal implementation might be &quot;return 0...UInt8.max&quot;. 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><br></div><div><br></div><div><b>**Implementation strategy and edge cases**</b></div><div><div><br></div><div><a href="https://twitter.com/CodaFi_/status/920132464001024001" target="_blank">Last year</a>, Robert Widmann put together an implementation of CaseEnumerable: <a href="https://github.com/apple/swift/compare/master...CodaFi:ace-attorney" target="_blank">https://<wbr>github.com/apple/swift/<wbr>compare/master...CodaFi:ace-<wbr>attorney</a> </div><div>I&#39;d love to hear from anyone more familiar with the code whether there&#39;s anything we&#39;d want to change about this approach.</div></div><div><br></div><div>A few tricky situations have been brought to my attention:</div><div><br></div><div>- Enums <b>imported from C/Obj-C</b> headers. Doug Gregor writes: <i>&quot;<span style="font-size:12.8px">The autogenerated allValues would only be able to </span><span class="gmail-m_697417550142501560gmail-il" style="font-size:12.8px">list</span><span style="font-size:12.8px"> the </span><span class="gmail-m_697417550142501560gmail-il" style="font-size:12.8px">enum</span><span style="font-size:12.8px"> </span><span class="gmail-m_697417550142501560gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px"> it knows about from the header it was compiled with. If the library changes to add </span><span class="gmail-m_697417550142501560gmail-il" style="font-size:12.8px">cases</span><span style="font-size:12.8px"> in the future (which, for example, Apple frameworks tend to do), those wouldn’t be captured in allValues.</span>&quot;</i></div><div><br></div></div></blockquote><div> </div><div>This proposal would be very useful in reducing boiler plate code in many cases!</div><div><br></div><div>With &quot;Enums imported from C/Obj-C headers&quot;, it would be nice to automatically bridge it by using an appropriately namespaced (and dynamically loaded) ObjC equivalent values(s), if one is available. I think this addresses Doug&#39;s concerns.</div><div><br></div><div>For example:</div><div><br></div><div><div class="gmail_quote"><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// Foundation </span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">NSObjCRuntime.h</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span></p></div><div class="gmail_quote"><br></div><div class="gmail_quote"><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(120,73,42)">#define NS_VALUE_ENUMERABLE(__TYPE__) \</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(120,73,42)">    FOUNDATION_EXPORT NSInteger const __TYPE__ ##ValueCount; \</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(120,73,42)">    FOUNDATION_EXPORT __TYPE__ __TYPE__##ValueAtIndex(NSInteger index);</p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">//</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">UITableViewCellStyle.h</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">//</span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px"><br></span></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px">...</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px"><br></span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(120,73,42)">NS_VALUE_ENUMERABLE</span>(UITableViewCellStyle);</p><div><br></div><div><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// UITableViewCellStyle.m</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px"><br></span></p></div><div><br></div><div><span style="color:rgb(0,0,0);font-family:Menlo;font-size:11px">...</span><br></div><div><br></div>
<p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(112,61,170)">NSInteger</span> <span style="color:rgb(186,45,162)">const</span>      UITableViewCellStyleValueCount = <span style="color:rgb(39,42,216)">4</span>;</p>
<p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(79,129,135)">UITableViewCellStyle</span> UITableViewCellStyleValueAtIndex(<span style="color:rgb(112,61,170)">NSInteger</span> index) {</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,132,0)">    // Can be a switch statement in more complicated cases</p>
<p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">    </span><span style="color:rgb(186,45,162)">return</span><span style="color:rgb(0,0,0)"> (</span>UITableViewCellStyle<span style="color:rgb(0,0,0)">) index;</span></p>
<p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><br></p><div><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">UITableViewCellStyle-Generated.swift</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span></p></div><div><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(186,45,162)">extension</span><span style="color:rgb(0,0,0)"> </span>UITableViewCellStyle<span style="color:rgb(0,0,0)">: </span>ValueEnumerable<span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">    </span><span style="color:rgb(186,45,162)">typealias</span><span style="color:rgb(0,0,0)"> AllValuesCollection = </span>_NSValueEnumerableCollection<span style="color:rgb(0,0,0)">&lt;</span>UITableViewCellStyle<span style="color:rgb(0,0,0)">&gt;</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">    </span><span style="color:rgb(186,45,162)">static</span><span style="color:rgb(0,0,0)"> </span><span style="color:rgb(186,45,162)">var</span><span style="color:rgb(0,0,0)"> allValues: </span>AllValuesCollection<span style="color:rgb(0,0,0)"> {</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">        </span><span style="color:rgb(186,45,162)">return</span><span style="color:rgb(0,0,0)"> </span>AllValuesCollection<span style="color:rgb(0,0,0)">(</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(79,129,135)"><span style="color:rgb(0,0,0)">            count: </span>UITableViewCellStyleValueCount<span style="color:rgb(0,0,0)">,</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(49,89,93)"><span style="color:rgb(0,0,0)">            valueAtIndex: </span>UITableViewCellStyleValueAtIndex<span style="color:rgb(0,0,0)">)</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    }</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p></div></div></div></div><div class="gmail_quote"><div><span style="font-family:monospace,monospace"><br></span></div><div><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// Swift Standard Library</span><br></p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><span style="color:rgb(0,132,0);font-family:Menlo;font-size:11px">// </span></p></div><div><span style="font-family:monospace,monospace"><br></span></div><div><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">struct</span> _NSValueEnumerableCollection&lt;T&gt;: <span style="color:rgb(112,61,170)">Collection</span> {</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo"><span style="color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">private</span> <span style="color:rgb(186,45,162)">let</span> valueAtIndex: (<span style="color:rgb(112,61,170)">Int</span>) -&gt; </span><span style="color:rgb(79,129,135)">T</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">let</span> count: <span style="color:rgb(112,61,170)">Int</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">var</span> startIndex: <span style="color:rgb(112,61,170)">Int</span> { <span style="color:rgb(186,45,162)">return</span> <span style="color:rgb(39,42,216)">0</span> }</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">var</span> endIndex: <span style="color:rgb(112,61,170)">Int</span> { <span style="color:rgb(186,45,162)">return</span> <span style="color:rgb(79,129,135)">count</span> }</p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">init</span>(count: <span style="color:rgb(112,61,170)">Int</span>, valueAtIndex: @<span style="color:rgb(186,45,162)">escaping</span> (<span style="color:rgb(112,61,170)">Int</span>) -&gt; <span style="color:rgb(79,129,135)">T</span>) {</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">        <span style="color:rgb(186,45,162)">self</span>.<span style="color:rgb(79,129,135)">count</span> = count</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">        <span style="color:rgb(186,45,162)">self</span>.<span style="color:rgb(79,129,135)">valueAtIndex</span> = valueAtIndex</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    }</p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">subscript</span>(index: <span style="color:rgb(112,61,170)">Int</span>) -&gt; <span style="color:rgb(79,129,135)">T</span> {</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">        <span style="color:rgb(0,132,0)">// Potentially valueAtIndex could be @convention(c) returning</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(0,132,0)">        // an integer type. </span><span style="color:rgb(0,132,0)">T could be RawRepresentable, and conversion</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)"><span style="color:rgb(0,132,0)">        // errors could be handled here.</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">        <span style="color:rgb(186,45,162)">return</span> <span style="color:rgb(186,45,162)">self</span>.<span style="color:rgb(79,129,135)">valueAtIndex</span>(index)</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    }</p><p style="margin:0px;font-stretch:normal;font-size:12px;line-height:normal;font-family:Helvetica;min-height:14px"><br></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    <span style="color:rgb(186,45,162)">public</span> <span style="color:rgb(186,45,162)">func</span> index(after i: <span style="color:rgb(112,61,170)">Int</span>) -&gt; <span style="color:rgb(112,61,170)">Int</span> {</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">        <span style="color:rgb(186,45,162)">return</span> i + <span style="color:rgb(39,42,216)">1</span></p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">    }</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">

















</p><p style="margin:0px;font-stretch:normal;font-size:11px;line-height:normal;font-family:Menlo;color:rgb(0,0,0)">}</p></div></div><div class="gmail_quote"><div><br></div><div>Thanks,</div><div>Andrew Bennett</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div dir="ltr"><div></div><div>My understanding of the runtime/importer is very shallow, but with the current metadata-based strategy, I suspect imported enums couldn&#39;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&#39;t behave the same way when you drop in an upgraded version of the library. Is that correct?</div><div><br></div><div>(Nonetheless, if a user really wanted this auto-generation, it would be nice to allow it somehow. Personally, I have had enums whose &quot;source of truth&quot; was an Obj-C header file, but since it was compiled in with the rest of the application, we didn&#39;t care at all about library upgrades. Maybe an internal extension adding a conformance can be allowed to participate in auto-generation?)</div><div><br></div><div>- Enums with <b>availability</b> annotations on some cases. Doug Gregor writes: <i>&quot;<span style="font-size:12.8px">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>&quot;</i></div><div><br></div><div><div>If we fetch cases from the enum metadata, does this &quot;just work&quot; since the metadata will be coming from whichever version of the library is loaded at runtime? If not, is it at least <i>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><br></div><div>- Should it be possible to add a CaseEnumerable conformance in an <b>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&#39;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&#39;t actually necessary. The question to answer is: in exactly which circumstances can the implementation be synthesized?</div><div><br></div><div><br></div><div>Looking forward to hearing everyone&#39;s thoughts,</div><div><div class="gmail-m_697417550142501560gmail_signature"><div dir="ltr"><div>Jacob<br></div></div></div></div>
</div>
<br>______________________________<wbr>_________________<br>
swift-evolution mailing list<br>
<a href="mailto:swift-evolution@swift.org">swift-evolution@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-evolution" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>evolution</a><br>
<br></blockquote></div><br></div></div>