On Wed, Jan 10, 2018 at 5:06 AM, Brent Royal-Gordon <br...@architechies.com> wrote:
> 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` 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 > >
_______________________________________________ swift-evolution mailing list swift-evolution@swift.org https://lists.swift.org/mailman/listinfo/swift-evolution