Pre-proposal: CaseEnumerable protocol (derived collection of enum cases)

I think it would make sense for the syntax to be explicit that something is
being derived by the compiler. Haskell uses "deriving"
<Chapter 6. Using Typeclasses;
for this. I'm not sure what it should look like in Swift (suggestions
welcome), and I'm also not sure if it's worth figuring that out for the
first version of this feature, or whether we'd be better off making that
change later.

Jacob

···

On Tue, Jan 19, 2016 at 11:37 AM, David Waite <david@alkaline-solutions.com> wrote:

ErrorType’s magic is only for Objective-C interoperability, and only
exposed by diving into compiler implementation-level protocols.

This seems like a case where some syntax consistency is needed between
‘code generation’ proposals (such as member wise initializers). If there
was guidance on what an eventual macro system syntax could look like, this
could be taken into account as well.

-DW

On Jan 19, 2016, at 11:36 AM, Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> wrote:

Yes: ErrorType.

On Tue, Jan 19, 2016 at 10:21 AM, Sune Foldager <cyano@me.com> wrote:

Hm, I don’t think I like that. Are there other examples of magic
protocols in Swift?

The compiler implicitly derives T: Equatable for enums without associated values, and IMO it'd be reasonable to extend this to other types and/or other protocols.

-Joe

···

On Jan 19, 2016, at 11:37 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

ErrorType’s magic is only for Objective-C interoperability, and only exposed by diving into compiler implementation-level protocols.

This seems like a case where some syntax consistency is needed between ‘code generation’ proposals (such as member wise initializers). If there was guidance on what an eventual macro system syntax could look like, this could be taken into account as well.

-DW

On Jan 19, 2016, at 11:36 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

Yes: ErrorType.
On Tue, Jan 19, 2016 at 10:21 AM, Sune Foldager <cyano@me.com <mailto:cyano@me.com>> wrote:

Hm, I don’t think I like that. Are there other examples of magic protocols in Swift?

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

Would you agree that it makes sense to:

1. Automatically derive CaseEnumerable (or ValueEnumerable or whatever we
choose) for Swift enums without associated values;
2. Still allow "extension Foo: CaseEnumerable {}" to add it to enums
imported from (Obj-)C headers?

Or is it OK as-is, or is it worth pursuing some kind of "deriving" syntax?

Jacob Bandes-Storch

···

On Tue, Jan 19, 2016 at 12:17 PM, Joe Groff <jgroff@apple.com> wrote:

The compiler implicitly derives T: Equatable for enums without associated
values, and IMO it'd be reasonable to extend this to other types and/or
other protocols.

-Joe

On Jan 19, 2016, at 11:37 AM, David Waite via swift-evolution < > swift-evolution@swift.org> wrote:

ErrorType’s magic is only for Objective-C interoperability, and only
exposed by diving into compiler implementation-level protocols.

This seems like a case where some syntax consistency is needed between
‘code generation’ proposals (such as member wise initializers). If there
was guidance on what an eventual macro system syntax could look like, this
could be taken into account as well.

-DW

On Jan 19, 2016, at 11:36 AM, Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> wrote:

Yes: ErrorType.

On Tue, Jan 19, 2016 at 10:21 AM, Sune Foldager <cyano@me.com> wrote:

Hm, I don’t think I like that. Are there other examples of magic
protocols in Swift?

_______________________________________________

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

ErrorType’s magic is only for Objective-C interoperability, and only exposed by diving into compiler implementation-level protocols.

This seems like a case where some syntax consistency is needed between ‘code generation’ proposals (such as member wise initializers). If there was guidance on what an eventual macro system syntax could look like, this could be taken into account as well.

-DW

···

On Jan 19, 2016, at 11:36 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

Yes: ErrorType.
On Tue, Jan 19, 2016 at 10:21 AM, Sune Foldager <cyano@me.com <mailto:cyano@me.com>> wrote:

Hm, I don’t think I like that. Are there other examples of magic protocols in Swift?

Would you agree that it makes sense to:

1. Automatically derive CaseEnumerable (or ValueEnumerable or whatever we choose) for Swift enums without associated values;

I think you'd want to opt in, by declaring 'enum Foo: CaseEnumerable {}', since providing this conformance makes some implicit promises not every type may want to publicly commit to.

2. Still allow "extension Foo: CaseEnumerable {}" to add it to enums imported from (Obj-)C headers?

Or is it OK as-is, or is it worth pursuing some kind of "deriving" syntax?

This is just my opinion, but I think declaring a conformance ought to be enough. You could imagine the compiler-synthesized implementation being provided by a protocol extension with sufficiently advanced language features from the future to implement the default behavior.

-Joe

···

On Jan 19, 2016, at 12:20 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Would you agree that it makes sense to:

1. Automatically derive CaseEnumerable (or ValueEnumerable or whatever we choose) for Swift enums without associated values;

I think you'd want to opt in, by declaring 'enum Foo: CaseEnumerable {}', since providing this conformance makes some implicit promises not every type may want to publicly commit to.

+1

2. Still allow "extension Foo: CaseEnumerable {}" to add it to enums imported from (Obj-)C headers?

Or is it OK as-is, or is it worth pursuing some kind of "deriving" syntax?

This is just my opinion, but I think declaring a conformance ought to be enough. You could imagine the compiler-synthesized implementation being provided by a protocol extension with sufficiently advanced language features from the future to implement the default behavior.

Also +1

:)

-- E, who believes in the sufficiently advanced language features from the future

···

On Jan 19, 2016, at 1:54 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 19, 2016, at 12:20 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

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

As long as they aren’t trying to kill my parents or myself for a bug I wrote in the source code for Skynet, I’m fine.

More seriously, I think of this sort of thing as being a default implementation of a protocol via an extension, just using internal language implementation features.

-DW

···

On Jan 19, 2016, at 6:05 PM, Erica Sadun via swift-evolution <swift-evolution@swift.org> wrote:

On Jan 19, 2016, at 1:54 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

This is just my opinion, but I think declaring a conformance ought to be enough. You could imagine the compiler-synthesized implementation being provided by a protocol extension with sufficiently advanced language features from the future to implement the default behavior.

Also +1

:)

-- E, who believes in the sufficiently advanced language features from the future

Big +1 from me for the proposal including the variations.

-Thorsten

···

Am 18.01.2016 um 02:06 schrieb Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org>:

Here's a better link that tracks my branch:
https://github.com/jtbandes/swift-evolution/blob/case-enumerable/proposals/0000-derived-collection-of-enum-cases.md

On Sun, Jan 17, 2016 at 3:44 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:
Hi folks,

I've drafted a proposal to add a CaseEnumerable protocol, which will derive a static variable "cases" for enum types. Feedback is welcome, especially for refining the proposal before I submit a formal PR.

The draft is here; full text below. https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md

Derived Collection of Enum Cases

Proposal: SE-NNNN
Author(s): Jacob Bandes-Storch
Status: Awaiting review
Review manager: TBD
Introduction

