SE-0194: Derived Collection of Enum Cases

What is your evaluation of the proposal?

+1 to the idea. Disappointing that we don’t seem to be able to add this to @objc enums. Could we come up with some kind of workaround for this?

Is the problem being addressed significant enough to warrant a change to Swift?

Yes, this is a common problem where I constantly write all case static properties, and where I use the count. To be honest, for such a robust system, I found it a glaring omission.

Does this proposal fit well with the feel and direction of Swift?

Yes, the way the proposed solution is designed seems to work well.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I’ve done a lot of hacked enum “allCases” implementations in Swift, and we really didn’t have this power in the other languages I’ve used.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Quick reading.

···

Sent from my iPad

On 9 Jan 2018, at 6:02 am, Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:

Hello Swift community,

The review of SE-0194 "Derived Collection of Enum Cases" begins now and runs through January 11, 2018. The proposal is available here:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub
Reply text
Other replies
What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?
Is the problem being addressed significant enough to warrant a change to Swift?
Does this proposal fit well with the feel and direction of Swift?
If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?
More information about the Swift evolution process is available at

swift-evolution/process.md at master · apple/swift-evolution · GitHub
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Proposal link:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub
<Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub;
What is your evaluation of the proposal?

+1 on the general problem, but I have a few comments on the design specifics

- I'd very much prefer automatic synthesis with enums,
- I strongly feel the compiler should auto-generate the implementation in an extension for enums.
- Should we add `ValueCollection.Index == Int` to the associatedtype so it can always be indexed as in a table?
  - At the least, we should ensure the synthesized implementation is int-indexed for tables.
- I feel we should allow auto-generation of imported c enums in an extension, as just an array of the known cases rather than the magical metadata iterator if necessary.

Is the problem being addressed significant enough to warrant a change to Swift?

Definitely. It's been a gaping hole in the language since the beginning.

Does this proposal fit well with the feel and direction of Swift?

Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I prefer Java's automatic allValues(), but this is better than nothing.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed at least the start of several threads and read the proposal.

+1

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?

I like it. My minor, bike-shed vote is for "allCases": enumerations use the "case" keyword, so it provides nice symmetry.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. It's common to write this code manually, and it's uncommon to write code that stays synchronized with the enumeration (especially for non-Int-based).

Does this proposal fit well with the feel and direction of Swift?

Yes. Swift has been providing more and more useful things for end users by default (deriving Equatable, etc.).

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

In Haskell, you can derive "Enum" and "Bounded" for simple sum types and write an "allCases" function that stays in lockstep with the compiler. This is similar to the Int-based workaround mentioned in the proposal. Swift's auto-derivation and -definition would be even simpler.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick read.

Stephen

···

On Jan 8, 2018, at 2:02 PM, Douglas Gregor <dgregor@apple.com> wrote:

What is your evaluation of the proposal?

+1 it’s something I often hack myself

Is the problem being addressed significant enough to warrant a change to Swift?

Yes

Does this proposal fit well with the feel and direction of Swift?

Yes - I think it will encourage the use of enumerations in more places where they are appropriate

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?
How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

A quick reading and followed the issue when it came up before.

···

More information about the Swift evolution process is available at

swift-evolution/process.md at master · apple/swift-evolution · GitHub
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hello Swift community,

The review of SE-0194 "Derived Collection of Enum Cases" begins now and
runs through January 11, 2018. The proposal is available here:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:

Proposal link:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub

Reply text

Other replies

<Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub
goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

   - What is your evaluation of the proposal?

Big +1. I've been looking forward to something like this for a long time.

I'm extremely happy with the API chosen here. ValueEnumerable and allValues
are absolutely the correct names, because the protocol is not *restricted*
to enums, it is merely *synthesized automatically* for enums. That's a very
important distinction. Someone could provide their own conformance to
ValueEnumerable and use it in generic algorithms and have everything work
as expected.

