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


(Jacob Bandes-Storch) #1

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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html>
(December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html>
(December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html>
(January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type>
(June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum>
(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" <http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx>
available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`


(Jacob Bandes-Storch) #2

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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`


(Will Entriken) #3

I do like the prospect of being able to get a list of enumerations. Also, I
can attest to how painful this has been, I think you have cited my question
on SO. In English, it is disturbing to find that an enumeration ("enum") is
not enumerable.

If there is an enum which is not enumerable, a lesser distinction would be
whether it is countable. This allows you to use Card.count() which may also
useful.

I was surprised that 麻 was missing from your list.

···

On Sun, Jan 17, 2016 at 6:44 PM, Jacob Bandes-Storch via swift-evolution < swift-evolution@swift.org> 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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`

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


(Rod Brown) #4

While this isn’t in review yet, massive +1 from me. Good first implementation, and very welcome functionality.

Personally, I’m actually dealing with the difficulties of counts and enumerating enums in Obj-C and this is a pain in the neck. It’s not difficult, but it’s prone to breakage when someone alters code.

This would be a vast improvement.

···

On 18 Jan 2016, at 10:44 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> 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 <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>
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) <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December 8, 2015)
Proposal: Enum 'count' functionality <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December 21, 2015)
Draft Proposal: count property for enum types <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January 17, 2016)
Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

How to enumerate an enum with String type? <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June 3, 2014; question score 131)
How do I get the count of a Swift enum? <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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" <http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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 <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available for reflection, including GetValues() and GetNames().

Java implicitly declares <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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 <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.

The Template Haskell extension to Haskell provides a function reify which extracts info about types <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>, 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 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]
<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]`
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Joe Groff) #5

The functionality proposed is reasonable, but I'd suggest more generic, less enum-centric names, since this is reasonable functionality for any finite type to provide.

-Joe

···

On Jan 17, 2016, at 3:44 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> 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 <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>
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) <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December 8, 2015)
Proposal: Enum 'count' functionality <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December 21, 2015)
Draft Proposal: count property for enum types <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January 17, 2016)
Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

How to enumerate an enum with String type? <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June 3, 2014; question score 131)
How do I get the count of a Swift enum? <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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" <http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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 <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available for reflection, including GetValues() and GetNames().

Java implicitly declares <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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 <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.

The Template Haskell extension to Haskell provides a function reify which extracts info about types <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>, 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 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]
<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]`
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #6

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.

Thank you Jacob—this is a wonderful starting point. A few thoughts:

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.

Just wanted to note this really started the proposal off with some panache!

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.

I discuss these below.

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]

That's a pretty cool use case!

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.

Again, see below.

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

I don't think this is really necessary; it's exactly equivalent to `cases.map { $0.rawValue }`. (And I think if you're only going to have one, `cases` is definitely more important than `rawValues`.)

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

I think this is an excellent idea. As Joe points out, this would imply that the protocol and property should have names which don't include the word "case". (Renaming is also a good idea because if the property is `cases`, you'll be tempted to say `for case in Foo.cases`, but `for case` will be misparsed.)

Simply start the discussion, I suggest this definition:

  /// Any type which can only take on one of a limited, easily enumerable list of values may
  /// conform to FiniteType to make that list easy to access. For certain simple types,
  /// Swift will automatically generate an implementation of `values`.
  protocol FiniteType {
    /// A collection containing all possible values of this type.
    ///
    /// Invariant: T.values.contains(valueOfTypeT) for any valueOfTypeT. In other words, `values`
    /// must be truly exhaustive.
    static var values: ...
  }

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

Ouch, that's an ugly limitation. Is this going to change in Swift 3?

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

Theoretically, `cases` on an OptionSetType should return all combinations of options, so I think we'd need something different there.

  • 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 very strongly believe this is a job for reflection, not for our case mechanism. (So, I agree with you.)

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

Since I think I was the first to suggest this, of course I think it should be included, but I agree it can wait. If `CaseEnumerable` becomes an ordinary protocol which sometimes has an automatically-generated implementation, this basically just becomes a more sophisticated auto-generator that can be used in additional situations.

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

This would be a nice feature at some point, but I think it ought to go by a different name.

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

This is basically just the conditional conformance feature which I *believe* is already planned, plus the enumerable-associated-values feature. I like it a lot.

(Although it would be more accurate to say that `Optional<MyEnum>.cases` contains `MyEnum.cases.map(.Some) + [.None]`.)

···

--
Brent Royal-Gordon
Architechies


(Howard Lovatt) #7

+1 from me. Minor changes I would suggest are:

1. All enums implicitly implement `CaseEnumerable`, so that you do not
have to remember to add it.
2. That `CaseEnumerable` has exploit static members `cases` and
`caseFromName`. So that `CaseEnumerable` can be useful outside of `enum`
constructs. In Java the equivalent of `caseFromName` has proved beneficial.
3. That `cases` is a `Set<String>`, i.e. a more specific collection.

···

On Monday, 18 January 2016, Jacob Bandes-Storch via swift-evolution < swift-evolution@swift.org> 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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`