It is a truth universally acknowledged, that a programmer in possession of an enum with many cases, must eventually be in want of dynamic enumeration over them.

This topic has come up three times on the swift-evolution mailing list so far:

List of all Enum values (for simple enums) (December 8, 2015)
Proposal: Enum 'count' functionality (December 21, 2015)
Draft Proposal: count property for enum types (January 17, 2016)
Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

How to enumerate an enum with String type? (June 3, 2014; question score 131)
How do I get the count of a Swift enum? (November 23, 2014; question score 37)
Motivation

Simple enums are finite, and their values are statically known to the compiler, yet working with them programmatically is challenging. It is often desirable to iterate over all possible cases of an enum, or to know the number of cases (or maximum valid rawValue).

Currently, however, there is no built-in reflection or enumeration support. Users must resort to manually listing out cases in order to iterate over them:

enum Attribute {
    case Date, Name, Author
}
func valueForAttribute(attr: Attribute) -> String { …from elsewhere… }

// Cases must be listed explicitly:
[Attribute.Date, .Name, .Author].map{ valueForAttribute($0) }.joinWithSeparator("\n")
For RawRepresentable enums, users have often relied on iterating over the known (or assumed) allowable raw values:

Annotated excerpt from Nate Cook's post, Loopy, Random Ideas for Extending "enum" (October 2014):

enum Reindeer: Int {
    case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner, Blitzen, Rudolph
}
extension Reindeer {
    static var allCases: [Reindeer] {
        var cur = 0
        return Array(
            GeneratorOf<Reindeer> {
                return Reindeer(rawValue: cur++)
            }
        )
    }
    static var caseCount: Int {
        var max: Int = 0
        while let _ = self(rawValue: ++max) {}
        return max
    }
    static func randomCase() -> Reindeer {
        // everybody do the Int/UInt32 shuffle!
        let randomValue = Int(arc4random_uniform(UInt32(caseCount)))
        return self(rawValue: randomValue)!
    }
}
There are many problems with these existing techniques:

They are ad-hoc and can't benefit every enum type without duplicated and code.
They are not standardized across codebases, nor provided automatically by libraries such as Foundation and {App,UI}Kit.
They are sometimes prone to bugs when enum cases are added, but the user forgets to update a hard-coded static collection of cases.
Precedent in other languages

Rust does not seem to have a solution for this problem.

C#'s Enum has several methods available for reflection, including GetValues() and GetNames().

Java implicitly declares a static values() function, returning an array of enum values, and valueOf(String name) which takes a String and returns the enum value with the corresponding name (or throws an exception). More examples here.

The Template Haskell extension to Haskell provides a function reify which extracts info about types, including their constructors.

Proposed solution

Introduce a CaseEnumerable protocol. Conforming to CaseEnumerable will automagically derive a static var cases, whose type is a CollectionType of all the enum's values.

Like ErrorType, the CaseEnumerable protocol will not have any user-visible requirements; merely adding the conformance is enough to enable case enumeration.

enum Ma { case 马, 吗, 妈, 码, 骂, :racehorse:, :horse: }

extension Ma: CaseEnumerable {}

Ma.cases // returns some CollectionType whose Generator.Element is Ma
Ma.cases.count // returns 7
Array(Ma.cases) // returns [Ma.马, .吗, .妈, .码, .骂, .:racehorse:, .:horse:]
Conformances can even be added for enums which are defined in other modules:

extension NSTextAlignment: CaseEnumerable {}

Array(NSTextAlignment.cases) // returns [NSTextAlignment.Left, .Right, .Center, .Justified, .Natural]
Detailed design

Enum cases are enumerated in the order they appear in the source code.

The cases collection does not necessitate Ω(number of cases) static storage. For integer-backed enums, only the range(s) of valid rawValues need to be stored, and the enum construction can happen dynamically.

Attempting to derive CaseEnumerable for a non-enum type will result in a compiler error.

Attempting to derive CaseEnumerable for an enum with associated values will result in a compiler error.

Possible variations

I'd like us to discuss these, but they should be folded into either Proposed solution or Future directions before the proposal is submitted for review.

For enums with raw values, a static rawValues property (a collection of RawValue rather than the enum type itself) could also be synthesized.

CaseEnumerable could have a user-visible declaration requiring static var cases, which would allow users to add conformances for custom non-enum types.

In this case, adding a conformance for a non-enum type would not be a compiler error, it would just require an explicit implementation of static var cases, since the compiler wouldn't synthesize it.
This would probably require cases to be AnySequence<Self>, or to introduce an AnyCollection, since we aren't able to say associatedtype CaseCollection: CollectionType where CaseCollection.Generator.Element == Self.
It would be nice to have a way of supporting this for OptionSetType structs. I would recommend that cases for an OptionSetType should include only the already-declared static properties (not all possible combinations of them). However, I'm not sure it fits into this proposal.

Impact on existing code

