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