<div dir="ltr">On Mon, Nov 13, 2017 at 8:16 PM, 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:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><span class="gmail-"><blockquote type="cite"><div>On Nov 12, 2017, at 10:16 AM, Xiaodi Wu &lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt; wrote:</div><br class="gmail-m_6022901788387251292Apple-interchange-newline"><div><div dir="ltr">On Sun, Nov 12, 2017 at 4:54 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:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><span><blockquote type="cite"><div>On Nov 10, 2017, at 11:01 PM, Xiaodi Wu &lt;<a href="mailto:xiaodi.wu@gmail.com" target="_blank">xiaodi.wu@gmail.com</a>&gt; wrote:</div><br class="gmail-m_6022901788387251292m_-731029454393875740Apple-interchange-newline"><div><div 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">Nit: if you want to call it `ValueEnumerable`, then this should be `DefaultValueCollection`.</div><div 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"></div></div></blockquote><div><br></div></span><div>I used `DefaultCaseCollection` because, although the protocol can work with any type to return its values, this type can only work with enums and only returns their cases. `ValueEnumerable` could be sensibly applied to `Int`; `DefaultCaseCollection` could not.</div></div></div></blockquote><div><br></div><div> Because of how you&#39;ve chosen to implement `DefaultCaseCollection`, or do you mean to say that you deliberately want a design where types other than enums do not share the default return type?</div></div></div></div></div></blockquote><div><br></div></span><div>Because the way `DefaultCaseCollection` works is that it queries runtime metadata that can only exist for enums.</div><div><br></div><div>In theory, we might be able to write a `DefaultValueCollection` which would work for structs with `ValueEnumerable` properties by generating all possible permutations of those fields. In practice, I suspect that this would only rarely be useful. Structs&#39; types are rarely specified so tightly that all permutations are valid; for instance, a `struct PlayingCard` with an integer `rank` property would only be *valid* with a rank between 1 and 13, even though `Int`&#39;s range is much wider. So I don&#39;t think we&#39;ll ever want this type to support structs, and it would therefore be clearer to bake its enum-only nature into its name.</div><div><br></div><div>(If your response is that &quot;your argument against permuting all possible struct values is just as true with an integer associated value&quot;…well, you&#39;re not wrong, and that might be an argument against making integer types `ValueEnumerable`.</div></div></div></blockquote><div><br></div><div>Not only is it an argument against making integer types `ValueEnumerable`, it&#39;s a pretty darn good argument against any design that makes such an eventuality possible--in the absence of a compelling use case that would make that design desirable for other reasons.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><div>But we don&#39;t propose conforming `Int` to `ValueEnumerable` immediately, just adopting a design flexible enough to permit it.)</div><span class="gmail-"><br><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><div>Tony&#39;s &quot;no more specific than they need to&quot; language applies here. The way I see it is this:</div><div><br></div><div>* `Bool` is not an enum, but it could be usefully conformed to `ValueEnumerable`. Why should we prevent that?</div><div>* A type with two independently-switchable `Bool`s—say, `isMirrored` and `isFlipped`—could be usefully conformed to `ValueEnumerable`. Why should we prevent that?</div><div>* Having integer types conform to `ValueEnumerable` with `static let allValues = Self.min...Self.max` could be useful. Why should we prevent that?</div></div></div></blockquote><div><br></div><div>I&#39;d say you&#39;re looking at it the wrong way. We&#39;re not *preventing* anything. We&#39;re adding a feature, and the question is, why should we *add* more than is justified by the use case?</div></div></div></div></div></blockquote><div><br></div></span>Okay, here&#39;s the positive justification: The choice of an enum vs. a struct ought, to some degree, to be an implementation detail. As a general example of this, `__attribute__((swift_wrapper(<wbr>enum)) ` types in Objective-C are actually imported into Swift as structs, but this detail rarely matters to users. A little closer to home, `Bool` could be an enum, but is implemented as a struct instead. `EncodingError` and `DecodingError` could be structs, but are implemented as enums instead.</div></div></blockquote><div><br></div><div>This is not a terrible argument from a practical standpoint, but I&#39;d turn it around: based on your examples, it&#39;s a convincing argument for improving Objective-C import so that all types that users would want to be enums can actually be bridged as enums, and for making `Bool` into an enum. I don&#39;t see what the problem is with `EncodingError` or `DecodingError` being enums, so I can&#39;t comment on that.</div><div> </div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div></div><div>To allow this flexibility, Swift rarely creates features for enums which are completely closed off to the structs (or vice versa), though they may have convenience features on only one of them. For example, both can have initializers, but only structs have them created implicitly; both can be RawRepresentable, but only enums get the sugar syntax. (The big exceptions are enum&#39;s pattern matching and struct&#39;s ability to encapsulate implementation details, but we&#39;ve talked about bringing both of these features to the other side in some fashion.)</div><div><br></div><div>Therefore, I think this feature should follow the general Swift pattern and not be completely closed off to structs. It may not be as convenient to use there, but it should be possible. This preserves flexibility for type designers so they aren&#39;t forced to use enums merely because they want to use the standard mechanism for publishing the possible values of a type.</div></div></blockquote><div><br></div><div>Or, we could fix the edge cases so that no user needs to use a `struct` where an `enum` would be most suitable so that the choice of enum vs. struct is never an implementation artifact but consistently holds some semantic significance. In the meantime, I&#39;d argue we shouldn&#39;t weaken what distinctions do exist between the two.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><span class="gmail-"><div></div><blockquote type="cite"><div><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Is there a clamor for enumerating the possible values of a type with two independently switchable `Bool`s? If so, is that not an argument to make `Bool` a valid raw value type? (There is already a bug report to make tuples of raw value types valid raw value types themselves.)</div></div></div></div></div></blockquote><div><br></div></span><div>FWIW, I&#39;m surprised Swift thinks &quot;raw type &#39;Bool&#39; is not expressible by any literal&quot; when Bool is `ExpressibleByBooleanLiteral`. I&#39;m not sure whether this is an oversight or if there&#39;s a specific reason for it.</div></div></div></blockquote><div><br></div><div>Yes, I agree that this should be fixed.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><span class="gmail-"><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>Why is it useful for (fixed-width) integer types to conform to `ValueEnumerable`? What use cases, exactly, would that enable that are not possible now?</div><div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div></div></div></blockquote></div></div></div></blockquote><div><br></div></span><div>It might permit advanced `ValueEnumerable` synthesis, for one thing. (But again, see my misgivings above about the usefulness of generating all possible permutations.)</div><span class="gmail-"><br><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><div>And at the same time, a small, specialized collection type _also_ helps with our intended use case in some ways (while admittedly making things more difficult in others). So I think the more general design, which also works better for our intended use case, is the superior option.</div><span><br><blockquote type="cite"><div><div 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">Along the lines of user ergonomics, I would advocate for as many enums as possible to conform without explicit opt-in. It&#39;s true that we are moving away from such magical designs, and for good reason, but the gain here of enums Just Working(TM) for such a long-demanded feature has, I would argue, more benefits than drawbacks. To my mind, the feature is a lot like `RawRepresentable` in several ways, and it would be defensible for an equal amount of magic to be enabled for it.</div></div></blockquote></span></div><div><br></div><div>But `RawRepresentable` *doesn&#39;t* get automatically added to all enums—you explicitly opt in, albeit using a special sugar syntax. No, I think opt-in is the right answer here. We might be able to justify adding sugar to opt-in, but I can&#39;t actually think of a way to make opting in easier than conforming to a protocol and letting the complier synthesize the requirements.</div></div></blockquote><div><br></div><div>Yes, you&#39;re right that `RawRepresentable` conformance *doesn&#39;t* get automatically added in, but there exists special sugar which makes the end result indistinguishable. By this I mean that the user gets `RawRepresentable` conformance without ever writing `Foo : RawRepresentable` anywhere (and neither do they write `Foo : Bar` where `Bar` is in turn `RawRepresentable`).</div><div></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"></div></blockquote></div></div></div></blockquote><div><br></div></span><div>That is *not* getting conformance &quot;without explicit opt-in&quot;. That is explicitly opting in through a sugar feature.</div><div><br></div><div>If you want to suggest a form of sugar which is substantially easier for users than adding a `ValueEnumerable` conformance clause, we&#39;re all ears. But I don&#39;t think there really is one. I don&#39;t think `@allValues enum Foo` is really enough of a win over `enum Foo: ValueEnumerable` to justify the additional language surface area.</div></div></div></blockquote><div><br></div><div>As a thought experiment, let&#39;s suppose we wanted to find an attribute that would be suitable. What would such an attribute be named? Probably not `@allValues enum`, which is kind of cryptic. (What, after all, is an enum that isn&#39;t &quot;all values&quot;? Would there be &quot;some references and some values&quot;? Absurd!) How about simply `@valueEnumerable enum`? But enumerations are a value type, so clearly its cases are values, making `value` redundant. Likewise, similar reasoning would apply to `@caseEnumerable`. Now we&#39;re left with `@enumerable enum`. See the problem there?</div><div><br></div><div>No, I&#39;m really suggesting automatic conformance. Enums (aka enumerations), after all, should be enumerable. It says so right in the name. (Yes, Swift extends enums to support features such as associated values which can make their enumeration a tricky issue, but that&#39;s part and parcel of _extending a paradigm_; it is still important, however, to reckon with what a &quot;bread-and-butter&quot; enum is and what it is capable of.) To say that we have an enumerable enum should be a redundant redundancy; we have truly diluted away any semblance of what an enum is if we have to opt into enums being enumerable.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-style:solid;border-left-color:rgb(204,204,204);padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space"><div><span class="gmail-"><blockquote type="cite"><div dir="ltr"><div class="gmail_extra"><div class="gmail_quote"><div>This is, in fact, a perfect opportunity to bring up a question I&#39;ve been leaving implicit. Why not explore moving away from using a protocol? The proposed protocol has no syntactic requirements, and when constrained only the use case of enums, it has essentially no semantic requirements either. It seems that it&#39;s only because we&#39;ve committed to using a protocol that we&#39;ve opened up this exploration of what it means semantically to be `ValueEnumerable`, and how to generalize it to other types, and how to design the return type of the synthesized function, etc.</div><div><br></div><div>What if some attribute would simply make the metatype conform to `Sequence` (or, for that matter, `BidirectionalCollection`)?</div></div></div></div></blockquote><br></span></div><div>I would absolutely *adore* being able to conform metatypes to protocols, but I assume this would require major surgery to the type system. I also assume that the code generation needed to fulfill `RandomAccessCollection`&#39;s requirements on `Foo.Type` would be much more complicated than generating a conformance. And there&#39;s also the problem that subscripts are currently not allowed as class/static members.</div><div><br></div><div>If it&#39;s actually feasible to modify the compiler with the features necessary to support this in the Swift 5 timeframe, I am totally willing to consider using `Foo.self` as the collection instead of `Foo.allValues`. But &quot;give me a collection of all the cases&quot; is a major convenience that users have been requesting for four years, and I really don&#39;t want to keep them waiting any longer just so we can deliver a Platonically ideal design.`Array(Foo.self)` would be pretty cool, but it&#39;s not *so* cool that we should delay something users will be ecstatic to have for several years just to get it.</div></div></blockquote><div><br></div><div>Just for the record, let it be recorded that you and I agree that the _perfect_ solution would be having the ability to write `for case in Foo.self`.</div><div><br></div><div>Now, here&#39;s the thing: we don&#39;t need to be able to conform any metatype to any protocol in order to deliver what users have asked for. As a first pass, we need only to allow enums to magically conform to `Collection` (not even `RandomAccessCollection`, just `Collection`). You&#39;d be able to iterate over the cases in a for...in loop, you&#39;d be able to write `Array(Foo.self)`, and that comfortably gets us 80% of the way there _while adding nothing to the API surface area that would not later be subsumed_ by a more complete implementation of the ideal solution.</div><div><br></div><div>There is precedent for such a solution. Recall how tuples of Equatable types have `==` defined up to arity 6 by a manual implementation. We still haven&#39;t implemented the general solution, but when we do, the interim solution can fade away transparently.</div><div><br></div><div><br></div></div></div></div>