This proposal only adds functionality, so existing code will not be affected. (The identifier CaseEnumerable doesn't make any significant appearances in Google and GitHub searches.)

Alternatives considered

The community has not raised any solutions that differ significantly from this proposal, except for solutions which provide strictly more functionality. These are covered in the next section, Future directions.

An alternative is to not implement this feature. The cons of this are discussed in the Motivation section above.

The functionality could also be provided entirely through the Mirror/reflection APIs, but this would result in much more obscure and confusing usage patterns.

Future directions

Many people would be happy to see even more functionality than what's proposed here. I'm keeping this proposal intentionally limited, but I hope the community can continue discussing the topic to flesh out more features.

Here are some starting points, which are not part of this proposal:

Support for enum case names. It would be useful to get case names even for enums which have integer rawValues. This could be part of the existing reflection APIs, or it could take the form of derived implementations of StringLiteralConvertible/CustomStringConvertible.

Support for enums with associated values.

When all associated values are themselves CaseEnumerable, this could happen automatically:

enum Suit: CaseEnumerable { case Spades, Hearts, Diamonds, Clubs }
enum Rank: Int, CaseEnumerable {
    case Ace = 1, Two, Three, Four, Five, Six
    case Seven, Eight, Nine, Ten, Jack, Queen, King
}
enum Card {
    case Joker
    case Value(Rank, Suit)
}

// This now works, and generates all possible card types (Joker, Value(Ace, Spades), ...)
extension Card: CaseEnumerable {}
If associated values aren't CaseEnumerable, but all cases are homogeneous, the cases collection could vend functions of AssociatedValueType -> EnumType:

enum LogMessage { case Error(String), Warning(String), Info(String) }
extension LogMessage: CaseEnumerable {}

LogMessage.cases // elements are (String) -> LogMessage
If Swift had anonymous sum types like A | B | C, then E.cases could vend elements of type A->E | B->E | C->E.

enum Expr { case Apply(Expr, Expr), Tuple(Expr, Expr), Literal(Int) }
extension Value: CaseEnumerable {}

// This example is pretty contrived, but illustrates the functionality.
let fortyTwos = Expr.cases.map {
   // $0 is of type `Int -> Expr | (Expr, Expr) -> Expr`
   switch $0 {
   case let lit as Int -> Expr: // handles .Literal
       return lit(42)
   case let bin as (Expr, Expr) -> Expr: // handles .Apply and .Tuple
       return bin(.Literal(42), .Literal(42))
   // all cases are covered
   }
}
Support for generic enums.

CaseEnumerable could be conditionally supported depending on the generic argument(s). A great example would be Optional:

enum MyEnum: CaseEnumerable {}
extension Optional: CaseEnumerable where Wrapped: CaseEnumerable {}

// Optional<MyEnum>.cases effectively contains `MyEnum.cases + [.None]`

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

Regarding support for enums with associated values, I’d like to argue for the idea of returning the constructors along side the instances of simple enum cases.

I find that it’s common to have enums that have mostly simple cases, but a couple with associated values that provide more dynamic customization. I would hate to be unable to include the cases with associated values in order to get conformance for this feature. Here’s an example of something like that. The concept is that you have an enum that defines a number of built-in color schemes, but provides a mechanism for users to dynamically create their own color schemes:

I have other real-world ideas for where this would be useful too. This is a contrived example that is simplified such that it would be just as easy to implement the result without the constructor being included in the list of cases (or allValues), but there are use-cases for where there are multiple cases with the same constructor signature where it would be valuable for them to be included.

Joe Groff said:

Asking for the collection of case constructors seems like a different thing to me—you're getting a [(Payload) -> Self] rather than a [Self] collection.

I agree that this is kind of a problem, but I find this a better tradeoff than the alternatives, which as I see them are:

1. Don’t support enums with disparate constructor signatures at all (currently proposed — compiler error for these)
2. Allow conformance for all enums, but omit all cases that have associated values (more useful than the proposed solution, but confusing and not ideal)
3. A combination of 1 and 2, where some cases with associated values can potentially have multiple cases included, or none at all (confusing and potentially annoying to work around if it’s not what you want)

Omitting the values and getting back a collection of [Self] would be easy enough anyway: MyEnum.cases.flatMap { $0 as? MyEnum }

Here’s what I like about this approach:

- This would allow all enums be able to conform, not just ones where every case has the same type.
- The array consists of a 1-to-1 mapping regardless of the type of case. This could be very valuable and powerful compared to some of the other ideas that could omit or have multiple entries for a single case.
- As a base implementation, all the other ideas for how to deal with cases with associated values can be implemented on top of this foundation by developers, whereas the other approaches are somewhat more limiting and would not cover the use case I have above.

Downsides I can see to this approach:

- For enums that have disparate constructor signatures, [Any] would have to be the return value of cases, which doesn’t seem ideal because the cases array would always require some kind of manual manipulation to be useful. Would it even be possible for cases to return the [EnumType] for simple enums but [Any] for more complex ones? Is there a better type than [Any] that could be used?

- There isn’t currently a way in the type system to know what case you are working based on the constructor. For instance say two cases both have a constructor of (String), you can’t determine which case you are working on until you call the constructor to instantiate the case. This could be done by looking at the index of the constructor in the cases array, but that is brittle and messy. If there was a way to determine that more dynamically, this would be more powerful and easier to use.

FYI, some of this content is taken from a post where I wrote about this, but it was just before Christmas and didn’t really get any feedback: [swift-evolution] Proposal: Enum 'count' functionality

···

On Jan 20, 2016, at 10:40 PM, Slava Pestov via swift-evolution <swift-evolution@swift.org> wrote:

Support for enum case names. It would be useful to get case names even for enums which have integer rawValues. This could be part of the existing reflection APIs, or it could take the form of derived implementations of StringLiteralConvertible/CustomStringConvertible.

I’d love to see reflection move to an opt-in model, with a compiler flag enabling reflection for all types in a module, and a protocol to opt-in conditionally otherwise.

Support for enums with associated values.

This seems tricky to use statically in the general case, as you see below. Do you have any good examples in mind? It seems that case enumeration only really makes sense for types with a finite number of elements, and not when you have associated values.

Perhaps it should only work if the associated values are themselves enums, and they can be enumerated recursively? But even that seems overkill, and something that people should implement themselves if they need it.

If Swift had anonymous sum types like A | B | C, then E.cases could vend elements of type A->E | B->E | C->E.

As part of write reflection, it would make sense to expose constructor functions [Any -> E?] that perform a dynamic type check, failing with nil.

Imagine if you could reflect an enum of type T, and get an array of [EnumCase<T>]:

enum EnumCase<T> {
  // constructor makes a value: Payload -> T
  // projection tests a value, returning payload if its that case, or nil: T -> Payload?
  // type is the runtime type of the payload
  case PayloadCase(name: String, constructor: Any -> T, projection: T -> Any?, type: Any.Type)
  case EmptyCase(name: String, value: T)
}

Then you can get representative values of all cases by mapping over this array, returning the ‘value’ element of an EmptyCase.

enum Expr { case Apply(Expr, Expr), Tuple(Expr, Expr), Literal(Int) }
extension Value: CaseEnumerable {}

// This example is pretty contrived, but illustrates the functionality.
let fortyTwos = Expr.cases.map {
   // $0 is of type `Int -> Expr | (Expr, Expr) -> Expr`
   switch $0 {
   case let lit as Int -> Expr: // handles .Literal
       return lit(42)
   case let bin as (Expr, Expr) -> Expr: // handles .Apply and .Tuple
       return bin(.Literal(42), .Literal(42))
   // all cases are covered
   }
}

I think in this example program, it would make more sense to define these data types:

enum BinaryExprKind { cae Apply, Tuple }
enum Expr { case Binary(Kind, Expr, Expr), Literal(Int) }

You can still enumerate the cases of BinaryExprKind dynamically, but destructuring Expr requires a switch.

Support for generic enums.

CaseEnumerable could be conditionally supported depending on the generic argument(s). A great example would be Optional:

enum MyEnum: CaseEnumerable {}
extension Optional: CaseEnumerable where Wrapped: CaseEnumerable {}

// Optional<MyEnum>.cases effectively contains `MyEnum.cases + [.None]`
_______________________________________________
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 <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

Attributes would be an option as well — but even if they are imho the natural choice, I think I would prefer a "protocol with synthesized default behavior".

Introduce a CaseEnumerable protocol. Conforming to CaseEnumerable will automagically derive a static var cases, whose type is a CollectionType of all the enum's values.

Hm, I don’t think I like that. Are there other examples of magic protocols in Swift?

Same opinion — if a protocol is the way to go, it shouldn't receive more special treatment than necessary:
Every type should be allowed to conform to the protocol, and enums could work like Joe suggests

This is just my opinion, but I think declaring a conformance ought to be enough. You could imagine the compiler-synthesized implementation being provided by a protocol extension with sufficiently advanced language features from the future to implement the default behavior.

There might be even situations where it could make sense to have "cases" that are generated at runtime (at least a can't see a reason to forbid it).

Tino

Hi Jose,
Enums with payloads were discussed, but the proposal considers them out of
scope — I was trying to keep it minimal so we could get a first version
into the language. (Still waiting for it to be merged though...!
Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub )

Jacob

···

On Tue, Mar 8, 2016 at 6:34 PM, Jose Cheyo Jimenez <cheyo@masters3d.com> wrote:

Hi Jacob,

This may be totally unrelated but how would proposed protocol handle enums
with payloads?
I field a SR requesting a way to get the declared order cases of enums
with payloads.
[SR-887] Expose the declaration order of Enum cases with payloads · Issue #43499 · apple/swift · GitHub

Do you think your proposal could be expanded to include Enums with
payloads?

Thanks!

> Hi folks,
>
> I've drafted a proposal to add a CaseEnumerable protocol, which will
derive
> a static variable "cases" for enum types. Feedback is welcome, especially
> for refining the proposal before I submit a formal PR.
>
> The draft is here; full text below.
>
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md
>
>
> Derived Collection of Enum Cases
>
> - Proposal: SE-NNNN
> <
https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-derived-collection-of-enum-cases.md
>
> - Author(s): Jacob Bandes-Storch<https://github.com/jtbandes&gt;
> - Status: *Awaiting review*
> - Review manager: TBD
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#introduction
>
> Introduction
>
> It is a truth universally acknowledged, that a programmer in possession
of
> an enum with many cases, must eventually be in want of dynamic
enumeration
> over them.
>
> This topic has come up three times on the swift-evolution mailing list so
> far:
>
> - List of all Enum values (for simple enums)
> <
[swift-evolution] List of all Enum values (for simple enums)
>
> (December
> 8, 2015)
> - Proposal: Enum 'count' functionality
> <
[swift-evolution] Proposal: Enum 'count' functionality
>
> (December
> 21, 2015)
> - Draft Proposal: count property for enum types
> <
[swift-evolution] Draft Proposal: count property for enum types
>
> (January
> 17, 2016)
>
> Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:
>
> - How to enumerate an enum with String type?
> <
swift - How to enumerate an enum with String type? - Stack Overflow
>
> (June
> 3, 2014; question score 131)
> - How do I get the count of a Swift enum?
> <
How do I get the count of a Swift enum? - Stack Overflow
>
> (November
> 23, 2014; question score 37)
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#motivation
>
> Motivation
>
> Simple enums are finite, and their values are statically known to the
> compiler, yet working with them programmatically is challenging. It is
> often desirable to iterate over all possible cases of an enum, or to know
> the number of cases (or maximum valid rawValue).
>
> Currently, however, there is no built-in reflection or enumeration
support.
> Users must resort to manually listing out cases in order to iterate over
> them:
>
> enum Attribute {
> case Date, Name, Author
> }func valueForAttribute(attr: Attribute) ->String { …from elsewhere… }
> // Cases must be listed explicitly:
> [Attribute.Date, .Name, .Author].map{ valueForAttribute($0)
> }.joinWithSeparator("\n")
>
> For RawRepresentable enums, users have often relied on iterating over the
> known (or assumed) allowable raw values:
>
> *Annotated excerpt from Nate Cook's post, Loopy, Random Ideas for
Extending
> "enum"<Loopy, Random Ideas for Extending "enum" — Nate Cook
>(October
> 2014):*
>
> enum Reindeer: Int {
> case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner,
> Blitzen, Rudolph
> }extension Reindeer {
> static var allCases: [Reindeer] {
> var cur = 0
> return Array(
> GeneratorOf<Reindeer>{
> return Reindeer(rawValue: cur++)
> }
> )
> }
> static var caseCount: Int {
> var max: Int = 0
> while let _ = self(rawValue: ++max) {}
> return max
> }
> static func randomCase() ->Reindeer {
> // everybody do the Int/UInt32 shuffle!
> let randomValue = Int(arc4random_uniform(UInt32(caseCount)))
> return self(rawValue: randomValue)!
> }
> }
>
> There are many problems with these existing techniques:
>
> - They are ad-hoc and can't benefit every enum type without duplicated
> and code.
> - They are not standardized across codebases, nor provided automatically
> by libraries such as Foundation and {App,UI}Kit.
> - They are sometimes prone to bugs when enum cases are added, but the
> user forgets to update a hard-coded static collection of cases.
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#precedent-in-other-languages
>Precedent
> in other languages
>
> -
>
> Rust does not seem to have a solution for this problem.
> -
>
> C#'s Enum has several methods
> <Microsoft Learn: Build skills that open doors in your career;
> available
> for reflection, including GetValues() and GetNames().
> -
>
> Java implicitly declares
> <Chapter 8. Classes
> static values() function, returning an array of enum values, and
> valueOf(String
> name) which takes a String and returns the enum value with the
> corresponding name (or throws an exception). More examples here
> <Chapter 8. Classes.
> -
>
> The Template Haskell extension to Haskell provides a function reify which
> extracts info about types
> <
Language.Haskell.TH.Syntax
>,
> including their constructors.
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#proposed-solution
>Proposed
> solution
>
> Introduce a CaseEnumerable protocol. Conforming to CaseEnumerable will
> automagically derive a static var cases, whose type is a CollectionType
of
> all the enum's values.
>
> Like ErrorType, the CaseEnumerable protocol will not have any
user-visible
> requirements; merely adding the conformance is enough to enable case
> enumeration.
>
> enum Ma { case 马, 吗, 妈, 码, 骂, :racehorse:, :horse: }
> extension Ma: CaseEnumerable {}
>
> Ma.cases // returns some CollectionType whose Generator.Element is Ma
> Ma.cases.count // returns 7Array(Ma.cases) // returns [Ma.马, .吗,
> .妈, .码, .骂, .:racehorse:, .:horse:]
>
> Conformances can even be added for enums which are defined in other
modules:
>
> extension NSTextAlignment: CaseEnumerable {}
> Array(NSTextAlignment.cases) // returns [NSTextAlignment.Left,
> .Right, .Center, .Justified, .Natural]
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#detailed-design
>Detailed
> design
>
> Enum cases are enumerated in the order they appear in the source code.
>
> The cases collection does not necessitate Ω(number of cases) static
> storage. For integer-backed enums, only the range(s) of valid rawValues
> need to be stored, and the enum construction can happen dynamically.
>
> Attempting to derive CaseEnumerable for a non-enum type will result in a
> compiler error.
>
> Attempting to derive CaseEnumerable for an enum with associated values
will
> result in a compiler error.
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#possible-variations
>Possible
> variations
>
> I'd like us to discuss these, but they should be folded into either
*Proposed
> solution* or *Future directions* before the proposal is submitted for
> review.
>
> -
>
> For enums with raw values, a static rawValues property (a collection of
> RawValue rather than the enum type itself) could also be synthesized.
> -
>
> CaseEnumerable could have a user-visible declaration requiring static
> var cases, which would allow users to add conformances for custom non-
> enum types.
> - In this case, adding a conformance for a non-enum type would not be a
> compiler error, it would just require an explicit implementation
> of static
> var cases, since the compiler wouldn't synthesize it.
> - This would probably require cases to be AnySequence<Self>, or to
> introduce an AnyCollection, since we aren't able to say associatedtype
> CaseCollection: CollectionType where CaseCollection.Generator.Element ==
> Self.
> -
>
> It would be nice to have a way of supporting this for OptionSetType
> structs. I would recommend that cases for an OptionSetType should
> include only the already-declared static properties (not all possible
> combinations of them). However, I'm not sure it fits into this proposal.
>
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#impact-on-existing-code
>Impact
> on existing code
>
> This proposal only adds functionality, so existing code will not be
> affected. (The identifier CaseEnumerable doesn't make any significant
> appearances in Google and GitHub searches.)
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#alternatives-considered
>Alternatives
> considered
>
> The community has not raised any solutions that differ significantly from
> this proposal, except for solutions which provide strictly *more*
> functionality.
> These are covered in the next section, *Future directions*.
>
> An alternative is to *not* implement this feature. The cons of this are
> discussed in the *Motivation* section above.
>
> The functionality could also be provided entirely through the
> Mirror/reflection APIs, but this would result in much more obscure and
> confusing usage patterns.
> <
https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#future-directions
>Future
> directions
>
> Many people would be happy to see even more functionality than what's
> proposed here. I'm keeping this proposal intentionally limited, but I
hope
> the community can continue discussing the topic to flesh out more
features.
>
> Here are some starting points, which are *not* part of this proposal:
>
> -
>
> Support for enum case *names*. It would be useful to get case names even
> for enums which have integer rawValues. This could be part of the
existing
> reflection APIs, or it could take the form of derived implementations of
> StringLiteralConvertible/CustomStringConvertible.
> -
>
> Support for enums with associated values.
> -
>
> When all associated values are themselves CaseEnumerable, this could
> happen automatically:
>
> enum Suit: CaseEnumerable { case Spades, Hearts, Diamonds, Clubs
> }enum Rank: Int, CaseEnumerable {
> case Ace = 1, Two, Three, Four, Five, Six
> case Seven, Eight, Nine, Ten, Jack, Queen, King
> }enum Card {
> case Joker
> case Value(Rank, Suit)
> }
> // This now works, and generates all possible card types (Joker,
> Value(Ace, Spades), ...)extension Card: CaseEnumerable {}
>
> -
>
> If associated values aren't CaseEnumerable, but all cases are
> homogeneous, the cases collection could vend functions of
> AssociatedValueType
> ->EnumType:
>
> enum LogMessage { case Error(String), Warning(String),
> Info(String) }extension LogMessage: CaseEnumerable {}
>
> LogMessage.cases // elements are (String) ->LogMessage
>
> -
>
> If Swift had anonymous sum types like A | B | C, then E.cases could
> vend elements of type A->E | B->E | C->E.
>
> enum Expr { case Apply(Expr, Expr), Tuple(Expr, Expr),
> Literal(Int) }extension Value: CaseEnumerable {}
> // This example is pretty contrived, but illustrates the
> functionality.let fortyTwos = Expr.cases.map {
> // $0 is of type `Int ->Expr | (Expr, Expr) ->Expr`
> switch $0 {
> case let lit as Int ->Expr: // handles .Literal
> return lit(42)
> case let bin as (Expr, Expr) ->Expr: // handles .Apply and .Tuple
> return bin(.Literal(42), .Literal(42))
> // all cases are covered
> }
> }
>
> -
>
> Support for generic enums.
> -
>
> CaseEnumerable could be conditionally supported depending on the
> generic argument(s). A great example would be Optional:
>
> enum MyEnum: CaseEnumerable {}extension Optional: CaseEnumerable
> where Wrapped: CaseEnumerable {}
> // Optional<MyEnum>.cases effectively contains `MyEnum.cases + [.None]`
>
>
>

