On Tue, Nov 14, 2017 at 5:49 AM, Brent Royal-Gordon <br...@architechies.com> wrote:
> On Nov 13, 2017, at 9:21 PM, Xiaodi Wu <xiaodi...@gmail.com> wrote: > > ...I should add, if full conformance to `Collection` is still too much to > ask, enabling "for `case` in Foo.self" by magic would itself address the > entirety of the proposal's use case, adding no API surface area. > > > No, Xiaodi. No, it would not. > > Okay, thus far we've talked vaguely about "accessing the cases of an > enum", but let'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: > > PROBLEM: > > You have an enum like: > > enum BugStatus { > case open, inProgress, resolved, closed, reopened > var localizedName: String { … } > } > > You wish to present these options in a user interface so a user can select > one of them. For instance, if you're on iOS and using UITableView, you > might want to present the enum's values through `UITableViewDataSource` and > allow selection through `UITableViewDelegate`. > > REQUIREMENTS: > > 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. > > 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. > OK, first of all, nowhere in the proposal text are these requirements stated as part of the use case. You'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're telling me that it's something other than what's written. But sure, let's proceed on this basis. PROPOSED SOLUTION: > > You conform `BugStatus` to `ValueEnumerable`: > > enum BugStatus: ValueEnumerable { > case open, inProgress, resolved, closed, reopened > var localizedName: String { … } > } > > And then write the table view data source to present the elements of > `BugStatus.allValues`: > > class BugStatusDataSource: NSObject, UITableViewDataSource, > UITableViewDelegate { > @IBOutlet var tableView: UITableView? > @objc dynamic var selected: BugStatus? { // Observable via KVO > didSet { tableView.reloadData() } > } > func status(at indexPath: IndexPath) -> Status { > BugStatus.allValues[indexPath.row] > } > func tableView(_: UITableView, numberOfRowsInSection section: Int) -> Int { > return BugStatus.allValues.count > } > func tableView(_: UITableView, cellForRowAt indexPath: IndexPath) -> > UITableViewCell { > let status = self.status(at: indexPath) > let identifier = (status == selected) ? "SelectedCell" : "RegularCell" > let cell = tableView.dequeueReusableCell(withIdentifier: identifier, for: > indexPath) > cell.titleLabel.text = status.localizedName > return cell > } > func tableView(_: UITableView, didSelectRowAt indexPath: IndexPath) { > selected = status(at: indexPath) > } > } > > 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. > > EXTENSIONS: > > Now, let's quickly talk about a couple extensions of this use case: > > * 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. > > * 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's > helpful to have the type conform to *some* sort of protocol and expose the > value list through that protocol. > > You say that: > > Essentially all other uses for enumeration of enum cases can be > trivially recreated based on just that. > > > But with this use case in mind, we can see that it is "trivial" 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'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's values. > You cannot truly believe that ``` var cases = [BugStatus]() for c in BugStatus.self { cases.append(c) } ``` is "serious pain." Yes, part of being an incomplete implementation is that it lacks the ergonomics of a fleshed-out conformance to `Collection`. But "serious pain"? 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. *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. > Indeed. The point here is that we don't need a name for this protocol. You'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't that a sublime way of expressing exactly what we mean? > 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't think we should delay this feature indefinitely to get a design > whose only real benefit is elegance. > We may (almost certainly) need more time to realize the full design. But we don'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'd rather ship a complete but inelegant design forever than an incomplete but useful part of an elegant design now. I wouldn't make that trade-off.
_______________________________________________ swift-dev mailing list swift-dev@swift.org https://lists.swift.org/mailman/listinfo/swift-dev