> On Jan 9, 2018, at 10:26 PM, Xiaodi Wu via swift-evolution 
> <swift-evolution@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` 

I won't rehash the entire discussion in previous threads, but to summarize my 

        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 

> 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 

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 

        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

swift-evolution mailing list

Reply via email to