proposal +1

I was originally thinking T.allCases. When reading trough.

valueEnumerable could work trying to think something shorter and easier to read.

This is short and easy to understand and could be used in other contexts:
T.allValues

···

On Jan 18, 2016, at 11:15 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

OK, I can see the argument here. I was worried about making this proposal so broad that it wouldn't be seriously considered, but I agree that choosing more general names will open it up to future expansion.

Here are some more name ideas:

- CaseEnumerable (in the proposal draft)
- T.cases (in the proposal draft)
- T.allCases
- FiniteType (as you suggested)
- FiniteValueType
- ValueEnumerable
- T.values
- T.allValues

Thoughts? More suggestions? I think I like ValueEnumerable.

I would like to see in this proposal go into review.

···

On Mar 9, 2016, at 12:45 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

Hi Jose,
Enums with payloads were discussed, but the proposal considers them out of scope — I was trying to keep it minimal so we could get a first version into the language. (Still waiting for it to be merged though...! Deriving collections of enum cases by jtbandes · Pull Request #114 · apple/swift-evolution · GitHub )

Jacob

On Tue, Mar 8, 2016 at 6:34 PM, Jose Cheyo Jimenez <cheyo@masters3d.com> wrote:
Hi Jacob,

This may be totally unrelated but how would proposed protocol handle enums with payloads?
I field a SR requesting a way to get the declared order cases of enums with payloads.
[SR-887] Expose the declaration order of Enum cases with payloads · Issue #43499 · apple/swift · GitHub

Do you think your proposal could be expanded to include Enums with payloads?

Thanks!

> Hi folks,
>
> I've drafted a proposal to add a CaseEnumerable protocol, which will derive
> a static variable "cases" for enum types. Feedback is welcome, especially
> for refining the proposal before I submit a formal PR.
>
> The draft is here; full text below.
> https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md
>
>
> Derived Collection of Enum Cases
>
> - Proposal: SE-NNNN
> <https://github.com/apple/swift-evolution/blob/master/proposals/NNNN-derived-collection-of-enum-cases.md&gt;
> - Author(s): Jacob Bandes-Storch<https://github.com/jtbandes&gt;
> - Status: *Awaiting review*
> - Review manager: TBD
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#introduction&gt;
> Introduction
>
> It is a truth universally acknowledged, that a programmer in possession of
> an enum with many cases, must eventually be in want of dynamic enumeration
> over them.
>
> This topic has come up three times on the swift-evolution mailing list so
> far:
>
> - List of all Enum values (for simple enums)
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html&gt;
> (December
> 8, 2015)
> - Proposal: Enum 'count' functionality
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html&gt;
> (December
> 21, 2015)
> - Draft Proposal: count property for enum types
> <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html&gt;
> (January
> 17, 2016)
>
> Enumerating enumerations in Swift is also a popular topic on Stack Overflow:
>
> - How to enumerate an enum with String type?
> <swift - How to enumerate an enum with String type? - Stack Overflow;
> (June
> 3, 2014; question score 131)
> - How do I get the count of a Swift enum?
> <How do I get the count of a Swift enum? - Stack Overflow;
> (November
> 23, 2014; question score 37)
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#motivation&gt;
> Motivation
>
> Simple enums are finite, and their values are statically known to the
> compiler, yet working with them programmatically is challenging. It is
> often desirable to iterate over all possible cases of an enum, or to know
> the number of cases (or maximum valid rawValue).
>
> Currently, however, there is no built-in reflection or enumeration support.
> Users must resort to manually listing out cases in order to iterate over
> them:
>
> enum Attribute {
> case Date, Name, Author
> }func valueForAttribute(attr: Attribute) ->String { …from elsewhere… }
> // Cases must be listed explicitly:
> [Attribute.Date, .Name, .Author].map{ valueForAttribute($0)
> }.joinWithSeparator("\n")
>
> For RawRepresentable enums, users have often relied on iterating over the
> known (or assumed) allowable raw values:
>
> *Annotated excerpt from Nate Cook's post, Loopy, Random Ideas for Extending
> "enum"<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/&gt;\(October
> 2014):*
>
> enum Reindeer: Int {
> case Dasher, Dancer, Prancer, Vixen, Comet, Cupid, Donner,
> Blitzen, Rudolph
> }extension Reindeer {
> static var allCases: [Reindeer] {
> var cur = 0
> return Array(
> GeneratorOf<Reindeer>{
> return Reindeer(rawValue: cur++)
> }
> )
> }
> static var caseCount: Int {
> var max: Int = 0
> while let _ = self(rawValue: ++max) {}
> return max
> }
> static func randomCase() ->Reindeer {
> // everybody do the Int/UInt32 shuffle!
> let randomValue = Int(arc4random_uniform(UInt32(caseCount)))
> return self(rawValue: randomValue)!
> }
> }
>
> There are many problems with these existing techniques:
>
> - They are ad-hoc and can't benefit every enum type without duplicated
> and code.
> - They are not standardized across codebases, nor provided automatically
> by libraries such as Foundation and {App,UI}Kit.
> - They are sometimes prone to bugs when enum cases are added, but the
> user forgets to update a hard-coded static collection of cases.
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#precedent-in-other-languages&gt;Precedent
> in other languages
>
> -
>
> Rust does not seem to have a solution for this problem.
> -
>
> C#'s Enum has several methods
> <Microsoft Learn: Build skills that open doors in your career;
> available
> for reflection, including GetValues() and GetNames().
> -
>
> Java implicitly declares
> <Chapter 8. Classes
> static values() function, returning an array of enum values, and
> valueOf(String
> name) which takes a String and returns the enum value with the
> corresponding name (or throws an exception). More examples here
> <Chapter 8. Classes.
> -
>
> The Template Haskell extension to Haskell provides a function reify which
> extracts info about types
> <Language.Haskell.TH.Syntax,
> including their constructors.
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#proposed-solution&gt;Proposed
> solution
>
> Introduce a CaseEnumerable protocol. Conforming to CaseEnumerable will
> automagically derive a static var cases, whose type is a CollectionType of
> all the enum's values.
>
> Like ErrorType, the CaseEnumerable protocol will not have any user-visible
> requirements; merely adding the conformance is enough to enable case
> enumeration.
>
> enum Ma { case 马, 吗, 妈, 码, 骂, :racehorse:, :horse: }
> extension Ma: CaseEnumerable {}
>
> Ma.cases // returns some CollectionType whose Generator.Element is Ma
> Ma.cases.count // returns 7Array(Ma.cases) // returns [Ma.马, .吗,
> .妈, .码, .骂, .:racehorse:, .:horse:]
>
> Conformances can even be added for enums which are defined in other modules:
>
> extension NSTextAlignment: CaseEnumerable {}
> Array(NSTextAlignment.cases) // returns [NSTextAlignment.Left,
> .Right, .Center, .Justified, .Natural]
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#detailed-design&gt;Detailed
> design
>
> Enum cases are enumerated in the order they appear in the source code.
>
> The cases collection does not necessitate Ω(number of cases) static
> storage. For integer-backed enums, only the range(s) of valid rawValues
> need to be stored, and the enum construction can happen dynamically.
>
> Attempting to derive CaseEnumerable for a non-enum type will result in a
> compiler error.
>
> Attempting to derive CaseEnumerable for an enum with associated values will
> result in a compiler error.
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#possible-variations&gt;Possible
> variations
>
> I'd like us to discuss these, but they should be folded into either *Proposed
> solution* or *Future directions* before the proposal is submitted for
> review.
>
> -
>
> For enums with raw values, a static rawValues property (a collection of
> RawValue rather than the enum type itself) could also be synthesized.
> -
>
> CaseEnumerable could have a user-visible declaration requiring static
> var cases, which would allow users to add conformances for custom non-
> enum types.
> - In this case, adding a conformance for a non-enum type would not be a
> compiler error, it would just require an explicit implementation
> of static
> var cases, since the compiler wouldn't synthesize it.
> - This would probably require cases to be AnySequence<Self>, or to
> introduce an AnyCollection, since we aren't able to say associatedtype
> CaseCollection: CollectionType where CaseCollection.Generator.Element ==
> Self.
> -
>
> It would be nice to have a way of supporting this for OptionSetType
> structs. I would recommend that cases for an OptionSetType should
> include only the already-declared static properties (not all possible
> combinations of them). However, I'm not sure it fits into this proposal.
>
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#impact-on-existing-code&gt;Impact
> on existing code
>
> This proposal only adds functionality, so existing code will not be
> affected. (The identifier CaseEnumerable doesn't make any significant
> appearances in Google and GitHub searches.)
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#alternatives-considered&gt;Alternatives
> considered
>
> The community has not raised any solutions that differ significantly from
> this proposal, except for solutions which provide strictly *more*
> functionality.
> These are covered in the next section, *Future directions*.
>
> An alternative is to *not* implement this feature. The cons of this are
> discussed in the *Motivation* section above.
>
> The functionality could also be provided entirely through the
> Mirror/reflection APIs, but this would result in much more obscure and
> confusing usage patterns.
> <https://github.com/jtbandes/swift-evolution/blob/977a9923fd551491623b6bfd398d5859488fe1ae/proposals/0000-derived-collection-of-enum-cases.md#future-directions&gt;Future
> directions
>
> Many people would be happy to see even more functionality than what's
> proposed here. I'm keeping this proposal intentionally limited, but I hope
> the community can continue discussing the topic to flesh out more features.
>
> Here are some starting points, which are *not* part of this proposal:
>
> -
>
> Support for enum case *names*. It would be useful to get case names even
> for enums which have integer rawValues. This could be part of the existing
> reflection APIs, or it could take the form of derived implementations of
> StringLiteralConvertible/CustomStringConvertible.
> -
>
> Support for enums with associated values.
> -
>
> When all associated values are themselves CaseEnumerable, this could
> happen automatically:
>
> enum Suit: CaseEnumerable { case Spades, Hearts, Diamonds, Clubs
> }enum Rank: Int, CaseEnumerable {
> case Ace = 1, Two, Three, Four, Five, Six
> case Seven, Eight, Nine, Ten, Jack, Queen, King
> }enum Card {
> case Joker
> case Value(Rank, Suit)
> }
> // This now works, and generates all possible card types (Joker,
> Value(Ace, Spades), ...)extension Card: CaseEnumerable {}
>
> -
>
> If associated values aren't CaseEnumerable, but all cases are
> homogeneous, the cases collection could vend functions of
> AssociatedValueType
> ->EnumType:
>
> enum LogMessage { case Error(String), Warning(String),
> Info(String) }extension LogMessage: CaseEnumerable {}
>
> LogMessage.cases // elements are (String) ->LogMessage
>
> -
>
> If Swift had anonymous sum types like A | B | C, then E.cases could
> vend elements of type A->E | B->E | C->E.
>
> enum Expr { case Apply(Expr, Expr), Tuple(Expr, Expr),
> Literal(Int) }extension Value: CaseEnumerable {}
> // This example is pretty contrived, but illustrates the
> functionality.let fortyTwos = Expr.cases.map {
> // $0 is of type `Int ->Expr | (Expr, Expr) ->Expr`
> switch $0 {
> case let lit as Int ->Expr: // handles .Literal
> return lit(42)
> case let bin as (Expr, Expr) ->Expr: // handles .Apply and .Tuple
> return bin(.Literal(42), .Literal(42))
> // all cases are covered
> }
> }
>
> -
>
> Support for generic enums.
> -
>
> CaseEnumerable could be conditionally supported depending on the
> generic argument(s). A great example would be Optional:
>
> enum MyEnum: CaseEnumerable {}extension Optional: CaseEnumerable
> where Wrapped: CaseEnumerable {}
> // Optional<MyEnum>.cases effectively contains `MyEnum.cases + [.None]`
>
>
>

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

