<div dir="ltr">On Tue, Nov 14, 2017 at 5:49 AM, Brent Royal-Gordon <span dir="ltr">&lt;<a href="mailto:brent@architechies.com" target="_blank">brent@architechies.com</a>&gt;</span> wrote:<br><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><span><div><blockquote type="cite"><div>On Nov 13, 2017, at 9:21 PM, Xiaodi Wu &lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt; wrote:</div><br class="m_4202062879742265811m_3972179143067859950Apple-interchange-newline"><div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important">...I should add, if full conformance to `Collection` is still too much to ask, enabling &quot;for `case` in Foo.self&quot; by magic would itself address the entirety of the proposal&#39;s use case, adding no API surface area.</span></div></blockquote></div><div><br></div></span><div>No, Xiaodi. No, it would not.</div><div><br></div><div>Okay, thus far we&#39;ve talked vaguely about &quot;accessing the cases of an enum&quot;, but let&#39;s talk a little more concretely about what that means. I think that, especially on Apple platforms, this is the most important concrete use case:</div><div><br></div><div>PROBLEM:</div><div><br></div><div>You have an enum like:</div><div><br></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>enum BugStatus {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>case open, inProgress, resolved, closed, reopened</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>var localizedName: String { … } </div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>}</div><div><br></div><div>You wish to present these options in a user interface so a user can select one of them. For instance, if you&#39;re on iOS and using UITableView, you might want to present the enum&#39;s values through `UITableViewDataSource` and allow selection through `UITableViewDelegate`.</div><div><br></div><div>REQUIREMENTS:</div><div><br></div><div>1. It must be possible to easily access the count of values, and to access any particular value using contiguous `Int` indices. This could be achieved either by directly accessing elements in the list of values through an Int subscript, or by constructing an Array from the list of values.</div><div><br></div><div>2. It must be possible to control the order of values in the list of values, either by using source order or through some other simple, straightforward mechanism.</div></div></blockquote><div> </div><div>OK, first of all, nowhere in the proposal text are these requirements stated as part of the use case. You&#39;re free to put forward new use cases, but here I am trying to design the most elegant way to fulfill a stated need and you&#39;re telling me that it&#39;s something other than what&#39;s written. But sure, let&#39;s proceed on this basis.<br></div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div>PROPOSED SOLUTION:</div><div><br></div><div>You conform `BugStatus` to `ValueEnumerable`:</div><div><br></div><div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>enum BugStatus: ValueEnumerable {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>case open, inProgress, resolved, closed, reopened</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>var localizedName: String { … } </div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>}</div></div><div><br></div><div>And then write the table view data source to present the elements of `BugStatus.allValues`:</div><div><br></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>class BugStatusDataSource: NSObject, UITableViewDataSource, UITableViewDelegate {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>@IBOutlet var tableView: UITableView?</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>@objc dynamic var selected: BugStatus? {<span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>// Observable via KVO</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>didSet { tableView.reloadData() }</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>}</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>func status(at indexPath: IndexPath) -&gt; Status {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>BugStatus.allValues[indexPath.<wbr>row]</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>}</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>func tableView(_: UITableView, numberOfRowsInSection section: Int) -&gt; Int {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>return BugStatus.allValues.count</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>}</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -&gt; UITableViewCell {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>let status = self.status(at: indexPath)</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>let identifier = (status == selected) ? &quot;SelectedCell&quot; : &quot;RegularCell&quot;</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>let cell = tableView.dequeueReusableCell(<wbr>withIdentifier: identifier, for: indexPath)</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>cell.titleLabel.text = status.localizedName</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>return cell</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>}</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span></div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) {</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                        </span>selected = status(at: indexPath)</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">                </span>}</div><div><span class="m_4202062879742265811m_3972179143067859950Apple-tab-span" style="white-space:pre-wrap">        </span>}</div><div><br></div><div>This is the most direct solution; a more sophisticated version might inject the list as an Array so that you can show a subset of the full set of values.</div><div><br></div><div>EXTENSIONS:</div><div><br></div><div>Now, let&#39;s quickly talk about a couple extensions of this use case:</div><div><br></div><div>* The values, and the table view cells, are grouped into sections. This suggests some sort of two-level, nested structure, which may not use `Int` indices.</div><div><br></div><div>* You want to write a *generic* data source which can present all the values of *any* ValueEnumerable type (or at least any conforming to a protocol that allows us to fill in their cells). For that purpose, it&#39;s helpful to have the type conform to *some* sort of protocol and expose the value list through that protocol.</div><div><br></div><div>You say that:</div><span><div><br></div><div><blockquote type="cite"><span style="float:none;display:inline!important"> </span>Essentially all other uses for enumeration of enum cases can be trivially recreated based on just that.</blockquote></div><div><br></div></span><div>But with this use case in mind, we can see that it is &quot;trivial&quot; in the sense that the annoying boilerplate you need to bridge the significant impedance mismatch is easy to come up with. Yes, you could construct an array using the magic `for` loop, but that would be a serious pain. (And there would be no ergonomic, mistake-resistant way to hide that pain behind a function/initializer call, because there&#39;s no way to say that a parameter must be a metatype for an enum.) What you really want is a way to access or construct an `Array` or array-like type containing the type&#39;s values.</div></div></blockquote><div><br></div><div>You cannot truly believe that</div><div><br></div><div>```</div><div>var cases = [BugStatus]()</div><div>for c in BugStatus.self { cases.append(c) }</div><div>```</div><div><br></div><div>is &quot;serious pain.&quot; Yes, part of being an incomplete implementation is that it lacks the ergonomics of a fleshed-out conformance to `Collection`. But &quot;serious pain&quot;?</div><div><br></div><div>The point here is that even a minimal step towards what we agree is the ideal design would make _possible_ what today is _impossible_: namely, future-proof enumeration of all the cases of an enum type without associated values.</div><div><br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div>*Actually* conforming the metatype to `Sequence` or `Collection` would be a different story. There, you could construct `Array`s or access elements using ordinary APIs and type system features. And you could write generic algorithms which used the set of all types: they would require conformance to `Sequence` or `Collection`, and users would specify `Foo.Type` as the generic parameter.</div></div></blockquote><div><br></div><div>Indeed. The point here is that we don&#39;t need a name for this protocol. You&#39;d be able to write useful generic algorithms by using functions such as `map` on `T.self` with intuitive constraints such as `T where T.Type : Collection`. Isn&#39;t that a sublime way of expressing exactly what we mean?</div><div> </div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div>But I suspect that would require deeper compiler changes than we can be certain to get in Swift 5 or really at any specific point on the roadmap, and I don&#39;t think we should delay this feature indefinitely to get a design whose only real benefit is elegance.</div></div></blockquote><div><br></div><div>We may (almost certainly) need more time to realize the full design. But we don&#39;t need much to take the first steps towards it, which--as I write above--would already make possible the currently impossible. It seems you&#39;d rather ship a complete but inelegant design forever than an incomplete but useful part of an elegant design now. I wouldn&#39;t make that trade-off.</div><div><br></div></div></div></div>