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

Reply via email to