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