[swift-evolution] [Review] SE-0194: Derived Collection of Enum Cases

Xiaodi Wu xiaodi.wu at gmail.com
Wed Jan 10 21:08:49 CST 2018

On Wed, Jan 10, 2018 at 5:06 AM, Brent Royal-Gordon <brent at architechies.com>

> On Jan 9, 2018, at 10:26 PM, Xiaodi Wu via swift-evolution <
> swift-evolution at swift.org> wrote:
> I continue to have concerns about this proposal, and I'm gravely and very
> bitterly disappointed that the concerns have not even been acknowledged in
> the Alternatives section, which is in my view the minimum action that an
> author should take when points are raised during a pitch phase, even (and
> especially) when the author disagrees, along with a cogent write-up of why
> the proposed design is superior in the author's view to the alternative. In
> this case, the proposal authors write:
>   "The community has not raised any solutions whose APIs differ
> significantly from this proposal, except for solutions which provide
> strictly more functionality."
> This is false, as I have offered a solution in the past whose API differs
> entirely from this proposal, and which provides strictly a subset of the
> functionality which goes to the core of the issue at stake.
> I can't speak for the other co-authors, but for my part, this was an
> oversight and I apologize for it. I think we should have discussed your
> `MyType.self` alternative.
> I won't rehash the entire discussion in previous threads, but to summarize
> my objections:

I appreciate the detailed reply below. Time constraints prohibit an
immediate reply with the same level of thoroughness, but I look forward to
composing one within a few days' time. Bear with me.

