Proposal: Enum 'count' functionality


(Andyy Hope) #1

Hi Swift Team and Community

I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to Enums for the Swift language
https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

Comments, feedback, scenario appends very much welcomed. Thank you

···

--
Andyy Hope
iOS Developer, Melbourne
@andyyhope
andyyhope.com


(Brent Royal-Gordon) #2

I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to Enums for the Swift language
https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

I think we'd be much better off having a list of all cases. That would make this feature useful for enums which don't have a raw type, or don't use Int as their raw type, or don't use sequential raw values for their cases.

That approach has been discussed before, but not formally proposed.

···

--
Brent Royal-Gordon
Architechies


(Charles Constant) #3

If you change your proposal to focus on ".values" instead
of ".values.count" (which you'd get for free, as you note) I would strongly
support it. I don't need ".count" on its own, but ".values" ... well, I
have a whole load of enums in my current projects with a ".values" I had to
hardcode myself. I would love to get that for free.

···

On Mon, Dec 21, 2015 at 12:46 AM, Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

> I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to
Enums for the Swift language
> https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

I think we'd be much better off having a list of all cases. That would
make this feature useful for enums which don't have a raw type, or don't
use Int as their raw type, or don't use sequential raw values for their
cases.

That approach has been discussed before, but not formally proposed.

--
Brent Royal-Gordon
Architechies

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


#4

Does the naming "values" have precedence elsewhere? To me, "cases" is clearer, whereas "rawValues" would make sense if there was an additional, auto-generated static property for enumerations with underlying values.

    enum Section: Int { case SectionA, Section B }
    Section.cases // [.SectionA, .SectionB]
    Section.rawValues // [0, 1]

Because "rawValue" already exists, "values" makes things a bit more confusing: are they the cases themselves or are they their underlying values?

Stephen

···

On Dec 21, 2015, at 3:57 AM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

If you change your proposal to focus on ".values" instead of ".values.count" (which you'd get for free, as you note) I would strongly support it. I don't need ".count" on its own, but ".values" ... well, I have a whole load of enums in my current projects with a ".values" I had to hardcode myself. I would love to get that for free.

On Mon, Dec 21, 2015 at 12:46 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
> I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to Enums for the Swift language
> https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

I think we'd be much better off having a list of all cases. That would make this feature useful for enums which don't have a raw type, or don't use Int as their raw type, or don't use sequential raw values for their cases.

That approach has been discussed before, but not formally proposed.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


#5

Would .cases return the enum’s cases in source-code order? That seems
fragile at first glance.

It might be worth considering to have it return an unordered collection
(set) instead.

Nevin

···

On Mon, Dec 21, 2015 at 10:52 AM, Stephen Celis via swift-evolution < swift-evolution@swift.org> wrote:

Does the naming "values" have precedence elsewhere? To me, "cases" is
clearer, whereas "rawValues" would make sense if there was an additional,
auto-generated static property for enumerations with underlying values.

    enum Section: Int { case SectionA, Section B }
    Section.cases // [.SectionA, .SectionB]
    Section.rawValues // [0, 1]

Because "rawValue" already exists, "values" makes things a bit more
confusing: are they the cases themselves or are they their underlying
values?

Stephen

On Dec 21, 2015, at 3:57 AM, Charles Constant via swift-evolution < > swift-evolution@swift.org> wrote:

If you change your proposal to focus on ".values" instead
of ".values.count" (which you'd get for free, as you note) I would strongly
support it. I don't need ".count" on its own, but ".values" ... well, I
have a whole load of enums in my current projects with a ".values" I had to
hardcode myself. I would love to get that for free.

On Mon, Dec 21, 2015 at 12:46 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

> I’ve put a proposal up on GitHub to add a cases ‘count’ functionality
to Enums for the Swift language
> https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

I think we'd be much better off having a list of all cases. That would
make this feature useful for enums which don't have a raw type, or don't
use Int as their raw type, or don't use sequential raw values for their
cases.

That approach has been discussed before, but not formally proposed.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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

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


#6

Care to elaborate on why you think it may be fragile?

Retaining order makes sense to me given that integer-backed enumerations auto-increment and that enumerations cannot be extended with new cases. I'm not opposed to using a set, though, if that makes more sense.

Stephen

···

On Dec 21, 2015, at 11:27 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com> wrote:

Would .cases return the enum’s cases in source-code order? That seems fragile at first glance.

It might be worth considering to have it return an unordered collection (set) instead.


(Jacob Bandes-Storch) #7

I brought up this topic a couple weeks ago in a thread called "List of all
Enum values (for simple enums)":

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html

I suppose it's time to start synthesizing those ideas/discussion into a
proposal :slight_smile:

Jacob

···

On Mon, Dec 21, 2015 at 8:35 AM, Stephen Celis via swift-evolution < swift-evolution@swift.org> wrote:

On Dec 21, 2015, at 11:27 AM, Nevin Brackett-Rozinsky < > nevin.brackettrozinsky@gmail.com> wrote:

Would .cases return the enum’s cases in source-code order? That seems
fragile at first glance.

It might be worth considering to have it return an unordered collection
(set) instead.

Care to elaborate on why you think it may be fragile?

Retaining order makes sense to me given that integer-backed enumerations
auto-increment and that enumerations cannot be extended with new cases. I'm
not opposed to using a set, though, if that makes more sense.

Stephen

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


(Joe Groff) #8

There's only one kind of enum fundamentally, and I think we should make for more continuity between different instances of enum rather than less. Any type with a reasonably finite number of states ought to be able to support a `values` collection. If it happens to have a RawRepresentable conformance, mapping from values to rawValues is easy, and could be provided by a protocol extension on ValueEnumerable where Self: RawRepresentable. Enums with associated values that are themselves ValueEnumerable could enumerate all the values of the associated value, so something like this:

enum Foo: ValueEnumerable { case A, B, C }

enum Bar: ValueEnumerable { case X(Foo), Y(Foo) }

would give you `.X(.A)`, `.X(.B)`, `.X(.C)`, etc. (You could also derive ValueEnumerable for structs by walking the cartesian product of the stored properties' values, but I don't know if that's really useful.)

-Joe

···

On Dec 21, 2015, at 12:09 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

My wild aspirations in a nutshell:

Core enums: Any enum that's created without raw or associated values, e.g. enum MyEnum {case This, That, Whatever, Etc}, can (and should) be Array<Self> representable. This would add intrinsic ordering and raw value construction starting with 0, up to count - 1. End-devs could use the ordering or not use the ordering, but it would be possible to convert to bit representation (1 << this.rawValue), support iteration through the enumeration, introduce ranges for switches, etc. A massive improvement.

Raw value enums: Any enum that uses raw values, e.g. enum ForExample: String {case Hello = "hello", There = "there"} should be representable as Set<T>

Associated type enums: all bets are off


(Erica Sadun) #9

My wild aspirations in a nutshell:

Core enums: Any enum that's created without raw or associated values, e.g. enum MyEnum {case This, That, Whatever, Etc}, can (and should) be Array<Self> representable. This would add intrinsic ordering and raw value construction starting with 0, up to count - 1. End-devs could use the ordering or not use the ordering, but it would be possible to convert to bit representation (1 << this.rawValue), support iteration through the enumeration, introduce ranges for switches, etc. A massive improvement.

Raw value enums: Any enum that uses raw values, e.g. enum ForExample: String {case Hello = "hello", There = "there"} should be representable as Set<T>

Associated type enums: all bets are off

-- E

···

On Dec 21, 2015, at 12:52 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

I brought up this topic a couple weeks ago in a thread called "List of all Enum values (for simple enums)":

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html

I suppose it's time to start synthesizing those ideas/discussion into a proposal :slight_smile:

Jacob

On Mon, Dec 21, 2015 at 8:35 AM, Stephen Celis via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Dec 21, 2015, at 11:27 AM, Nevin Brackett-Rozinsky <nevin.brackettrozinsky@gmail.com <mailto:nevin.brackettrozinsky@gmail.com>> wrote:

Would .cases return the enum’s cases in source-code order? That seems fragile at first glance.

It might be worth considering to have it return an unordered collection (set) instead.

Care to elaborate on why you think it may be fragile?

Retaining order makes sense to me given that integer-backed enumerations auto-increment and that enumerations cannot be extended with new cases. I'm not opposed to using a set, though, if that makes more sense.

Stephen

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto: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


(Erica Sadun) #10

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:

-- E

···

On Dec 21, 2015, at 1:14 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 21, 2015, at 12:09 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

My wild aspirations in a nutshell:

Core enums: Any enum that's created without raw or associated values, e.g. enum MyEnum {case This, That, Whatever, Etc}, can (and should) be Array<Self> representable. This would add intrinsic ordering and raw value construction starting with 0, up to count - 1. End-devs could use the ordering or not use the ordering, but it would be possible to convert to bit representation (1 << this.rawValue), support iteration through the enumeration, introduce ranges for switches, etc. A massive improvement.

Raw value enums: Any enum that uses raw values, e.g. enum ForExample: String {case Hello = "hello", There = "there"} should be representable as Set<T>

Associated type enums: all bets are off

There's only one kind of enum fundamentally, and I think we should make for more continuity between different instances of enum rather than less. Any type with a reasonably finite number of states ought to be able to support a `values` collection. If it happens to have a RawRepresentable conformance, mapping from values to rawValues is easy, and could be provided by a protocol extension on ValueEnumerable where Self: RawRepresentable. Enums with associated values that are themselves ValueEnumerable could enumerate all the values of the associated value, so something like this:

enum Foo: ValueEnumerable { case A, B, C }

enum Bar: ValueEnumerable { case X(Foo), Y(Foo) }

would give you `.X(.A)`, `.X(.B)`, `.X(.C)`, etc. (You could also derive ValueEnumerable for structs by walking the cartesian product of the stored properties' values, but I don't know if that's really useful.)

-Joe


(Joe Groff) #11

If the value collection were sufficiently capable, you wouldn't necessarily need an implicit Int raw value; you could do `allValues.indexOf(.A)` to get the index for a case.

-Joe

···

On Dec 21, 2015, at 12:24 PM, Erica Sadun <erica@ericasadun.com> wrote:

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:


(Erica Sadun) #12

That would be okay too. Thank you, Santa Joe.

-- E, who hopes she was on the nice list.

···

On Dec 21, 2015, at 3:56 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 21, 2015, at 12:24 PM, Erica Sadun <erica@ericasadun.com> wrote:

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:

If the value collection were sufficiently capable, you wouldn't necessarily need an implicit Int raw value; you could do `allValues.indexOf(.A)` to get the index for a case.

-Joe


(Zef Houssney) #13

I agree with Stephen Celis that the best names for this (yet) are definitely `cases` and optionally `rawValues` if the cases are RawRepresentable.

Regarding handling cases with associated values where some of those values are other enums, it seems odd to me to try to return every variation of each case. I can’t picture how that would work sanely when there are multiple associated values, such as a case like `MyCase(String, SomeEnum)`. Therefore I prefer the idea of having the array contain the constructor for cases with associated values. The main problem I see here is that the type system (as far as I know) can’t fully represent that type of thing, but I think it’s still potentially valuable. Related to this I’m putting together a separate proposal that explores a bit more on why I believe the enum constructor to be valuable, though that’s in a bit of a different context.

I shared these thoughts in a bit more detail here a couple weeks ago, with some followup in the next two messages:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001864.html

Zef

···

On Dec 21, 2015, at 3:58 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

That would be okay too. Thank you, Santa Joe.

-- E, who hopes she was on the nice list.

On Dec 21, 2015, at 3:56 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 21, 2015, at 12:24 PM, Erica Sadun <erica@ericasadun.com> wrote:

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:

If the value collection were sufficiently capable, you wouldn't necessarily need an implicit Int raw value; you could do `allValues.indexOf(.A)` to get the index for a case.

-Joe

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


(Howard Lovatt) #14

+1 for ".values" instead of just ".count". Really handy feature that Java has. In Java it returns a set not an array which is great for representing status. In C you would use a bit field, but an enum set is better.

···

Sent from my iPad

On 21 Dec 2015, at 7:57 PM, Charles Constant via swift-evolution <swift-evolution@swift.org> wrote:

If you change your proposal to focus on ".values" instead of ".values.count" (which you'd get for free, as you note) I would strongly support it. I don't need ".count" on its own, but ".values" ... well, I have a whole load of enums in my current projects with a ".values" I had to hardcode myself. I would love to get that for free.

On Mon, Dec 21, 2015 at 12:46 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:
> I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to Enums for the Swift language
> https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

I think we'd be much better off having a list of all cases. That would make this feature useful for enums which don't have a raw type, or don't use Int as their raw type, or don't use sequential raw values for their cases.

That approach has been discussed before, but not formally proposed.

--
Brent Royal-Gordon
Architechies

_______________________________________________
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


(Andyy Hope) #15

I've updated the gist to include alternative collection types and naming for the enum values

Andyy Hope
iOS Developer, Melbourne
@andyyhope
andyyhope.com

···

On 22 Dec 2015, at 7:24 AM, Erica Sadun <erica@ericasadun.com> wrote:

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:

-- E

On Dec 21, 2015, at 1:14 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 21, 2015, at 12:09 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

My wild aspirations in a nutshell:

Core enums: Any enum that's created without raw or associated values, e.g. enum MyEnum {case This, That, Whatever, Etc}, can (and should) be Array<Self> representable. This would add intrinsic ordering and raw value construction starting with 0, up to count - 1. End-devs could use the ordering or not use the ordering, but it would be possible to convert to bit representation (1 << this.rawValue), support iteration through the enumeration, introduce ranges for switches, etc. A massive improvement.

Raw value enums: Any enum that uses raw values, e.g. enum ForExample: String {case Hello = "hello", There = "there"} should be representable as Set<T>

Associated type enums: all bets are off

There's only one kind of enum fundamentally, and I think we should make for more continuity between different instances of enum rather than less. Any type with a reasonably finite number of states ought to be able to support a `values` collection. If it happens to have a RawRepresentable conformance, mapping from values to rawValues is easy, and could be provided by a protocol extension on ValueEnumerable where Self: RawRepresentable. Enums with associated values that are themselves ValueEnumerable could enumerate all the values of the associated value, so something like this:

enum Foo: ValueEnumerable { case A, B, C }

enum Bar: ValueEnumerable { case X(Foo), Y(Foo) }

would give you `.X(.A)`, `.X(.B)`, `.X(.C)`, etc. (You could also derive ValueEnumerable for structs by walking the cartesian product of the stored properties' values, but I don't know if that's really useful.)

-Joe


(Bart Whiteley) #16

I would discourage adding just a `.count`. I've heard a few people ask for
this over the last year. In almost all cases it is due to people falling
back to the way they dealt with enums in Objective C when in fact there is
usually a better way in Swift. Adding `.count` would inhibit the discovery
of better ways of doing things.

However, I'm having a hard time arguing against `.cases`. In at least one
place I currently have an `.allCases` computed property on an enum.

···

On Mon, Dec 21, 2015 at 1:42 AM, Andyy Hope via swift-evolution < swift-evolution@swift.org> wrote:

Hi Swift Team and Community

I’ve put a proposal up on GitHub to add a cases ‘count’ functionality to
Enums for the Swift language
https://gist.github.com/andyyhope/2fc5b6bee8ee1346f688

Comments, feedback, scenario appends very much welcomed. Thank you

--
bart


(Extelligent Cocoa) #17

I'd just like to +1 this as it's a feature I often use - enums are perfect for limiting user choices (days of the week, anyone?) and having to manually create and update the list of cases would be much easier and safer with a dedicated feature. (I'd prefer for it not to be a set because a set has no inbuilt order; if it were returned as a set I'd then have to create an array manually anyway; I want my weekdays to appear neither randomly sorted nor alphabetical.

Catja Pafort

···

Brent Royal-Gordon <mailto:swift-evolution@swift.org> wrote:

I think we'd be much better off having a list of all cases. That would make this feature useful for enums which don't have a raw type, or don't use Int as their raw type, or don't use sequential raw values for their cases.

That approach has been discussed before, but not formally proposed.

--
www.extelligentcocoa.org


(Joe Groff) #18

I agree with Stephen Celis that the best names for this (yet) are definitely `cases` and optionally `rawValues` if the cases are RawRepresentable.

Regarding handling cases with associated values where some of those values are other enums, it seems odd to me to try to return every variation of each case. I can’t picture how that would work sanely when there are multiple associated values, such as a case like `MyCase(String, SomeEnum)`. Therefore I prefer the idea of having the array contain the constructor for cases with associated values. The main problem I see here is that the type system (as far as I know) can’t fully represent that type of thing, but I think it’s still potentially valuable. Related to this I’m putting together a separate proposal that explores a bit more on why I believe the enum constructor to be valuable, though that’s in a bit of a different context.

Generating a list of values for MyCase(String, SomeEnum) wouldn't make sense because there are an infinite number of Strings, so Strings don't make sense as a value-enumerable type. However, you could have a mostly-flat enum that uses subenums for organization:

enum Food {
  enum Fruit {
    case Apple, Banana, BellPepper
  }
  case fruit(Fruit)

  enum Meat {
    case Beef, Pork, Venison
  }
  case meat(Meat)
}

In many C family code bases it's common to group related enums by order, even if there's otherwise no intrinsic order among the elements. Preserving the structure of the cases in the type system makes them easier to understand and work with in pattern-matching, since you can handle all fruits by matching `.fruit(_)` instead of having to maintain `food >= FirstFruit && foo <= LastFruit` ranges. For these kinds of enums, recursively generating the associated values is still useful.

-Joe

···

On Dec 21, 2015, at 10:33 PM, Zef Houssney <zefmail@gmail.com> wrote:

I shared these thoughts in a bit more detail here a couple weeks ago, with some followup in the next two messages:

https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001864.html

Zef

On Dec 21, 2015, at 3:58 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

That would be okay too. Thank you, Santa Joe.

-- E, who hopes she was on the nice list.

On Dec 21, 2015, at 3:56 PM, Joe Groff <jgroff@apple.com> wrote:

On Dec 21, 2015, at 12:24 PM, Erica Sadun <erica@ericasadun.com> wrote:

I could be satisfied by such an approach. I could even be more satisfied if

enum Foo: ValueEnumerable { case A, B, C }

were also essentially an alias for

enum Foo: Int, ValueEnumerable { case A=0, B, C }

:slight_smile:

If the value collection were sufficiently capable, you wouldn't necessarily need an implicit Int raw value; you could do `allValues.indexOf(.A)` to get the index for a case.

-Joe

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


(Rob Mayoff) #19

That makes me want to say `for case in SomeEnum.cases { ... }`, which is a
syntax error, as opposed to `for value in SomeEnum.values { ... }`, which
is legal.

···

On Tue, Dec 22, 2015 at 12:33 AM, Zef Houssney via swift-evolution < swift-evolution@swift.org> wrote:

I agree with Stephen Celis that the best names for this (yet) are
definitely `cases` and optionally `rawValues` if the cases are
RawRepresentable.


(Pierre Monod-Broca) #20

+1 for an ordered collection of all cases, it could be an ordered set to please proponents of a set.

I have no strong opinions about the name
`cases` is good, but I agree it begs to do ''for case in SomeEnum.cases''
`values` is good, and IMO it's consistent with `rawValue` when there is one, not confusing

···

--
Pierre