--
  -- Howard.


(Alan Skipp) #8

What do you think of the idea of enums acquiring successor() and predecessor() functions and the ability to use them in Ranges?

enum Char { case A, B, C, D, E, F }

let a = Char.A
a.successor() // Char.B

(Char.A ... Char.D) // Range Char.A -> Char.D


(Sune Foldager) #9

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

-Sune

···

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

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.


(David Sweeris) #10

I am NOT saying “here’s the answer”, but I put something up on my github that does some of this: https://github.com/TheOtherDave/EnumEnhancer

Again, not saying that’s how we should do it or anything, but if anyone thinks it’d be useful until the “official” enhancements come out, help yourself. There’ll still be boilerplate, but it makes some attempt to keep things correct.

- Dave Sweeris

···

On Jan 17, 2016, at 15:44, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> 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


(Slava Pestov) #11

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
https://lists.swift.org/mailman/listinfo/swift-evolution


(Chéyo Jiménez) #12

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.
https://bugs.swift.org/browse/SR-887

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>
- 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)
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html>
(December
8, 2015)
- Proposal: Enum 'count' functionality
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html>
(December
21, 2015)
- Draft Proposal: count property for enum types
<https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html>
(January
17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

- How to enumerate an enum with String type?
<http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type>
(June
3, 2014; question score 131)
- How do I get the count of a Swift enum?
<http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum>
(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"<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/>(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
<https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx>
available
for reflection, including GetValues() and GetNames().
-

Java implicitly declares
<http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>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
<http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.
-

The Template Haskell extension to Haskell provides a function reify which
extracts info about types
<http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
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]`


(Jacob Bandes-Storch) #13

While this isn’t in review yet, massive +1 from me. Good first
implementation, and very welcome functionality.

Personally, I’m actually dealing with the difficulties of counts and
enumerating enums in Obj-C and this is a pain in the neck. It’s not
difficult, but it’s prone to breakage when someone alters code.

If you're willing to share, I think it would help to have more examples of
what problems people are trying to solve.

Jacob

···

On Sun, Jan 17, 2016 at 5:56 PM, Rod Brown <rodney.brown6@icloud.com> wrote:

This would be a vast improvement.

On 18 Jan 2016, at 10:44 AM, Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> 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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`

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


(Jacob Bandes-Storch) #14

I do like the prospect of being able to get a list of enumerations. Also,
I can attest to how painful this has been, I think you have cited my
question on SO. In English, it is disturbing to find that an enumeration
("enum") is not enumerable.

I'm not sure which SO post you're referring to (neither of the two I linked
seems to be it) but if you have another relevant one, I'm happy to add it
to the intro section.

If there is an enum which is not enumerable, a lesser distinction would be
whether it is countable. This allows you to use Card.count() which may also
useful.

I'm not sure it would be much easier to provide this than to provide all
cases. I hope we'll eventually be able to generate cases recursively, at
which point Card.cases.count will work fine.

I was surprised that 麻 was missing from your list.

My vocabulary isn't very good. I'll add it :slight_smile:

···

On Sun, Jan 17, 2016 at 6:05 PM, Will Entriken <fulldecent@gmail.com> wrote:

On Sun, Jan 17, 2016 at 6:44 PM, Jacob Bandes-Storch via swift-evolution < > swift-evolution@swift.org> 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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`

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


(Trent Nadeau) #15

Big +1.

I think the proposed solution is plenty for a first implementation. The
rest of the possible variations can be added later in a backward-compatible
way.

I'm not sure if the rawValues property is useful enough as you can loop
through the cases (or use map) and get the rawValue for each. Is there a
use case for needing all of the raw values where you don't also need the
actual cases at some point?

···

On Sun, Jan 17, 2016 at 8:06 PM, Jacob Bandes-Storch via swift-evolution < swift-evolution@swift.org> wrote:

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
   <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>
   - 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)
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December
   8, 2015)
   - Proposal: Enum 'count' functionality
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December
   21, 2015)
   - Draft Proposal: count property for enum types
   <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January
   17, 2016)

Enumerating enumerations in Swift is also a popular topic on Stack
Overflow:

   - How to enumerate an enum with String type?
   <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June
   3, 2014; question score 131)
   - How do I get the count of a Swift enum?
   <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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"
<http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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
   <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available
   for reflection, including GetValues() and GetNames().
   -

   Java implicitly declares
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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
   <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>
   .
   -

   The Template Haskell extension to Haskell provides a function reify which
   extracts info about types
   <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>,
   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]`

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

--
Trent Nadeau


(Rod Brown) #16

In this case, I have a standard NS_ENUM with a series of cases. I require to create an NSArray by enumerating through the cases and adding an object per case.

To do so, I either assume what the last case is in the enum and perform a for loop, or I add a final item in the for loop e.g. “EGSectionEnum_Count”, and then enumerate to it.

This is fragile as the moment anyone adds a case beyond the final item, the code becomes incorrect. If I add the EGSectionEnum_Count item, I am exposing an invalid value. Also, it assumes that the enum NSInteger values are continuous increasing by +1. Where someone specifically defines a value for the item, the entire for loop enumerating through fails.

By allowing direct enumeration by case, we avoid assuming values for each item and therefore code fragility, we avoid exposing a count of the items as a separate enum value which should not exist, and we avoid the reliance on Integer based enums, as these previous methods would not work on Swift’s string- and other- type based enums.

···

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

On Sun, Jan 17, 2016 at 5:56 PM, Rod Brown <rodney.brown6@icloud.com <mailto:rodney.brown6@icloud.com>> wrote:
While this isn’t in review yet, massive +1 from me. Good first implementation, and very welcome functionality.

Personally, I’m actually dealing with the difficulties of counts and enumerating enums in Obj-C and this is a pain in the neck. It’s not difficult, but it’s prone to breakage when someone alters code.

If you're willing to share, I think it would help to have more examples of what problems people are trying to solve.

Jacob

This would be a vast improvement.

On 18 Jan 2016, at 10:44 AM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <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>
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) <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December 8, 2015)
Proposal: Enum 'count' functionality <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December 21, 2015)
Draft Proposal: count property for enum types <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January 17, 2016)
Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