For the protocol name, I rather like FiniteType (or FiniteValueType), since I find it to be more general and future-proof. ValueEnumerable would also work, though.

My preference for the static var would be T.values or T.allValues - we want to get all values of the finite type T.

Definitely a +1 on the overall proposal.

···

On Jan 18, 2016, at 11:15 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

OK, I can see the argument here. I was worried about making this proposal so broad that it wouldn't be seriously considered, but I agree that choosing more general names will open it up to future expansion.

Here are some more name ideas:

- CaseEnumerable (in the proposal draft)
- T.cases (in the proposal draft)
- T.allCases
- FiniteType (as you suggested)
- FiniteValueType
- ValueEnumerable
- T.values
- T.allValues

Thoughts? More suggestions? I think I like ValueEnumerable.

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

I've been thinking about naming a bit. I'm worried that "values" /
"allValues" won't make sense if this feature eventually goes in the
direction of returning type constructors. For enums with associated types,
the cases may actually be *functions* taking the associated type and
returning the enum type. (There's an example under "Future directions" in
the proposal). Calling these "values" seems strange to me, but calling them
"cases" makes sense.

Even though the "case" *keyword* is mostly used with enums, I think the
actual English *word* "case" applies pretty well when thinking about any
type, not just enums. It refers to a member of the type. T.cases or
T.allCases are all the cases of valid instances/members of the type.

