While my main focus is on the proposal about consolidating cases, I had notes 
about almost that exact syntax... :-D

enum TokenState: State {

    let id: Int
    let token: String
    
    case loading {
        let startedAt: Date

        var description: String {
            return "Loading token from disk"
        }
    }

    case none {
        let reason: String
    }

}

The main thing to note is the difference between stored properties (well, 
associated values) and calculated ones. I think this new syntax can be done 
entirely within enum's current capabilities. The `let` statements outside the 
case simply define some associated values used by all cases. The `let` 
statements inside the cases declare additional associated values used for only 
that case.

In all areas, anything that's a `var` is dynamic, and so repeats type 
information in all `case` blocks because that seems like the least disruptive 
way to do it.

To Guillaume's point, this actually makes enums have _less_ special syntax, and 
treat them more like a closed family of structs with special powers.

BUT, again my focus is first on consolidating the case logic.

—Tim

> On Jan 9, 2017, at 11:11 AM, Sean Heber <[email protected]> wrote:
> 
> I would like something along these lines. I would suggest going farther and 
> do something like this if possible to avoid repeating the type information 
> all over the place:
> 
> enum OneOnOneField: Int {
>       let title: String
>       let placeholder: String
>       let image: UIImage
> 
>       case agenda {
>               title: NSLocalizedString("Agenda", comment: "One on one field 
> header")
>               placeholder: NSLocalizedString("Add an agenda", comment: "One 
> on one field placeholder”)
>               image: #imageLiteral(resourceName: "Agenda-Small”)
>       }
> 
>       // etc
> }
> 
> l8r
> Sean
> 
> 
> 
>> On Jan 9, 2017, at 11:54 AM, Tim Shadel via swift-evolution 
>> <[email protected]> wrote:
>> 
>> It seems like my original (simplified) examples didn't clearly highlight 
>> what the problem I'm focused on solving.
>> 
>> Enums get large, and they get complicated because the code for each case 
>> gets sliced up and scattered across many functions. It becomes a "one of 
>> these things is not like the other" situation because writing functions 
>> inside enums is unlike writing functions in any other part of Swift code.
>> 
>> Also, to be clear, my goal is _not_ code brevity. It is coherence, the 
>> property where related code is located together. Some increase in code 
>> verbosity is acceptable to make code more coherent, since that leads to 
>> long-term maintainability.
>> 
>> I've pulled out a few of the larger enums I've seen in code to try to 
>> illustrate this. Along the way, I've made a few alterations based on the 
>> comments I've seen come through. (Rien & Robert: I've pushed the definitions 
>> inside the declaration, to ensure they don't end up in other files since 
>> that was never my intention; Rien & Daniel: I've altered the syntax to open 
>> a brace right after the case declaration making everything outside it the 
>> default, and I like that better).
>> 
>> Here's an enum used to consolidate the descriptions of UI elements for a 
>> screen, allowing the datasource to alter their order, presentation, and 
>> visibility while keeping the set of possible fields clean and finite. These 
>> enum values are used much like singletons.
>> 
>> enum OneOnOneField: Int {
>> 
>> 
>> case agenda
>>    case summary
>>    case date
>>    case notes
>> 
>>    struct Info {
>> 
>>        var title
>> : String
>> 
>>        var placeholder
>> : String
>> 
>>        var image
>> :
>> UIImage
>> 
>> }
>> 
>> 
>>    var info
>> : Info {
>> 
>> 
>> switch self {
>> 
>> 
>> case .agenda:
>> 
>> 
>> return Info(
>> 
>>                title
>> : NSLocalizedString("Agenda", comment: "One on one field header"),
>> 
>>                placeholder
>> : NSLocalizedString("Add an agenda", comment: "One on one field 
>> placeholder"),
>> 
>>                image
>> : #imageLiteral(resourceName: "Agenda-Small"))
>> 
>> 
>> case .summary:
>> 
>> 
>> return Info(
>> 
>>                title
>> : NSLocalizedString("Summary", comment: "One on one field header"),
>> 
>>                placeholder
>> : NSLocalizedString("Add a summary", comment: "One on one field 
>> placeholder"),
>> 
>>                image
>> : #imageLiteral(resourceName: "Summary-Small"))
>> 
>> 
>> case .date:
>> 
>> 
>> return Info(title: "", placeholder: "", image: UIImage())
>> 
>> 
>> case .notes:
>> 
>> 
>> return Info(title: NSLocalizedString("Personal Notes", comment: "Title for 
>> personal notes screen"), placeholder: "", image: UIImage())
>> 
>> 
>> }
>> 
>> 
>> }
>> }
>> Consolidating them could instead look something like this:
>> 
>> enum OneOnOneField: Int {
>> 
>> 
>>    var title
>> : String { return "" }
>> 
>>    var placeholder
>> : String { return "" }
>> 
>>    var image
>> : UIImage { return UIImage() }
>> 
>> 
>> 
>> case agenda {
>>        var title: String { return NSLocalizedString("Agenda", comment: "One 
>> on one field header") }
>> 
>>        var placeholder
>> : String { return NSLocalizedString("Add an agenda", comment: "One on one 
>> field placeholder") }
>> 
>>        var image
>> : UIImage { return #imageLiteral(resourceName: "Agenda-Small") }
>> 
>> 
>> }
>> 
>> 
>> 
>> case summary {
>>        var title: String { return NSLocalizedString("Summary", comment: "One 
>> on one field header") }
>> 
>>        var placeholder
>> : String { return NSLocalizedString("Add a summary", comment: "One on one 
>> field placeholder") }
>> 
>>        var image
>> : UIImage { return #imageLiteral(resourceName: "Summary-Small") }
>> 
>> 
>> }
>> 
>> 
>> 
>> case date
>> 
>>    case notes {
>>        var title: String { return NSLocalizedString("Personal Notes", 
>> comment: "Title for personal notes screen") }
>> 
>> 
>> }
>> 
>> 
>> 
>> }
>> Here's an enum that implements the basics of a state machine for OAuth 2 
>> token use, refreshing, and login. Some of its cases have associated values 
>> and some don't.
>> 
>> enum TokenState: State {
>> 
>> 
>> 
>> case loading
>>    case none
>>    case expired(Date)
>>    case untested(token: String)
>>    case validated(token: String)
>> 
>>    var description: String {
>> 
>> 
>> switch self {
>> 
>> 
>> case .loading:
>> 
>> 
>> return "Loading token from disk"
>> 
>> 
>> case .none:
>> 
>> 
>> return "No token found"
>> 
>> 
>> case let .expired(at):
>> 
>> 
>> return "Expired at \(at)"
>> 
>> 
>> case let .untested(token):
>> 
>> 
>> return "Received token \(token), but it hasn't been tested."
>> 
>> 
>> case let .validated(token):
>> 
>> 
>> return "Token \(token) has been validated."
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>>    mutating func react
>> (to event: Event) {
>> 
>> 
>> switch self {
>> 
>> 
>> case .loading:
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as TokenNotFound:
>> 
>>                self 
>> = .
>> none
>> 
>> case let expired as TokenExpired:
>> 
>>                self 
>> = .expired(expired.at)
>> 
>> 
>> case let loaded as TokenLoaded:
>> 
>>                self 
>> = .untested(token: loaded.token)
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> case .none:
>> 
>> 
>> switch event {
>> 
>> 
>> case let loggedIn as UserLoggedIn:
>> 
>>                self 
>> = .untested(token: loggedIn.token)
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> case .expired:
>> 
>> 
>> switch event {
>> 
>> 
>> case let refreshed as TokenRefreshed:
>> 
>>                self 
>> = .untested(token: refreshed.token)
>> 
>> 
>> case _ as TokenRefreshErrored:
>> 
>>                self 
>> = .
>> none
>> 
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> case let .untested(token):
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as UserLoaded:
>> 
>>                self 
>> = .validated(token: token)
>> 
>> 
>> case _ as TokenRejected:
>> 
>>                self 
>> = .expired(at: Date())
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> case .validated:
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as TokenRejected:
>> 
>>                self 
>> = .expired(Date())
>> 
>> 
>> case _ as UserLoggedOut:
>> 
>>                self 
>> = .
>> none
>> 
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> static var initialState: TokenState {
>> 
>> 
>> return .
>> loading
>> 
>> }
>> 
>> 
>> 
>> }
>> After consolidation, this becomes:
>> 
>> enum TokenState: State {
>> 
>> 
>> 
>> static var initialState: TokenState {
>> 
>> 
>> return .
>> loading
>> 
>> }
>> 
>> 
>> 
>> case loading {
>>        var description: String {
>> 
>> 
>> return "Loading token from disk"
>> 
>> 
>> }
>> 
>> 
>>        mutating func react
>> (to event: Event) {
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as TokenNotFound:
>> 
>>                self 
>> = .
>> none
>> 
>> case let expired as TokenExpired:
>> 
>>                self 
>> = .expired(at: expired.at)
>> 
>> 
>> case let loaded as TokenLoaded:
>> 
>>                self 
>> = .untested(token: loaded.token)
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> case none {
>>        var description: String {
>> 
>> 
>> return "No token found"
>> 
>> 
>> }
>> 
>> 
>>        mutating func react
>> (to event: Event) {
>> 
>> 
>> switch event {
>> 
>> 
>> case let loggedIn as UserLoggedIn:
>> 
>>                self 
>> = .untested(token: loggedIn.token)
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> case expired(at: Date) {
>>        var description: String {
>> 
>> 
>> return "Expired at \(at)"
>> 
>> 
>> }
>> 
>> 
>>        mutating func react
>> (to event: Event) {
>> 
>> 
>> switch event {
>> 
>> 
>> case let refreshed as TokenRefreshed:
>> 
>>                self 
>> = .untested(token: refreshed.token)
>> 
>> 
>> case _ as TokenRefreshErrored:
>> 
>>                self 
>> = .
>> none
>> 
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> case untested(token: String) {
>>        var description: String {
>> 
>> 
>> return "Received token \(token), but it hasn't been tested."
>> 
>> 
>> }
>> 
>> 
>>        mutating func react
>> (to event: Event) {
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as UserLoaded:
>> 
>>                self 
>> = .validated(token: token)
>> 
>> 
>> case _ as TokenRejected:
>> 
>>                self 
>> = .expired(at: Date())
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> case validated(token: String) {
>>        var description: String {
>> 
>> 
>> return "Token \(token) has been validated."
>> 
>> 
>> }
>> 
>> 
>>        mutating func react
>> (to event: Event) {
>> 
>> 
>> switch event {
>> 
>> 
>> case _ as TokenRejected:
>> 
>>                self 
>> = .expired(at: Date())
>> 
>> 
>> case _ as UserLoggedOut:
>> 
>>                self 
>> = .
>> none
>> 
>>            default:
>> 
>> 
>> break
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> }
>> 
>> 
>> 
>> }
>> 
>> 
>> 
>>> On Jan 8, 2017, at 12:22 PM, Derrick Ho via swift-evolution 
>>> <[email protected]> wrote:
>>> 
>>> Currently we can write a helper method to aid in getting the values inside 
>>> the enum associated value.  Below is a fully working implementation:
>>> 
>>> ```
>>> enum Package {
>>>     case box(String, Int)
>>>     case circular(String)
>>>     
>>>     var associated: Associated {
>>>             return Associated(package: self)
>>>     }
>>>     
>>>     struct Associated {
>>>             let box: (String, Int)?
>>>             let circular: (String)?
>>>             init(package: Package) {
>>>                     switch package {
>>>                     case .box(let b):
>>>                             box = b
>>>                             circular = nil
>>>                     case .circular(let b):
>>>                             box = nil
>>>                             circular = b
>>>                     }
>>>             }
>>>     }
>>> }
>>> 
>>> let b = Package.box("square", 5)
>>> b.associated.box?.0 // Optional("square")
>>> b.associated.box?.1 // Optional(5)
>>> b.associated.circular // nil
>>> 
>>> let c = Package.circular("round")
>>> c.associated.box?.0 // nil
>>> c.associated.box?.1 // nil
>>> c.associated.circular // Optional("round")
>>> ```
>>> 
>>> I had to wedge in a special type called "Associated" and had to write some 
>>> boiler-plate code to get this effect.  It is quite predictable and can 
>>> probably be done under the hood.  I would of course prefer syntactic sugar 
>>> to simplify it and turn
>>> ```
>>> b.associated.box?.0
>>> ```
>>> into 
>>> ```
>>> b.box?.0
>>> ```
>>> 
>>> On Sun, Jan 8, 2017 at 1:05 PM David Sweeris via swift-evolution 
>>> <[email protected]> wrote:
>>> 
>>> On Jan 8, 2017, at 06:53, Karim Nassar via swift-evolution 
>>> <[email protected]> wrote:
>>> 
>>>> One area of enums that I’d love to see some sugar wrapped around (and 
>>>> perhaps this has already been discussed previously?) is extracting 
>>>> associated values.
>>>> 
>>>> There are many times where, given an enum like:
>>>> 
>>>> enum Feedback {
>>>>    case ok
>>>>    case info(String)
>>>>    case warning(String, Location)
>>>>    case error(String, Location)
>>>> }
>>>> 
>>>> I’d love it if we could tag the associated values with some semantic 
>>>> accessor, perhaps borrowed from tuples:
>>>> 
>>>> enum Feedback {
>>>>    case ok
>>>>    case info(msg: String)
>>>>    case warning(msg: String, loc: Location)
>>>>    case error(msg: String, loc: Location)
>>>> }
>>>> 
>>>> then:
>>>> 
>>>> let foo = self.getSomeFeedback() // -> Feedback
>>>> if let msg = foo.msg { // since not all cases can hold a ‘msg’ .msg is an 
>>>> Optional
>>>>    print(foo)
>>>> }
>>> 
>>> Can't remember if it's come up before, but +1. I can't count how many times 
>>> I've written something like:
>>> enum Foo : CustomStringConvertible {
>>>    case c1(T1)
>>>    case c2(T2)
>>>    ...
>>>    case cN(TN)
>>> 
>>>    var description: String {
>>>        switch self {
>>>            case .c1(let val): return "\(val)"
>>>            case .c2(let val): return "\(val)"
>>>            ...
>>>            case .cN(let val): return "\(val)"
>>>        }
>>>    }
>>> }
>>> 
>>> Being able to simplify that to:
>>> var description: String {
>>>    let nilDesc = "some appropriate description"
>>>    return "\(self.0 ?? nilDesc)"
>>> }
>>> 
>>> Would be great.
>>> 
>>> - Dave Sweeris 
>>> _______________________________________________
>>> 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