In a perfect world I would argue that the associated type of allValues
should be a Sequence instead of a Collection as this would allow it to be
extended to infinite sequences (i.e., cases with associated values that are
deeply ValueEnumerable), but I'll admit that I've never come up with a
compelling use case for this beyond obscure combinatorial algorithms. Since
getting the count of values is important to many users, Collection is a
reasonable compromise and I don't have any objections.

   - Is the problem being addressed significant enough to warrant a
   change to Swift?

Yes. It's a frequently requested feature and has utility both in UI-driven

logic (table view sections based on an enum) and other algorithms.

   - Does this proposal fit well with the feel and direction of Swift?

Yes. The API design fits right into other stdlib concepts, and the

conditions for synthesis align with the synthesis of other synthesized
protocols.

   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

Just Java's values() function that is present on all enums. The

functionality provided here is what I would expect, similar to other
languages.

   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

An in-depth read and I was heavily involved in some of the earlier

discussion threads.

···

On Mon, Jan 8, 2018 at 11:02 AM Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

More information about the Swift evolution process is available at

swift-evolution/process.md at master · apple/swift-evolution · GitHub

Thank you,

-Doug Gregor

Review Manager
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Hello Swift community,

The review of SE-0194 "Derived Collection of Enum Cases" begins now and
runs through January 11, 2018. The proposal is available here:

https://github.com/apple/swift-evolution/blob/master/
proposals/0194-derived-collection-of-enum-cases.md

Reviews are an important part of the Swift evolution process. All reviews
should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution

or, if you would like to keep your feedback private, directly to the
review manager. When replying, please try to keep the proposal link at the
top of the message:

Proposal link:

https://github.com/apple/swift-evolution/blob/master/
proposals/0194-derived-collection-of-enum-cases.md

Reply text

Other replies

<Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub
goes into a review?

The goal of the review process is to improve the proposal under review
through constructive criticism and, eventually, determine the direction of
Swift. When writing your review, here are some questions you might want to
answer in your review:

   - What is your evaluation of the proposal?

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.

In the past, I have stated on this list that once a statement has been
made, it should not be repeated simply because one is disappointed in the
outcome, as one should operate on the presumption that all actors proceed
in good faith and have already considered the statement. Here, however,
given that the write-up literally denies the existence of what I have
already written, I will restate my concerns here once again. I would expect
that on revision the authors will properly record a considered reply.