"FiniteType" is technically accurate, but to me it doesn't evoke "this
protocol allows you to enumerate the type's values". I'm leaning toward the
names that end in -Enumerable.

Unless someone has a compelling argument otherwise, I may leave the naming
as-is in the proposal, and let the core team bikeshed it to their liking :)

···

On Wed, Jan 20, 2016 at 8:45 AM, Denis Nikitenko <d.nikitenko@icloud.com> wrote:

For the protocol name, I rather like FiniteType (or FiniteValueType),
since I find it to be more general and future-proof. ValueEnumerable would
also work, though.

My preference for the static var would be T.values or T.allValues - we
want to get all values of the finite type T.

Definitely a +1 on the overall proposal.

>> On Jan 18, 2016, at 11:15 PM, Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> wrote:
>>
>>
>> OK, I can see the argument here. I was worried about making this
proposal so broad that it wouldn't be seriously considered, but I agree
that choosing more general names will open it up to future expansion.
>>
>> Here are some more name ideas:
>>
>> - CaseEnumerable (in the proposal draft)
>> - T.cases (in the proposal draft)
>> - T.allCases
>> - FiniteType (as you suggested)
>> - FiniteValueType
>> - ValueEnumerable
>> - T.values
>> - T.allValues
>>
>> Thoughts? More suggestions? I think I like ValueEnumerable.
>>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