> 1. `MyType.self` is harder to understand for someone who's never seen it
> than `MyType.allValues`. For instance, the expression
> `AccountStatus.allValues[0]` is completely self-explanatory, while
> `AccountStatus.self[0]` is more obscure and would require a trip to the
> documentation. (And since `self` here is a language keyword, not a real
> member, the most obvious route to the documentation is not available.) In
> my opinion, we should not prefer the `MyType.self` syntax.
> 2. Even if the community disagrees with me and thinks `MyType.self` is a
> better syntax than `MyType.allValues`, it is not better *enough* to
> outweigh the costs:
> • The metatype solution provides no additional functionality; it is merely
> a matter of which syntax we choose to support, and how much effort this
> support requires.
> • Conforming the metatype to `Collection` requires a lot of type system
> features we do not currently have. Currently, structural types cannot
> conform to protocols, and metatypes are a structural type. Metatypes also
> cannot have subscripts currently. Your proposed design has a lot of
> prerequisites. That is not in and of itself disqualifying, but it should be
> weighed against it.
> • You suggest that we should add bits and pieces of "`Collection`
> functionality" incrementally as engineering resources become available. The
> problem is that the most valuable part of the "`Collection` functionality"
> is conformance to `Collection` or at least `Sequence`, not the existence of
> any particular members of `Collection`, and this is the part that would
> require the most engineering resources.
> • A large part of the work we would do before supporting this conformance
> would be disposed of immediately once we could get it. For instance, all of
> the work to support having a metatype instead of a sequence in a `for-in`
> statement would be thrown away as soon as we could conform to `Sequence`.
> So this looks less like slowly building up pieces of the feature until we
> have all of them in place, and more like creating temporary hacks to
> emulate the feature until we have the time to do it right.
> • While this feature is not a showstopper, it is a highly desirable
> convenience. The proposal documents the high demand for this feature, so I
> won't elaborate on this point further.
> • Therefore, adopting this design would add a lot of engineering
> complexity before we could fully support a highly desirable feature, merely
> to get a syntax that we *might* prefer.
> To summarize the summary: The primary advantage of `MyType.self` is that
> it's elegant. To get that elegance, we must trade away getting a
> fully-functional implementation sooner, spending a lot of engineering
> resources (much of which would be wasted in the end), and—most crucially in
> my opinion—clarity at the point of use. It's not worth it.
> Earlier in this thread (or was it in the companion one?), another
> community member suggested that if `allValues` were to conform to
> `Sequence` instead of `Collection`, then even types that have an infinite
> number of possible values could conform to `ValueEnumerable`. Here's the
> rub: the definition of a type, or at least one of them, _is_ precisely the
> set of all possible values of a variable. If unconstrained by finiteness,
> then *all types* would meet the semantic requirements of `ValueEnumerable`.
> Not quite. There are types whose valid values are unknowable; for
> instance, a type representing "the ID of a record on the server" cannot
> know which IDs actually exist on the server, merely which ones *could*
> exist, and so an implementation of `allValues` would return invalid
> instances.
> But that's beside the point. What I think the "`allValues` should be
> allowed to be infinite" suggestion misses is that one of
> `ValueEnumerable`'s semantics is that it's not only theoretically
> *possible* to enumerate all the values, but actually *reasonable* to do so.
> This is more slippery and subjective than most protocol semantics, but I
> don't think that should disqualify it. There are plenty of places in the
> standard library where we make judgment calls like this. For instance, the
> decision that `Optional` should not conform to `Collection` is a similar
> judgment call: `Optional` could easily meet all of the formal requirements
> of a `Collection`, but we chose not to do it because we decided it didn't
> make *subjective* sense.
> Some simple enums could not be reasonably conformed to `ValueEnumerable`;
> for instance, there's little sense in conforming an `Error` enum, because
> you're unlikely to need to discover all the possible errors expressed by a
> type at runtime. Some non-simple enums could be reasonably conformed to
> `ValueEnumerable`; `Bool` is an obvious example.
> Some types, of course, fall into a gray area. `Int8` is fairly reasonable,
> but larger integer types get increasingly unreasonable until, by `Int64`,
> we reach types that would take decades to enumerate. Where the line should
> be drawn is a matter of opinion. (My opinion, to be clear, is that we
> shouldn't conform any of them; if someone really wants to do it, they can
> add a retroactive conformance.)
> As proposed, "`ValueEnumerable`...indicate[s] that a type has a finite,
> enumerable set of values"; now, we have the constraint that the type merely
> must have an upper bound in terms of memory referenced. Brent just wrote
> that he might later propose to extend `ValueEnumerable` to `Bool` and
> `Optional`, but theoretically and practically speaking it appears that it
> can correctly be extended to any type in the standard library that is not a
> `Sequence`, and with minimal effort even `Collection` types of fixed size
> (e.g., CollectionOfOne<T> with the right constraints as to the generic type
> T).
> Sure, but theoretically speaking, we could synthesize a `Comparable`
> implementation for all classes which compared them by address. This
> implementation would be totally correct, would fulfill all of the
> requirements of the protocol it was conforming to, and would usually be
> meaningless. So we don't.
> The fact that some types could be given a useless conformance to a
> protocol does not imply that the protocol shouldn't exist.
> First, "ValueEnumerable"-ness is neither universal to all enums (as those
> with associated types, indirect cases (think of all those tutorials about
> linked lists implemented as Swift enums), etc., are clearly not enumerable)
> nor unique as a property of enums,
> It's absolutely true that not all enums should be enumerable, and also
> true that many non-enums should be enumerable. That's precisely why the
> protocol is not `CaseEnumerable` and the property is not `allCases`.
> yet this proposal first makes a large generalization of the proposed
> protocol's semantics when the stated motivation is only about enums,
> Do you disagree that there are many types which are not enums, but
> which—like the enums we are trying to address with this proposal—it is also
> reasonable to want to retrieve all values of? Or do you think we have
> missed important aspects of these types by not deeply analyzing them? Or
> are you simply criticizing how this part of the proposal was drafted,
> despite believing that its conclusion is correct?
> then proceeds only to implement protocol conformance for enums.
> I think it's more accurate to say that it "only synthesizes a default
> implementation for simple enums". This is for three reasons:
> 1. Simple enums will probably be the most common conforming types, even if
> they aren't the only ones.
> 2. We can very easily synthesize an implementation which uses private APIs
> to return an `Int`-indexed and zero-based `RandomAccessCollection`, is
> highly optimized, and is forward-compatible. That is, for this subset of
> types and no others, we can implement a no-compromises, ideal
> implementation using special knowledge.
> 3. We are only confident that we know enough about the type to synthesize
> its implementation when it's a simple enum.
> More on that last point: Enums explicitly list all of the valid values in
> the source code. By contrast, a struct definition often permits values
> which are not actually valid because Swift's type system is not rich enough
> to conveniently express the constraints on their properties. For example,
> in these types:
> struct PlayingCard: ValueEnumerable {
> enum Suit: ValueEnumerable {
> case hearts, spades, diamonds, clubs
> }
> var suit: Suit
> var rank: Int
> }
> The compiler can correctly synthesize `PlayingCard.Suit.allValues` because
> all of its constraints are specified in code. By contrast, the compiler
> cannot know that `rank` must be between 1 and 13, so if it tried to
> synthesize `PlayingCard.allValues`, it would contain invalid values.
> These limitations are quite common in the types of code synthesis we've
> introduced so far. For example, we only synthesize an `Equatable`
> conformance if all the types involved are themselves `Equatable`; that's
> not because `Equatable` is only applicable to those types, it's just that
> we can't be reasonably sure of the desired semantics. The limitation of
> `ValueEnumerable` synthesis to simple enums is similar.
> Second, the collection of all values is properly just the type and not a
> property of the type, for the reasons outlined above.
> Okay, let's say that's true. Is that the only or best way to express it?
> So far, the justification I have heard for ignoring this objection is that
> (a) lots of people want the specific use case of knowing all the cases of
> an enum; and (b) a complete design which makes metatypes conform to
> `Collection` is not feasible for Swift 5. But that, in my view, cannot
> justify the _permanent_ inclusion (with ABI stability) of a protocol whose
> semantics apply to all non-`Sequence` types, littering the standard library
> and untold many other libraries with this conformance for the sake of
> having something done for Swift 5.
> My suggestion was, and is: if the motivation is to enumerate all the cases
> of an enum, deliver the best possible design for the specifically motivated
> use case rather than trying to deliver the most easy-to-implement design
> for the most general use case. In other words, give properly conformed
> enums (e.g. `enum MyEnum : Enumerable`--and I do suggest shortening the
> name, since what's enumerable is "the set of all values" == "the type") the
> synthesized ability to have all cases enumerated by iterating over the
> metatype: `for case in MyEnum.self { ... }`. Add as much other `Collection`
> functionality as can be implemented in the Swift 5 timeframe, starting with
> the most pressing based on the motivating use case. Then deliver more and
> more of the best possible design with each version of Swift as engineering
> resources permit.
> There is nothing wrong with the proposed design. It's a good design, and
> depending on one's priorities, it's arguably the *best* design. That it's
> feasible to deploy today is just icing on the cake.
> --
> Brent Royal-Gordon
> Architechies
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20180110/55ef3d13/attachment.html>

More information about the swift-evolution mailing list