My objection to the "ValueEnumerable" design--especially in its generalized
form here (versus "CaseEnumerable")--is that the protocol's semantics (and,
we'll recall, protocols in Swift must offer semantics that make possible
useful generic algorithms) are so broad as to be little more than
essentially what it means to be a type.

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`.

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).

Put another way, there are two issues with generalizing the very specific
use case of "I want to know all the cases of an enum" to what is proposed
here in terms of a protocol. 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, yet this proposal first makes a large generalization of the proposed
protocol's semantics when the stated motivation is only about enums, then
proceeds only to implement protocol conformance for enums. Second, the
collection of all values is properly just the type and not a property of
the type, for the reasons outlined above.

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.

   - Is the problem being addressed significant enough to warrant a
   change to Swift?

The specific use case of finding all cases of an enum is well motivated,

and significant enough. I see no motivation in the text for the proposed
generalized formulation of the proposed protocol's semantics and continue
to have grave concerns.

   - Does this proposal fit well with the feel and direction of Swift?

In my view, no.

   - If you have used other languages or libraries with a similar
   feature, how do you feel that this proposal compares to those?

The proposal compares this to reflection facilities in other languages. I

have used reflection in other languages, and the proposed semantics here
aren't entirely about that, nor does the proposed solution delineate how it
fits into a vision of Swift's future reflection facilities.

···

On Mon, Jan 8, 2018 at 1:02 PM, Douglas Gregor via swift-evolution < swift-evolution@swift.org> wrote:

   - How much effort did you put into your review? A glance, a quick
   reading, or an in-depth study?

In-depth study.

More information about the Swift evolution process is available at

swift-evolution/process.md at master · apple/swift-evolution · GitHub

Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

1 Like

What is your evaluation of the proposal?

+1. Yes please. Long overdue.

Is the problem being addressed significant enough to warrant a change to Swift?

It’s a long-standing sore thumb. The proposal’s evidence of community demand fits my own experience: I’ve wanted this on multiple occasions.

Does this proposal fit well with the feel and direction of Swift?

Yes, and in particular, on the name bikeshedding:

I favor property names with the “all” prefix, whether allValues or allCases. Looking over my own code, I’ve almost always used the word “all” for this when I had to hand-roll it — and either allValues or allCases make reasonable sense in my code when I substitute them.

Whichever protocol name we choose, the property name should be consistent:

  ValueEnumerable → allValues
  CaseEnumerable → allCases

Either ValueEnumerable or CaseEnumerable would be a fine name. Contra Chris, I slightly prefer ValueEnumerable, because it extends to situations where we still want to enumerate a fixed set of possibilities which don’t strictly correspond to enum cases but still have that sort of flavor. For example, one might want:

    enum SideOfBody
      {
      case left
      case right
      }

    enum Limb: ValueEnumerable
      {
      case arm(SideOfBody)
      case leg(SideOfBody)

      static let allValues =
        [
        arm(.left),
        arm(.right),
        leg(.left),
        leg(.right)
        ]
      }

To my eyes, this code reads better than it would with CaseEnumerable / allCases.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Java’s enums had this from the beginning, and Josh Bloch’s design for that feature has always worked nicely. Java’s design is slightly different: `Foo.values()` returns Foo. However, Swift doesn’t need to follow either that name or type choice: (1) Java doesn’t use the term “case” as Swift does, (2) the “all” prefix better fits Swift’s API guidelines IMO, and (3) using a concrete array type has as opposed to Collection has different implications in Java than it does Swift.

I _do_ agree that the proposal should consider constraining the Collection to be Int-indexed. Why should it ever be otherwise? What’s the motivation for leaving that open?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Medium quick study.

Cheers, P

What is your evaluation of the proposal?

+1. I have written plenty of enums with the `allValues` property. It will be nice to have this synthesized.

I do think a small clarification or revision is warranted, however. I would like to see a rationale stated for choosing the `Collection` requirement rather than `Sequence` or `RandomAccessCollection`. The `Sequence` alternative has been discussed in some depth on the list and this discussion is not represented in the alternatives section.

I bring up `RandomAccessCollection` as an alternative worth discussing because I strongly believe the proposal should require the compiler synthesized `ValueCollection` types to also conform to `RandomAccessCollection` and further have `Int` indices. One of the leading motivating use cases for this proposal is in implementing data sources which requires both `Int` indices and also carries an expectation of a constant time subscript operation.

Placing a stronger requirement on the compiler-synthesized implementation than we do on manual conformances is somewhat subtle so we should at least consider whether such requirements are acceptable for all conformances or not and address that in the proposal. I don’t know of any use cases for a manual conformance where meeting the stricter requirements would be problematic. On the other hand, requiring an `Int` index in particular seems like it is probably too strict.

I support the proposal regardless of the decision but believe additional discussion and documentation of the alternative constraints would be healthy.

Is the problem being addressed significant enough to warrant a change to Swift?

Absolutely. This is a very common source of boilerplate that is prone to break as code changes.

Does this proposal fit well with the feel and direction of Swift?

Yes.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

N/A

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

In-depth study and participation in prior threads.

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md\]

I think this is generally reasonable, and none of the names offend me enough to weigh in on that discussion. I do think it's a little weird that @objc enums defined in Swift cannot conform to ValueEnumerable, just because imported enums won't. (But especially while knee-deep in SE-0192, I think it's correct that imported enums won't. The exception could be C enums marked `enum_extensibility(closed)`, but I'm not convinced we need that yet.)

The biggest problem I have is unavailable cases. An unavailable case must not be instantiated—consider an enum where some cases are only available on iOS and not macOS. (I bet we optimize based on this, which makes it all the more important to get right.)

I think you should explicitly call out that the derived implementation only kicks in when ValueEnumerable is declared on the enum itself, not an extension. Or if that's not the case, it should be limited to extensions in the same module as the enum. (You could add "unless the enum is '@frozen'", but that's not really necessary.)

I don't think this should be implemented with a run-time function; compile-time code generation makes more sense to me. But that's an implementation detail; it doesn't change the language surface.

Jordan

I’m overall +1, but I’m curious: would you be able to conform an enum from another module to ValueEnumerable via an extension, and still have the compiler generate the protocol requirements for you? I can imagine that the client of a framework with an enum may have a valid use for iterating over all values that the author didn’t foresee, and I would very much like the client to be able to opt-in to the compiler code generation.

(Personally, I’d prefer that all simple enums automatically conform to the protocol and have automatic implementations, since I don’t really see a downside to this that exists for some other compiler-magic protocols like Codable)

···

On Jan 9, 2018, at 8:45 AM, Tony Allevato via swift-evolution <swift-evolution@swift.org> wrote:

On Mon, Jan 8, 2018 at 11:02 AM Douglas Gregor via swift-evolution <swift-evolution@swift.org> wrote:
Hello Swift community,

The review of SE-0194 "Derived Collection of Enum Cases" begins now and runs through January 11, 2018. The proposal is available here:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub
Reviews are an important part of the Swift evolution process. All reviews should be sent to the swift-evolution mailing list at

https://lists.swift.org/mailman/listinfo/swift-evolution
or, if you would like to keep your feedback private, directly to the review manager. When replying, please try to keep the proposal link at the top of the message:

Proposal link:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub
Reply text
Other replies
What goes into a review?

The goal of the review process is to improve the proposal under review through constructive criticism and, eventually, determine the direction of Swift. When writing your review, here are some questions you might want to answer in your review:

What is your evaluation of the proposal?

Big +1. I've been looking forward to something like this for a long time.

I'm extremely happy with the API chosen here. ValueEnumerable and allValues are absolutely the correct names, because the protocol is not *restricted* to enums, it is merely *synthesized automatically* for enums. That's a very important distinction. Someone could provide their own conformance to ValueEnumerable and use it in generic algorithms and have everything work as expected.

In a perfect world I would argue that the associated type of allValues should be a Sequence instead of a Collection as this would allow it to be extended to infinite sequences (i.e., cases with associated values that are deeply ValueEnumerable), but I'll admit that I've never come up with a compelling use case for this beyond obscure combinatorial algorithms. Since getting the count of values is important to many users, Collection is a reasonable compromise and I don't have any objections.

Is the problem being addressed significant enough to warrant a change to Swift?

Yes. It's a frequently requested feature and has utility both in UI-driven logic (table view sections based on an enum) and other algorithms.

Does this proposal fit well with the feel and direction of Swift?

Yes. The API design fits right into other stdlib concepts, and the conditions for synthesis align with the synthesis of other synthesized protocols.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Just Java's values() function that is present on all enums. The functionality provided here is what I would expect, similar to other languages.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

An in-depth read and I was heavily involved in some of the earlier discussion threads.

More information about the Swift evolution process is available at

swift-evolution/process.md at master · apple/swift-evolution · GitHub
Thank you,

-Doug Gregor

Review Manager

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

Proposal link:

swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub

What is your evaluation of the proposal?

+1 on the general problem, but I have a few comments on the design specifics

- I'd very much prefer automatic synthesis with enums,
- I strongly feel the compiler should auto-generate the implementation in an extension for enums.
- Should we add `ValueCollection.Index == Int` to the associatedtype so it can always be indexed as in a table?
  - At the least, we should ensure the synthesized implementation is int-indexed for tables.
- I feel we should allow auto-generation of imported c enums in an extension, as just an array of the known cases rather than the magical metadata iterator if necessary.

+1 for known cases at compile time.

···

On Jan 9, 2018, at 10:43 AM, Kevin Nattinger via swift-evolution <swift-evolution@swift.org> wrote:

Is the problem being addressed significant enough to warrant a change to Swift?

Definitely. It's been a gaping hole in the language since the beginning.

Does this proposal fit well with the feel and direction of Swift?

Yes

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

I prefer Java's automatic allValues(), but this is better than nothing.

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed at least the start of several threads and read the proposal.
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

My objection to the "ValueEnumerable" design--especially in its generalized form here (versus "CaseEnumerable")--is that the protocol's semantics (and, we'll recall, protocols in Swift must offer semantics that make possible useful generic algorithms) are so broad as to be little more than essentially what it means to be a type.

Thank you for writing this up Xiaodi. I completely agree with you that “CaseEnumerable” is a better name for this: it is more specific and purposeful and make it more clear what the purpose and scope is.

Your later suggestion of “Enumerable” seems to broad and potentially confusing to me. Tying it to the things that are being enumerated (enum cases) seems more purposeful.

Brent just wrote that he might later propose to extend `ValueEnumerable` to `Bool` and `Optional`,

To be fair, Optional *is* an enum, and Bool really should be (we only switched it to its current design for internal optimizer reasons).

Making Bool conform to this would require manual conformance - if we were motivated to give Bool all the enum-like facilities (including a Bool.true and Bool.false member) then having it conform seems conceptually fine to me.

I would personally object to attempts to make optional conform though. One of its cases has an associated value and this isn’t something we should support IMO. I believe that this is one of the roots of your objection.

-Chris

···

On Jan 9, 2018, at 10:26 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

    Proposal link:

        swift-evolution/0194-derived-collection-of-enum-cases.md at master · apple/swift-evolution · GitHub

          <Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub;

  * What is your evaluation of the proposal?

+1 on the general problem, but I have a few comments on the design specifics

- I'd very much prefer automatic synthesis with enums,
- I strongly feel the compiler should auto-generate the implementation in an extension for enums.
- Should we add `ValueCollection.Index == Int` to the associatedtype so it can always be indexed as in a table?
� - At the least, we should ensure the synthesized implementation is int-indexed for tables.
- I feel we should allow auto-generation of imported c enums in an extension, as just an array of the known cases rather than the magical metadata iterator if necessary.

FWIW +1 for all Kevin said

···

On 09.01.2018 21:43, Kevin Nattinger via swift-evolution wrote:

  * Is the problem being addressed significant enough to warrant a change to Swift?

Definitely. It's been a gaping hole in the language since the beginning.

  * Does this proposal fit well with the feel and direction of Swift?

Yes

  * If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares
    to those?

I prefer Java's automatic allValues(), but this is better than nothing.

  * How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Followed at least the start of several threads and read the proposal.

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

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:

  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.

···

On Jan 9, 2018, at 10:26 PM, Xiaodi Wu via swift-evolution <swift-evolution@swift.org> wrote:

--
Brent Royal-Gordon
Architechies

I read:

Ma.allValues.count // returns 8

But that sounds like all values will need to be computed in order to get
the count, so some people will be tempted to write:

Ma.lazy.allValues.count // returns 8

To avoid that, it may be nicer to make enum `Ma` behaves like a collection
directly, so that we can write:

Ma.count // returns 8
Ma.map { return "\($0)" } // returns a stringification

A little bit like what was done on String when we dropped the requirement
of writing `.characters`, it would be perfect to directly drop the
requirement of writing `.allValues`.

···

Le mer. 10 janv. 2018 à 14:40, Chris Lattner via swift-evolution < swift-evolution@swift.org> a écrit :

On Jan 9, 2018, at 10:26 PM, Xiaodi Wu via swift-evolution < > swift-evolution@swift.org> wrote:
> My objection to the "ValueEnumerable" design--especially in its
generalized form here (versus "CaseEnumerable")--is that the protocol's
semantics (and, we'll recall, protocols in Swift must offer semantics that
make possible useful generic algorithms) are so broad as to be little more
than essentially what it means to be a type.

Thank you for writing this up Xiaodi. I completely agree with you that
“CaseEnumerable” is a better name for this: it is more specific and
purposeful and make it more clear what the purpose and scope is.

Your later suggestion of “Enumerable” seems to broad and potentially
confusing to me. Tying it to the things that are being enumerated (enum
cases) seems more purposeful.

> Brent just wrote that he might later propose to extend `ValueEnumerable`
to `Bool` and `Optional`,

To be fair, Optional *is* an enum, and Bool really should be (we only
switched it to its current design for internal optimizer reasons).

Making Bool conform to this would require manual conformance - if we were
motivated to give Bool all the enum-like facilities (including a Bool.true
and Bool.false member) then having it conform seems conceptually fine to me.

I would personally object to attempts to make optional conform though. One
of its cases has an associated value and this isn’t something we should
support IMO. I believe that this is one of the roots of your objection.

-Chris

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

What is your evaluation of the proposal?

+1. Yes please. Long overdue.

Is the problem being addressed significant enough to warrant a change to Swift?

It’s a long-standing sore thumb. The proposal’s evidence of community demand fits my own experience: I’ve wanted this on multiple occasions.

Does this proposal fit well with the feel and direction of Swift?

Yes, and in particular, on the name bikeshedding:

I favor property names with the “all” prefix, whether allValues or allCases. Looking over my own code, I’ve almost always used the word “all” for this when I had to hand-roll it — and either allValues or allCases make reasonable sense in my code when I substitute them.

Whichever protocol name we choose, the property name should be consistent:

  ValueEnumerable → allValues
  CaseEnumerable → allCases

this is good point. I think it would be awesome to also have a compile time version named .cases.

.allCases would include unknown cases at runtime.
.cases would only include known at compile time cases.

···

On Jan 10, 2018, at 8:22 AM, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

Either ValueEnumerable or CaseEnumerable would be a fine name. Contra Chris, I slightly prefer ValueEnumerable, because it extends to situations where we still want to enumerate a fixed set of possibilities which don’t strictly correspond to enum cases but still have that sort of flavor. For example, one might want:

    enum SideOfBody
      {
      case left
      case right
      }

    enum Limb: ValueEnumerable
      {
      case arm(SideOfBody)
      case leg(SideOfBody)

      static let allValues =
        [
        arm(.left),
        arm(.right),
        leg(.left),
        leg(.right)
        ]
      }

To my eyes, this code reads better than it would with CaseEnumerable / allCases.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Java’s enums had this from the beginning, and Josh Bloch’s design for that feature has always worked nicely. Java’s design is slightly different: `Foo.values()` returns Foo. However, Swift doesn’t need to follow either that name or type choice: (1) Java doesn’t use the term “case” as Swift does, (2) the “all” prefix better fits Swift’s API guidelines IMO, and (3) using a concrete array type has as opposed to Collection has different implications in Java than it does Swift.

I _do_ agree that the proposal should consider constraining the Collection to be Int-indexed. Why should it ever be otherwise? What’s the motivation for leaving that open?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Medium quick study.

Cheers, P

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I don’t agree that the Collection should be Int-indexed. Source-order is not a very strong guarantee IMO, and it wouldn’t be good if people started writing things like "MyEnum.allValues[3]” to reference a specific case.

If you know the specific case you are looking for, just write it directly. If you found an interesting case while iterating allValues, remember its (opaque) index and come back to it later.

I’m not a fan of Int-indexes in general. It’s practical to allow it for Array, but in general, for generic Collections, I think it implies an awful lot of knowledge about the Collection’s contents.

- Karl

···

On 10. Jan 2018, at 17:22, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

What is your evaluation of the proposal?

+1. Yes please. Long overdue.

Is the problem being addressed significant enough to warrant a change to Swift?

It’s a long-standing sore thumb. The proposal’s evidence of community demand fits my own experience: I’ve wanted this on multiple occasions.

Does this proposal fit well with the feel and direction of Swift?

Yes, and in particular, on the name bikeshedding:

I favor property names with the “all” prefix, whether allValues or allCases. Looking over my own code, I’ve almost always used the word “all” for this when I had to hand-roll it — and either allValues or allCases make reasonable sense in my code when I substitute them.

Whichever protocol name we choose, the property name should be consistent:

  ValueEnumerable → allValues
  CaseEnumerable → allCases

Either ValueEnumerable or CaseEnumerable would be a fine name. Contra Chris, I slightly prefer ValueEnumerable, because it extends to situations where we still want to enumerate a fixed set of possibilities which don’t strictly correspond to enum cases but still have that sort of flavor. For example, one might want:

    enum SideOfBody
      {
      case left
      case right
      }

    enum Limb: ValueEnumerable
      {
      case arm(SideOfBody)
      case leg(SideOfBody)

      static let allValues =
        [
        arm(.left),
        arm(.right),
        leg(.left),
        leg(.right)
        ]
      }

To my eyes, this code reads better than it would with CaseEnumerable / allCases.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Java’s enums had this from the beginning, and Josh Bloch’s design for that feature has always worked nicely. Java’s design is slightly different: `Foo.values()` returns Foo. However, Swift doesn’t need to follow either that name or type choice: (1) Java doesn’t use the term “case” as Swift does, (2) the “all” prefix better fits Swift’s API guidelines IMO, and (3) using a concrete array type has as opposed to Collection has different implications in Java than it does Swift.

I _do_ agree that the proposal should consider constraining the Collection to be Int-indexed. Why should it ever be otherwise? What’s the motivation for leaving that open?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Medium quick study.

Cheers, P

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md\]

I think this is generally reasonable, and none of the names offend me enough to weigh in on that discussion. I do think it's a little weird that @objc enums defined in Swift /cannot/ conform to ValueEnumerable, just because imported enums won't. (But especially while knee-deep in SE-0192, I think it's /correct/ that imported enums won't. The exception could be C enums marked `enum_extensibility(closed)`, but I'm not convinced we need that yet.)

The biggest problem I have is unavailable cases. An unavailable case /must not/ be instantiated—consider an enum where some cases are only available on iOS and not macOS. (I bet we optimize based on this, which makes it all the more important to get right.)

I think you should explicitly call out that the derived implementation only kicks in when ValueEnumerable is declared on the enum itself, not an extension. Or if that's not the case, it should be limited to extensions in the same module as the enum. (You could add "unless the enum is '@frozen'", but that's not really necessary.)

I'd also ask to append that information into the proposal, as IMO it is very important point.
I was under impression that we'll be allowed to retroactively conform to ValueEnumerable for frozen enums from other modules. Wouldn't this be a very requested feature of ValueEnumerable ? I.e. when we have a module with frozen enum, but author of that enum didn't consider conforming to ValueEnumerable for some reason.

Vladimir.

···

On 11.01.2018 1:54, Jordan Rose via swift-evolution wrote:

I don't think this should be implemented with a run-time function; compile-time code generation makes more sense to me. But that's an implementation detail; it doesn't change the language surface.

Jordan

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

An alternative is a special #knownCases(of:) literal.

Its value is an array literal of the enum cases known at compile time.

This could also work with enums imported from Objective-C.

-- Ben

···

On 10 Jan 2018, at 22:54, Jordan Rose wrote:

[Proposal: https://github.com/apple/swift-evolution/blob/master/proposals/0194-derived-collection-of-enum-cases.md\]

I think this is generally reasonable, and none of the names offend me enough to weigh in on that discussion. I do think it's a little weird that @objc enums defined in Swift cannot conform to ValueEnumerable, just because imported enums won't. (But especially while knee-deep in SE-0192, I think it's correct that imported enums won't. The exception could be C enums marked `enum_extensibility(closed)`, but I'm not convinced we need that yet.)

The biggest problem I have is unavailable cases. An unavailable case must not be instantiated—consider an enum where some cases are only available on iOS and not macOS. (I bet we optimize based on this, which makes it all the more important to get right.)

I think you should explicitly call out that the derived implementation only kicks in when ValueEnumerable is declared on the enum itself, not an extension. Or if that's not the case, it should be limited to extensions in the same module as the enum. (You could add "unless the enum is '@frozen'", but that's not really necessary.)

I don't think this should be implemented with a run-time function; compile-time code generation makes more sense to me. But that's an implementation detail; it doesn't change the language surface.

Jordan

1 Like

Simple is better. Int indexes, please.

···

--
C. Keith Ray

* What Every Programmer Needs To… by C. Keith Ray [PDF/iPad/Kindle] <- buy my book?
* http://www.thirdfoundationsw.com/keith_ray_resume_2014_long.pdf
* http://agilesolutionspace.blogspot.com/

On Jan 10, 2018, at 10:44 AM, Karl Wagner via swift-evolution <swift-evolution@swift.org> wrote:

On 10. Jan 2018, at 17:22, Paul Cantrell via swift-evolution <swift-evolution@swift.org> wrote:

What is your evaluation of the proposal?

+1. Yes please. Long overdue.

Is the problem being addressed significant enough to warrant a change to Swift?

It’s a long-standing sore thumb. The proposal’s evidence of community demand fits my own experience: I’ve wanted this on multiple occasions.

Does this proposal fit well with the feel and direction of Swift?

Yes, and in particular, on the name bikeshedding:

I favor property names with the “all” prefix, whether allValues or allCases. Looking over my own code, I’ve almost always used the word “all” for this when I had to hand-roll it — and either allValues or allCases make reasonable sense in my code when I substitute them.

Whichever protocol name we choose, the property name should be consistent:

  ValueEnumerable → allValues
  CaseEnumerable → allCases

Either ValueEnumerable or CaseEnumerable would be a fine name. Contra Chris, I slightly prefer ValueEnumerable, because it extends to situations where we still want to enumerate a fixed set of possibilities which don’t strictly correspond to enum cases but still have that sort of flavor. For example, one might want:

    enum SideOfBody
      {
      case left
      case right
      }

    enum Limb: ValueEnumerable
      {
      case arm(SideOfBody)
      case leg(SideOfBody)

      static let allValues =
        [
        arm(.left),
        arm(.right),
        leg(.left),
        leg(.right)
        ]
      }

To my eyes, this code reads better than it would with CaseEnumerable / allCases.

If you have used other languages or libraries with a similar feature, how do you feel that this proposal compares to those?

Java’s enums had this from the beginning, and Josh Bloch’s design for that feature has always worked nicely. Java’s design is slightly different: `Foo.values()` returns Foo. However, Swift doesn’t need to follow either that name or type choice: (1) Java doesn’t use the term “case” as Swift does, (2) the “all” prefix better fits Swift’s API guidelines IMO, and (3) using a concrete array type has as opposed to Collection has different implications in Java than it does Swift.

I _do_ agree that the proposal should consider constraining the Collection to be Int-indexed. Why should it ever be otherwise? What’s the motivation for leaving that open?

How much effort did you put into your review? A glance, a quick reading, or an in-depth study?

Medium quick study.

Cheers, P

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

I don’t agree that the Collection should be Int-indexed. Source-order is not a very strong guarantee IMO, and it wouldn’t be good if people started writing things like "MyEnum.allValues[3]” to reference a specific case.

If you know the specific case you are looking for, just write it directly. If you found an interesting case while iterating allValues, remember its (opaque) index and come back to it later.

I’m not a fan of Int-indexes in general. It’s practical to allow it for Array, but in general, for generic Collections, I think it implies an awful lot of knowledge about the Collection’s contents.

- Karl
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution