I'm not much fond of Java enums but then Java also doesn't have structs so I think enums there were created to be a mix of structs and enums and that's why you can do all the things you mention on your list. That said: - The tuple typed enum approach is the closest thing we discussed to constructors like the ones in Java enums; - I never knew one could do that in Java but I think tuples can hold function types; - I also never heard one can do that in Java but I begin to think what you really want are structs here; - Both approaches here provide that, but accessors would allow for dynamic content to be generated, not static.
Also Swift already allow for functions to be declared in enums and I think this would be a better place than accessor properties to place code that will generate dynamic results. L On 1 June 2016 at 11:59, Matthew Johnson via swift-evolution <[email protected]> wrote: > > > Sent from my iPad > >> On Jun 1, 2016, at 9:48 AM, David Waite via swift-evolution >> <[email protected]> wrote: >> >> One thing I did often in Java (and miss in Swift) is using their enums to >> build state machines or implement command patterns for common commands. >> >> Java enums are a sealed set of subclasses of the enum base type with >> (hopefully) immutable, singleton instances. So you can do fun things like: >> - Define the base class constructor to be called to instantiate the >> subclasses, and declare the cases with the constructor arguments >> - Declare a method on the base type and refine it on 1-2 particular cases >> - Declare the enum implements an interface, and implement that interface >> separately for each case. >> - Define data accessors specific to the type (such as the planets example >> above) >> >> I like the SuitInfo approach below - with extensions, I think I can get >> close to what I have done in the past with Java. Maybe one day there is >> syntax to do this in the language directly > > This is pretty similar to what we were discussing last week in this thread: > https://lists.swift.org/pipermail/swift-evolution/Week-of-Mon-20160523/018799.html > > I'm planning to write up a proposal when I have time (hopefully in the next > week or so). > >> >> -DW >> >>> On May 31, 2016, at 11:44 AM, Vladimir.S via swift-evolution >>> <[email protected]> wrote: >>> >>> I'm not sure about my opinion on this proposal, but I believe you should >>> add this as alternatives of how we can have the similar features today >>> without injecting stored properties into enums : >>> >>> enum Suit { >>> case spades >>> case hearts >>> case diamonds >>> case clubs >>> >>> struct SuitInfo { >>> let simpleDescription: String >>> let color: UIColor >>> let symbol: String >>> let bezierPath: UIBezierPath >>> } >>> >>> var info : SuitInfo { >>> switch self { >>> case .spades: >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "spades", >>> color: .blackColor(), >>> symbol: "♠", >>> bezierPath: path) >>> >>> case .hearts: >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "hearts", >>> color: .redColor(), >>> symbol: "♥", >>> bezierPath: path) >>> >>> case .diamonds: >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "diamonds", >>> color: .redColor(), >>> symbol: "♦", >>> bezierPath: path) >>> >>> case .clubs: >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "clubs", >>> color: .blackColor(), >>> symbol: "♣", >>> bezierPath: path) >>> >>> } >>> } >>> } >>> >>> and this: >>> >>> enum Suit { >>> case spades >>> case hearts >>> case diamonds >>> case clubs >>> >>> struct SuitInfo { >>> let simpleDescription: String >>> let color: UIColor >>> let symbol: String >>> let bezierPath: UIBezierPath >>> } >>> >>> static let spadesInfo : SuitInfo = { >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "spades", >>> color: .blackColor(), >>> symbol: "♠", >>> bezierPath: path) >>> }() >>> >>> static let heartsInfo : SuitInfo = { >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "hearts", >>> color: .redColor(), >>> symbol: "♥", >>> bezierPath: path) >>> }() >>> >>> static let diamondsInfo : SuitInfo = { >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "diamonds", >>> color: .redColor(), >>> symbol: "♦", >>> bezierPath: path) >>> }() >>> >>> static let clubsInfo : SuitInfo = { >>> let path = UIBezierPath() >>> // omitted lines ... >>> >>> return SuitInfo( >>> simpleDescription: "clubs", >>> color: .blackColor(), >>> symbol: "♣", >>> bezierPath: path) >>> }() >>> >>> >>> var info : SuitInfo { >>> switch self { >>> case .spades: return Suit.spadesInfo >>> case .hearts: return Suit.heartsInfo >>> case .diamonds: return Suit.diamondsInfo >>> case .clubs: return Suit.clubsInfo >>> } >>> } >>> } >>> >>> >>>> On 31.05.2016 17:17, Jānis Kiršteins via swift-evolution wrote: >>>> I wrote a proposal draft: >>>> >>>> # Enum case stored properties >>>> >>>> * Proposal: TBD >>>> * Author: [Janis Kirsteins](https://github.com/kirsteins) >>>> * Status: TBD >>>> * Review manager: TBD >>>> >>>> ## Introduction >>>> >>>> This proposal allows each enum case to have stored properties. >>>> >>>> ## Motivation >>>> >>>> Enums cases can have a lot of constant (or variable) static values >>>> associated with it. For example, planets can have mass, radius, age, >>>> closest star etc. Currently there is no way to set or get those values >>>> easily. >>>> >>>> Example below shows that is hard to read and manage static associated >>>> values with each case. It is hard to add or remove case as it would >>>> require to add or remove code in four different places in file. Also >>>> static associated value like `UIBezierPath` is recreated each time the >>>> property is computed while it's constant. >>>> >>>> ```swift >>>> enum Suit { >>>> case spades >>>> case hearts >>>> case diamonds >>>> case clubs >>>> >>>> var simpleDescription: String { >>>> switch self { >>>> case .spades: >>>> return "spades" >>>> case .hearts: >>>> return "hearts" >>>> case .diamonds: >>>> return "diamonds" >>>> case .clubs: >>>> return "clubs" >>>> } >>>> } >>>> >>>> var color: UIColor { >>>> switch self { >>>> case .spades: >>>> return .blackColor() >>>> case .hearts: >>>> return .redColor() >>>> case .diamonds: >>>> return .redColor() >>>> case .clubs: >>>> return .blackColor() >>>> } >>>> } >>>> >>>> var symbol: String { >>>> switch self { >>>> case .spades: >>>> return "♠" >>>> case .hearts: >>>> return "♥" >>>> case .diamonds: >>>> return "♦" >>>> case .clubs: >>>> return "♣" >>>> } >>>> } >>>> >>>> var bezierPath: UIBezierPath { >>>> switch self { >>>> case .spades: >>>> let path = UIBezierPath() >>>> // omitted lines ... >>>> return path >>>> case .hearts: >>>> let path = UIBezierPath() >>>> // omitted lines ... >>>> return path >>>> case .diamonds: >>>> let path = UIBezierPath() >>>> // omitted lines ... >>>> return path >>>> case .clubs: >>>> let path = UIBezierPath() >>>> // omitted lines ... >>>> return path >>>> } >>>> } >>>> } >>>> ``` >>>> >>>> ## Proposed solution >>>> >>>> Support stored properties for enum cases just as each case were an >>>> instance. Case properties are initialized block after each case >>>> declaration. >>>> >>>> ```swift >>>> enum Suit { >>>> let simpleDescription: String >>>> let color: UIColor >>>> let symbol: String >>>> let bezierPath: UIBezierPath >>>> >>>> case spades { >>>> simpleDescription = "spades" >>>> color = .blackColor() >>>> symbol = "♠" >>>> let bezierPath = UIBezierPath() >>>> // omitted lines ... >>>> self.bezierPath = bezierPath >>>> } >>>> >>>> case hearts { >>>> simpleDescription = "hearts" >>>> color = .redColor() >>>> symbol = "♥" >>>> let bezierPath = UIBezierPath() >>>> // omitted lines ... >>>> self.bezierPath = bezierPath >>>> } >>>> >>>> case diamonds { >>>> simpleDescription = "diamonds" >>>> color = .redColor() >>>> symbol = "♦" >>>> let bezierPath = UIBezierPath() >>>> // omitted lines ... >>>> self.bezierPath = bezierPath >>>> } >>>> >>>> case clubs { >>>> simpleDescription = "clubs" >>>> color = .blackColor() >>>> symbol = "♣" >>>> let bezierPath = UIBezierPath() >>>> // omitted lines ... >>>> self.bezierPath = bezierPath >>>> } >>>> } >>>> >>>> let symbol = Suit.spades.symbol // "♠" >>>> ``` >>>> >>>> The proposed solution improves: >>>> - Readability as cases are closer with their related data; >>>> - Improves code maintainability as a case can be removed or added in one >>>> place; >>>> - Improved performance as there is no need to recreate static values; >>>> - ~30% less lines of code in given example. >>>> >>>> ## Detailed design >>>> >>>> #### Stored properties >>>> >>>> Enum stored properties are supported the same way they are supported >>>> for structs can classes. Unlike enum associated values, stored >>>> properties are static to case and are shared for the same case. >>>> >>>> Properties are accessed: >>>> ```swift >>>> let simpleDescription = Suit.spades.simpleDescription >>>> ``` >>>> >>>> Mutable properties can be set: >>>> ```swift >>>> Suit.spades.simpleDescription = "new simple description" >>>> ``` >>>> >>>> #### Initialization >>>> >>>> If enum has uninitialized stored property it must be initialized in a >>>> block after each case declaration. The block work the same way as >>>> struct initialization. At the end of initialization block all >>>> properties must be initialized. >>>> >>>> ```swift >>>> enum Suit { >>>> var simpleDescription: String >>>> >>>> case spades { >>>> simpleDescription = "spades" >>>> } >>>> } >>>> ``` >>>> >>>> Initialization block can be combine with use of `rawValue`: >>>> >>>> ```swift >>>> enum Suit: Int { >>>> var simpleDescription: String >>>> >>>> case spades = 1 { >>>> simpleDescription = "spades" >>>> } >>>> } >>>> ``` >>>> or associated values of the case: >>>> >>>> ```swift >>>> enum Suit { >>>> var simpleDescription: String >>>> >>>> case spades(Int) { >>>> simpleDescription = "spades" >>>> } >>>> } >>>> ``` >>>> >>>> ## Impact on existing code >>>> >>>> Stored properties for enums are not currently not supported, so there >>>> is no impact on existing code. >>>> >>>> ## Alternatives considered >>>> >>>> - Use labeled tuple as `rawValue` of the enum case. This approach is >>>> not compatible as it conflicts with intention of `rawValue` of Swift >>>> enum; >>>> - Use per case initializer like [Java >>>> Enum](https://docs.oracle.com/javase/tutorial/java/javaOO/enum.html). >>>> Swift enum uses custom initializer syntax to setup instances, not >>>> cases. So this approach is not suitable for Swift. >>>> >>>> >>>>> On Sun, May 29, 2016 at 3:42 PM, Leonardo Pessoa <[email protected]> >>>>> wrote: >>>>> I think that's the case with enums. You're changing their current >>>>> behaviour of only having stored values to one in which it's computed >>>>> (even if only once and then stored). Enums are IMO something that have a >>>>> static value you know beforehand and can count on. That's why I'm not >>>>> fond of the accessor proposal. Otherwise I think we're transforming enums >>>>> into a closed set of struct instances and one could do that already by >>>>> using a private init. >>>>> >>>>> >>>>>> On 29 May 2016, at 3:38 am, Jānis Kiršteins via swift-evolution >>>>>> <[email protected]> wrote: >>>>>> >>>>>> I agree with the argument about use of "where", not replacing the raw >>>>>> value and having some kind of initialization block. But I cannot see >>>>>> why "accessors" concept is any better than stored properties to solve >>>>>> the particular problem. The "accessors" concept has much wider scope >>>>>> than enums and is a separate proposal. >>>>>> >>>>>> On Sat, May 28, 2016 at 11:39 PM, Brent Royal-Gordon >>>>>> <[email protected]> wrote: >>>>>>>>> - Abusing rawValue is just that: an abuse. >>>>>>>> >>>>>>>> My original proposal does not replace rawValue and is compatible with >>>>>>>> it. >>>>>>> >>>>>>> `rawValue` has a different purpose from how you're using it. It's >>>>>>> supposed to allow you to convert your type to some other *equivalent* >>>>>>> type, like an equivalent integer or string. Moreover, it's supposed to >>>>>>> allow you to *reconstruct* the instance from the raw value—remember, >>>>>>> `RawRepresentable` has an `init(rawValue:)` requirement. >>>>>>> >>>>>>> It is *not* supposed to be an ancillary bag of information on the side. >>>>>>> You're cramming a square peg into a round hole here. >>>>>>> >>>>>>> (Also, if you use `rawValue` for an ancillary bag of information, that >>>>>>> means you *can't* use it on the same type for its intended purpose. For >>>>>>> instance, you would not be able to assign numbers to your Planet enum's >>>>>>> cases to help you serialize them or bridge them to Objective-C. That's >>>>>>> not good.) >>>>>>> >>>>>>>>> - Using `where` just doesn't match the use of `where` elsewhere in >>>>>>>>> the language; everywhere else, it's some kind of condition. >>>>>>>> >>>>>>>> It is also used in generic type constraints. Plus it reads like human >>>>>>>> language: `case mercury where (mass: 3.303e+23, radius: 2.4397e6)` >>>>>>> >>>>>>> But a generic constraint is also a type of condition: it specifies >>>>>>> types which are permitted and divides them from types that are not. >>>>>>> >>>>>>> This is *not* a condition. It's not anything like a condition. It's >>>>>>> simply not consistent with anything else in the language. >>>>>>> >>>>>>>>> - Dictionaries are the most straightforward way to handle this with >>>>>>>>> the current language, but their lack of exhaustiveness checking is a >>>>>>>>> problem. >>>>>>>> >>>>>>>> Dictionaries can be used as workaround, but they cannot (lack of >>>>>>>> exhaustiveness) solve the problem. >>>>>>> >>>>>>> I agree that they're a halfway solution. >>>>>>> >>>>>>> If `ValuesEnumerable` were to be accepted (and to have a generic >>>>>>> requirement for its `allValues` property), you could write a >>>>>>> Dictionary-like type which ensured at initialization time that it was >>>>>>> exhaustive. That's not as good as compile time, but it's not bad—sort >>>>>>> of a three-quarters solution. >>>>>>> >>>>>>> struct ExhaustiveDictionary<Key: Hashable, Value where Key: >>>>>>> ValuesEnumerable>: Collection, DictionaryLiteralConvertible { >>>>>>> private var dictionary: [Key: Value] >>>>>>> >>>>>>> init(dictionaryLiteral elements: (Key, Value)...) { >>>>>>> dictionary = [:] >>>>>>> for (k, v) in elements { >>>>>>> dictionary[k] = v >>>>>>> } >>>>>>> >>>>>>> if dictionary.count != Key.allValues.count { >>>>>>> let missingKeys = Key.allValues.filter { >>>>>>> dictionary[$0] == nil } >>>>>>> preconditionFailure("ExhaustiveDictionary >>>>>>> is missing elements from \(Key.self): \(missingKeys)") >>>>>>> } >>>>>>> } >>>>>>> >>>>>>> var startIndex: Dictionary.Index { >>>>>>> return dictionary.startIndex >>>>>>> } >>>>>>> var endIndex: Dictionary.Index { >>>>>>> return dictionary.endIndex >>>>>>> } >>>>>>> subscript(index: Dictionary.Index) -> (Key, Value) { >>>>>>> return dictionary[index] >>>>>>> } >>>>>>> func index(after i: Dictionary.Index) -> Dictionary.Index { >>>>>>> return dictionary.index(after: i) >>>>>>> } >>>>>>> >>>>>>> subscript(key: Key) -> Value { >>>>>>> get { return dictionary[key]! } >>>>>>> set { dictionary[key] = newValue } >>>>>>> } >>>>>>> } >>>>>>> >>>>>>>>> What I would do is borrow the "accessors" concept from the property >>>>>>>>> behaviors proposal and extend it so that it supported both functions >>>>>>>>> and variables. >>>>>>>> >>>>>>>> Wouldn't accessor just be a redundant keyword here? Currently enums do >>>>>>>> not support stored properties, so I guess there is no extra need to >>>>>>>> mark properties with any special keyword. >>>>>>> >>>>>>> The keyword is mainly to indicate the unusual syntax at the definition >>>>>>> site, where you only have to specify the name of the accessor you're >>>>>>> defining, not a `func` or `var` keyword, a return type, or even >>>>>>> parameter names. (Like `willSet`, there's a default parameter name you >>>>>>> can use.) Secondarily, though, I think it's helpful to indicate very >>>>>>> explicitly that this is not an ordinary method or property definition, >>>>>>> even if the compiler could perhaps sort things out without it. >>>>>>> `accessor` is something a user can Google if they've never seen it >>>>>>> before. >>>>>>> >>>>>>>> Property accessors might work for enums with associated values, but >>>>>>>> not so well without them. >>>>>>> >>>>>>> The two have nothing to do with each other. I showed your planets >>>>>>> example, which has no associated values but uses accessors just fine. >>>>>>> >>>>>>> -- >>>>>>> Brent Royal-Gordon >>>>>>> Architechies >>>>>> _______________________________________________ >>>>>> swift-evolution mailing list >>>>>> [email protected] >>>>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>>> _______________________________________________ >>>> swift-evolution mailing list >>>> [email protected] >>>> https://lists.swift.org/mailman/listinfo/swift-evolution >>> _______________________________________________ >>> swift-evolution mailing list >>> [email protected] >>> https://lists.swift.org/mailman/listinfo/swift-evolution >> >> _______________________________________________ >> swift-evolution mailing list >> [email protected] >> https://lists.swift.org/mailman/listinfo/swift-evolution > > _______________________________________________ > swift-evolution mailing list > [email protected] > https://lists.swift.org/mailman/listinfo/swift-evolution _______________________________________________ swift-evolution mailing list [email protected] https://lists.swift.org/mailman/listinfo/swift-evolution