How to enumerate an enum with String type? <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June 3, 2014; question score 131)
How do I get the count of a Swift enum? <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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" <http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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 <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available for reflection, including GetValues() and GetNames().

Java implicitly declares <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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 <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.

The Template Haskell extension to Haskell provides a function reify which extracts info about types <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>, 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 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]
<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]`
_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution


(Jacob Bandes-Storch) #17

Thanks for the feedback, Brent! Responses inline:

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

I think this is an excellent idea. As Joe points out, this would imply
that the protocol and property should have names which don't include the
word "case". (Renaming is also a good idea because if the property is
`cases`, you'll be tempted to say `for case in Foo.cases`, but `for case`
will be misparsed.)

Simply start the discussion, I suggest this definition:

        /// Any type which can only take on one of a limited, easily
enumerable list of values may
        /// conform to FiniteType to make that list easy to access. For
certain simple types,
        /// Swift will automatically generate an implementation of
`values`.
        protocol FiniteType {
                /// A collection containing all possible values of this
type.
                ///
                /// Invariant: T.values.contains(valueOfTypeT) for any
valueOfTypeT. In other words, `values`
                /// must be truly exhaustive.
                static var values: ...
        }

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.

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

Ouch, that's an ugly limitation. Is this going to change in Swift 3?

I can't speak to that. You might want to search the list to see if you can
find what's been said about the plan for Swift 3 generics so far.

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

Theoretically, `cases` on an OptionSetType should return all combinations
of options, so I think we'd need something different there.

As a user, I'd really love to be able to enumerate the individual "bits" of
an OptionSetType, such as all the NSWindow styleMask types: Borderless,
Titled, etc. .cases or .values seems to be a natural place to return
those, but I guess you're right that these wouldn't really encompass
*all* possible
cases/values, so maybe it doesn't fit as well as I'd hope.

Is it too crazy to suggest synthesizing a collection of all the *static
members* of a type which are of type Self? This would cover enum cases and
OptionSetType fields.

> • 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 very strongly believe this is a job for reflection, not for our case
mechanism. (So, I agree with you.)

Playing devil's advocate: if handling names are a job for reflection, why
isn't getting all cases/values also a job for reflection?

Jacob

···

On Mon, Jan 18, 2016 at 9:17 PM, Brent Royal-Gordon <brent@architechies.com> wrote:


(Step C) #18

Happy to see this proposal. Comments inline.

+1 from me. Minor changes I would suggest are:

1. All enums implicitly implement `CaseEnumerable`, so that you do not have to remember to add it.

I think explicit is better, particularly since this could have resilience implications. Changing the order of cases (or properties) can break your API, which is not true for plain enums currently. Though this means extensions that add conformance could still be fragile. Another option would be to require all cases to explicitly be listed again, but with compiler support to make sure the list is complete. That may be too verbose though.

2. That `CaseEnumerable` has exploit static members `cases` and `caseFromName`. So that `CaseEnumerable` can be useful outside of `enum` constructs. In Java the equivalent of `caseFromName` has proved beneficial.

`caseFromName` takes a String?

···

On Jan 18, 2016, at 7:21 PM, Howard Lovatt via swift-evolution <swift-evolution@swift.org> wrote:
3. That `cases` is a `Set<String>`, i.e. a more specific collection.

On Monday, 18 January 2016, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org> 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


(Daniel Steinberg) #19

+1 to the proposal and this comment.

···

On Jan 18, 2016, at 10:46 PM, Joe Groff via swift-evolution <swift-evolution@swift.org> wrote:

The functionality proposed is reasonable, but I'd suggest more generic, less enum-centric names, since this is reasonable functionality for any finite type to provide.

-Joe

On Jan 17, 2016, at 3:44 PM, Jacob Bandes-Storch via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>> 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 <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>
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) <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151207/001233.html> (December 8, 2015)
Proposal: Enum 'count' functionality <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20151221/003819.html> (December 21, 2015)
Draft Proposal: count property for enum types <https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160111/006853.html> (January 17, 2016)
Enumerating enumerations in Swift is also a popular topic on Stack Overflow:

How to enumerate an enum with String type? <http://stackoverflow.com/questions/24007461/how-to-enumerate-an-enum-with-string-type> (June 3, 2014; question score 131)
How do I get the count of a Swift enum? <http://stackoverflow.com/questions/27094878/how-do-i-get-the-count-of-a-swift-enum> (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" <http://natecook.com/blog/2014/10/loopy-random-enum-ideas/> (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 <https://msdn.microsoft.com/en-us/library/system.enum_methods.aspx> available for reflection, including GetValues() and GetNames().

Java implicitly declares <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3> 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 <http://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.9.3>.

The Template Haskell extension to Haskell provides a function reify which extracts info about types <http://hackage.haskell.org/package/template-haskell-2.10.0.0/docs/Language-Haskell-TH-Syntax.html#t:Info>, 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 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]
<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]`
_______________________________________________
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


(Jacob Bandes-Storch) #20

Yes: ErrorType.

···

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

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

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?

-Sune