I've been thinking about naming a bit. I'm worried that "values" / "allValues" won't make sense if this feature eventually goes in the direction of returning type constructors. For enums with associated types, the cases may actually be functions taking the associated type and returning the enum type. (There's an example under "Future directions" in the proposal). Calling these "values" seems strange to me, but calling them "cases" makes sense.

That seems like a fairly specialized use case to me. I'd expect the default behavior for enums with associated values to enumerate all possible values of each case (whose associated values would themselves need to be ValueEnumerable), not the case constructors themselves. That's more generally useful when using associated values to categorize constants.

-Joe

···

On Jan 20, 2016, at 9:01 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:

Even though the "case" keyword is mostly used with enums, I think the actual English word "case" applies pretty well when thinking about any type, not just enums. It refers to a member of the type. T.cases or T.allCases are all the cases of valid instances/members of the type.

"FiniteType" is technically accurate, but to me it doesn't evoke "this protocol allows you to enumerate the type's values". I'm leaning toward the names that end in -Enumerable.

Unless someone has a compelling argument otherwise, I may leave the naming as-is in the proposal, and let the core team bikeshed it to their liking :)

On Wed, Jan 20, 2016 at 8:45 AM, Denis Nikitenko <d.nikitenko@icloud.com <mailto:d.nikitenko@icloud.com>> wrote:
For the protocol name, I rather like FiniteType (or FiniteValueType), since I find it to be more general and future-proof. ValueEnumerable would also work, though.

My preference for the static var would be T.values or T.allValues - we want to get all values of the finite type T.

Definitely a +1 on the overall proposal.

>> On Jan 18, 2016, at 11:15 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:
>>
>>
>> OK, I can see the argument here. I was worried about making this proposal so broad that it wouldn't be seriously considered, but I agree that choosing more general names will open it up to future expansion.
>>
>> Here are some more name ideas:
>>
>> - CaseEnumerable (in the proposal draft)
>> - T.cases (in the proposal draft)
>> - T.allCases
>> - FiniteType (as you suggested)
>> - FiniteValueType
>> - ValueEnumerable
>> - T.values
>> - T.allValues
>>
>> Thoughts? More suggestions? I think I like ValueEnumerable.
>>
>
> _______________________________________________
> 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

I've been thinking about naming a bit. I'm worried that "values" / "allValues" won't make sense if this feature eventually goes in the direction of returning type constructors. For enums with associated types, the cases may actually be functions taking the associated type and returning the enum type. (There's an example under "Future directions" in the proposal). Calling these "values" seems strange to me, but calling them "cases" makes sense.

