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

-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

Attachment: signature.asc
Description: Message signed with OpenPGP using GPGMail

_______________________________________________
swift-evolution mailing list
[email protected]
https://lists.swift.org/mailman/listinfo/swift-evolution

Reply via email to