As I was writing my previous post, I was also thinking of more abstract names that would evoke the notion of enumerating elements in a set, e.g. T.enumeration or T.enumerate().

However, I think I prefer this idea of outsourcing the bikeshedding. ;)

···

On Jan 20, 2016, at 12:01 PM, Jacob Bandes-Storch <jtbandes@gmail.com> wrote:

Even though the "case" keyword is mostly used with enums, I think the actual English word "case" applies pretty well when thinking about any type, not just enums. It refers to a member of the type. T.cases or T.allCases are all the cases of valid instances/members of the type.

"FiniteType" is technically accurate, but to me it doesn't evoke "this protocol allows you to enumerate the type's values". I'm leaning toward the names that end in -Enumerable.

Unless someone has a compelling argument otherwise, I may leave the naming as-is in the proposal, and let the core team bikeshed it to their liking :)

On Wed, Jan 20, 2016 at 8:45 AM, Denis Nikitenko <d.nikitenko@icloud.com> wrote:
For the protocol name, I rather like FiniteType (or FiniteValueType), since I find it to be more general and future-proof. ValueEnumerable would also work, though.

My preference for the static var would be T.values or T.allValues - we want to get all values of the finite type T.

Definitely a +1 on the overall proposal.

>> On Jan 18, 2016, at 11:15 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> wrote:
>>
>>
>> OK, I can see the argument here. I was worried about making this proposal so broad that it wouldn't be seriously considered, but I agree that choosing more general names will open it up to future expansion.
>>
>> Here are some more name ideas:
>>
>> - CaseEnumerable (in the proposal draft)
>> - T.cases (in the proposal draft)
>> - T.allCases
>> - FiniteType (as you suggested)
>> - FiniteValueType
>> - ValueEnumerable
>> - T.values
>> - T.allValues
>>
>> Thoughts? More suggestions? I think I like ValueEnumerable.
>>
>
> _______________________________________________
> swift-evolution mailing list
> swift-evolution@swift.org
> https://lists.swift.org/mailman/listinfo/swift-evolution

If the associated values are all enums themselves, that's great. What if your enum is an error type, and the associated values are all strings with maybe diagnostic info or time stamps or something?

If Swift’s stdlib had a protocol (either `Initable` or `HasDefaultValue`, as used below), and the language had better support for generic tuple processing:
Tuples automatically conform to any protocol to which all its members conform.
Mapping over a tuple’s member’s types: let x = (Int, String, Double).map { $0.init() } // returns (Int.init(), String.init(), Double.init())
Mapping over a tuple’s members: let x = (0, “”, 0.0).map { $0.hashValue } // returns (0.hashValue, “”.hashValue, 0.0.hashValue)
Subscripting tuple’s: (0, “”, 0.0)[0]
The ability to append/appendElements to tuples, such that if T = (Int, Int), U = (Double, Double), then ().appendElements(T,U).append(NSFileManager) would equal (Int, Int, Double, Double, NSFileManager)
Then I think we’d only need a couple more features:
An implicit typealias in enums, which is a tuple of all the cases’ associated value’s type
The ability to restrict a protocol to only be conformable by enums, like we can already do with classes

and I think we’d get all this for free with a couple protocols:

protocol Initable { init() }
protocol HasDefaultValue {
    typealias DefaultValue
    static var defaultValue: DefaultValue {get}
}
extension HasDefaultValue where Self: Initable, DefaultValue == Self {
    static var defaultValue: DefaultValue { return Self.init() } // can be overridden if not appropriate
}
protocol FiniteEnumerable : enum, HasDefaultValue {
    typealias AssociatedValues // A tuple of the associated type for each case
    typealias DefaultValue
    static var defaultValues: DefaultValue {get}
    static var cases: [Self] {get}
}

extension FiniteEnumerable {
    static var count: Int { return Int(Mirror(reflecting: self.defaultValues).children.count) }
}
extension FiniteEnumerable where AssociatedValues: HasDefaultValue, DefaultValue == AssociatedValues.DefaultValue {
    static var defaultValues: DefaultValue { return AssociatedValues.defaultValue }
}
extension FiniteEnumerable where AssociatedValues: FiniteEnumerable, DefaultValue == [Self] {
    static var defaultValues: DefaultValue {
        var allCases = ()
        for i in 0 ..< Self.cases.count {
            for assocValue in AssociatedValues.cases[i] {
                allCases.append(theCase(assocValue)) // FWIW, this line and the “FiniteEnumerable : enum” parts are the only two errors Xcode can find.
            }
        }
        return allCases
    } // Over
}

Hmm… I may have gotten `defaultValues` and `cases` switched around in there. But the point is that they all could be added through protocol extensions, which have the advantage of:
1) provides default declarations
2) that are overridable if you don’t like them
3) it’ll throw a compile-time nonconformance error if there’s a hole in the protocol extensions
4) less magic.

- Dave Sweeris

···

On Jan 20, 2016, at 10:04, Joe Groff via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

On Jan 20, 2016, at 9:01 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> wrote:

I've been thinking about naming a bit. I'm worried that "values" / "allValues" won't make sense if this feature eventually goes in the direction of returning type constructors. For enums with associated types, the cases may actually be functions taking the associated type and returning the enum type. (There's an example under "Future directions" in the proposal). Calling these "values" seems strange to me, but calling them "cases" makes sense.

That seems like a fairly specialized use case to me. I'd expect the default behavior for enums with associated values to enumerate all possible values of each case (whose associated values would themselves need to be ValueEnumerable), not the case constructors themselves. That's more generally useful when using associated values to categorize constants.

-Joe

Then I don't even know what you would use CaseEnumerable for. Do you have a more concrete use case in mind?

-Joe

···

On Jan 20, 2016, at 3:13 PM, David Sweeris <davesweeris@mac.com> wrote:

If the associated values are all enums themselves, that's great. What if your enum is an error type, and the associated values are all strings with maybe diagnostic info or time stamps or something?

Off the top of my head?
enum NumClassification {
  case None(Int)
  case Even(Int)
  case Odd(Int)
  case Prime(Int)
  case Negative(Int)
  case Positive(Int) //Always gotta end on a positive note :-)
}

Although I must admit, if there’s a purpose to CaseEnumerable (or whatever it’s being called now) beyond just automatically getting a static cases:[Self] and labels:[String], I’ve missed the memo about it.

- Dave Sweeris

···

On Jan 20, 2016, at 15:18, Joe Groff <jgroff@apple.com> wrote:

On Jan 20, 2016, at 3:13 PM, David Sweeris <davesweeris@mac.com> wrote:

If the associated values are all enums themselves, that's great. What if your enum is an error type, and the associated values are all strings with maybe diagnostic info or time stamps or something?

Then I don't even know what you would use CaseEnumerable for. Do you have a more concrete use case in mind?

-Joe