[Idea] Find alternatives to `switch self`


(Brent Royal-Gordon) #1

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs
        
        static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }
        
        var description: String {
            switch(self) {
            case .Hearts:
                return ":heart:️"
            case .Spades:
                return ":spades:️"
            case .Diamonds:
                return ":diamonds:️"
            case .Clubs:
                return ":clubs:️"
            }
        }
        
        var isRed: Bool {
            switch(self) {
            case .Hearts, .Diamonds:
                return true
            case .Spades, .Clubs:
                return false
            }
        }
    }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs
        
        static var all = [ Hearts, Spades, Diamonds, Clubs ]
        
        var description: String {
        case .Hearts:
            return ":heart:️"
        case .Spades:
            return ":spades:️"
        case .Diamonds:
            return ":diamonds:️"
        case .Clubs:
            return ":clubs:️"
        }
        
        var isRed: Bool {
        case .Hearts, .Diamonds:
            return true
        case .Spades, .Clubs:
            return false
        }
    }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

    enum Suit: Int {
        var isRed: Bool { return false }
        
        case Hearts {
            let description: String { return ":heart:️" }
            let isRed: Bool { return true }
        }
        case Spades {
            let description: String { return ":spades:️" }
        }
        case Diamonds {
            let description: String { return ":diamonds:️" }
            let isRed: Bool { return true }
        }
        case Clubs {
            let description: String { return ":clubs:️" }
        }
        
        static var all = [ Hearts, Spades, Diamonds, Clubs ]
    }

Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

···

--
Brent Royal-Gordon
Architechies


(Scott Matthewman) #2

Looks like you’re coming close to @cjwirth’s approach of using a private
struct to represent the various combinations of values that a single enum
could represent:

http://cjwirth.com/2016/03/14/easier-enums/

It’s always tricky when coming up with generic and simple examples to
illustrate a pain point; I think in both your case and Caesar’s, enums’ switch
self is potentially liveable with.

When it gets to writing four or more for a single enum, the value object
approach has definite benefits – but it also feels as if it’s maybe a sign
that we’re leaning too heavily on the enum, and maybe there’s some other
amendments the problem’s object graph that may help.

···

Scott

On Wed, 23 Mar 2016 at 10:14 Brent Royal-Gordon via swift-evolution < swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating
phenomenon of `switch self` being absolutely everywhere. I first discovered
this in some of my very first Swift code, code so old we were still using
the `T[]` shorthand syntax:

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs

        static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs
] }

        var description: String {
            switch(self) {
            case .Hearts:
                return ":heart:️"
            case .Spades:
                return ":spades:️"
            case .Diamonds:
                return ":diamonds:️"
            case .Clubs:
                return ":clubs:️"
            }
        }

        var isRed: Bool {
            switch(self) {
            case .Hearts, .Diamonds:
                return true
            case .Spades, .Clubs:
                return false
            }
        }
    }

It would be nice if we could somehow eliminate that. I have two
suggestions:

* Implicitly switch on `self` at the top level of a function or accessor
(or at least an enum one with top-level `case` statements).

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs

        static var all = [ Hearts, Spades, Diamonds, Clubs ]

        var description: String {
        case .Hearts:
            return ":heart:️"
        case .Spades:
            return ":spades:️"
        case .Diamonds:
            return ":diamonds:️"
        case .Clubs:
            return ":clubs:️"
        }

        var isRed: Bool {
        case .Hearts, .Diamonds:
            return true
        case .Spades, .Clubs:
            return false
        }
    }

* Allow you to attach member definitions to particular cases. It would be
an error if they didn't all define the same members, unless there was a
top-level catchall.

    enum Suit: Int {
        var isRed: Bool { return false }

        case Hearts {
            let description: String { return ":heart:️" }
            let isRed: Bool { return true }
        }
        case Spades {
            let description: String { return ":spades:️" }
        }
        case Diamonds {
            let description: String { return ":diamonds:️" }
            let isRed: Bool { return true }
        }
        case Clubs {
            let description: String { return ":clubs:️" }
        }

        static var all = [ Hearts, Spades, Diamonds, Clubs ]
    }

Any thoughts? This has, to be honest, bothered me since approximately the
third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Tino) #3

I encountered the same issues and agree that it can be tedious to deal with enums.
Imho the first variant (skipping the switch(self) {} doesn't offer enough benefit to justify a special case.

The second is nice because it keeps the "properties" of each case together — but afaics, Swift doesn't use the model of case classes (like http://docs.scala-lang.org/tutorials/tour/case-classes.html) for its enums, so it is no good fit for the language (btw, shouldn't the cases be lower case?).

As "isRed" is declared as "var", I would expect it can be changed, but it's redeclared "let" afterwards… maybe this could be simplified by using each case-block like an init-method:
   enum Suit: Int {
       let isRed: Bool

       case Hearts {
          isRed = true
       }
….

Tino


#4

Hi Brent,

First of all, I like your suggestions however there was already a proposal about "Pattern matching partial functions" which could address this and many other issues (See pull request #111; unfortunately the discussion doesn't went on... Hopefully this time).

This proposal addresses the issue of switch statements which return something in every "case". It uses special closures as unapplied switch statements and uses a global "match" function:

func match<T, U>(value: T, closure: T -> U) -> U {
         return closure(value)
}

The case you describe could now be rewritten to:

// no returns
enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           return match(self) {
               case .Hearts: ":heart:️"
               case .Spades: ":spades:️"
               case .Diamonds: ":diamonds:️"
               case .Clubs: ":clubs:️"
           }
       }

       var isRed: Bool {
           return match(self) {
               case .Hearts, .Diamonds: true
               case .Spades, .Clubs: false
           }
       }
   }

// or even without case (which was highly controversial in the discussions)
enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           return match(self) {
               cases .Hearts: ":heart:️", .Spades: ":spades:️", .Diamonds: ":diamonds:️", .Clubs: ":clubs:️"
           }
       }

       var isRed: Bool {
           return match(self) {
               cases .Hearts, .Diamonds: true, .Spades, .Clubs: false
           }
       }
   }

What do you think about this?

Kind regards
- Maximilian

···

Am 23.03.2016 um 11:13 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org>:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           switch(self) {
           case .Hearts:
               return ":heart:️"
           case .Spades:
               return ":spades:️"
           case .Diamonds:
               return ":diamonds:️"
           case .Clubs:
               return ":clubs:️"
           }
       }

       var isRed: Bool {
           switch(self) {
           case .Hearts, .Diamonds:
               return true
           case .Spades, .Clubs:
               return false
           }
       }
   }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all = [ Hearts, Spades, Diamonds, Clubs ]

       var description: String {
       case .Hearts:
           return ":heart:️"
       case .Spades:
           return ":spades:️"
       case .Diamonds:
           return ":diamonds:️"
       case .Clubs:
           return ":clubs:️"
       }

       var isRed: Bool {
       case .Hearts, .Diamonds:
           return true
       case .Spades, .Clubs:
           return false
       }
   }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }
Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Dave Abrahams) #5

Type-switching is a “feature” of enums. If you don't want it, you could
consider using protocols instead :slight_smile:

···

on Wed Mar 23 2016, Brent Royal-Gordon <swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating
phenomenon of `switch self` being absolutely everywhere. I first
discovered this in some of my very first Swift code, code so old we
were still using the `T[]` shorthand syntax:

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs

        static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

        var description: String {
            switch(self) {
            case .Hearts:
                return ":heart:️"
            case .Spades:
                return ":spades:️"
            case .Diamonds:
                return ":diamonds:️"
            case .Clubs:
                return ":clubs:️"
            }
        }

        var isRed: Bool {
            switch(self) {
            case .Hearts, .Diamonds:
                return true
            case .Spades, .Clubs:
                return false
            }
        }
    }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or
accessor (or at least an enum one with top-level `case` statements).

    enum Suit: Int {
        case Hearts, Spades, Diamonds, Clubs

        static var all = [ Hearts, Spades, Diamonds, Clubs ]

        var description: String {
        case .Hearts:
            return ":heart:️"
        case .Spades:
            return ":spades:️"
        case .Diamonds:
            return ":diamonds:️"
        case .Clubs:
            return ":clubs:️"
        }

        var isRed: Bool {
        case .Hearts, .Diamonds:
            return true
        case .Spades, .Clubs:
            return false
        }
    }

* Allow you to attach member definitions to particular cases. It would
be an error if they didn't all define the same members, unless there
was a top-level catchall.

    enum Suit: Int {
        var isRed: Bool { return false }

        case Hearts {
            let description: String { return ":heart:️" }
            let isRed: Bool { return true }
        }
        case Spades {
            let description: String { return ":spades:️" }
        }
        case Diamonds {
            let description: String { return ":diamonds:️" }
            let isRed: Bool { return true }
        }
        case Clubs {
            let description: String { return ":clubs:️" }
        }

        static var all = [ Hearts, Spades, Diamonds, Clubs ]
    }

Any thoughts? This has, to be honest, bothered me since approximately
the third day I used the language; I'd love to address it sooner or
later.

--
Dave


(Andrey Tarantsov) #6

Honestly, we really need this for a lot of different cases:

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

A.


(David Waite) #7

In Java, the enum type behaves as an abstract class, sealed against the case members (which are singleton instance subclasses.)

While Swift enums aren’t discrete types or singletons, it sounds like what you would like is the ability to have an enum behave as so - to be able to override base (or protocol extension) behavior with a particular enum case, and have that translated most likely into a switch statement (most likely - I suppose if you are using witness tables it could optimize the switch away)

Actually with a protocol default behavior being overridden with a single enum case, this would give you functionality not possible today (referencing that protocol extension method)

In Java, I exploit the enum behavior to implement the State design pattern quite a bit, but am limited as Java enums are singletons and thus should be isolated from state. Swift enums are even more powerful here, but doing this in switch statements is a pain for maintainability.

I like the idea

-DW

···

On Mar 23, 2016, at 4:13 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           switch(self) {
           case .Hearts:
               return ":heart:️"
           case .Spades:
               return ":spades:️"
           case .Diamonds:
               return ":diamonds:️"
           case .Clubs:
               return ":clubs:️"
           }
       }

       var isRed: Bool {
           switch(self) {
           case .Hearts, .Diamonds:
               return true
           case .Spades, .Clubs:
               return false
           }
       }
   }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all = [ Hearts, Spades, Diamonds, Clubs ]

       var description: String {
       case .Hearts:
           return ":heart:️"
       case .Spades:
           return ":spades:️"
       case .Diamonds:
           return ":diamonds:️"
       case .Clubs:
           return ":clubs:️"
       }

       var isRed: Bool {
       case .Hearts, .Diamonds:
           return true
       case .Spades, .Clubs:
           return false
       }
   }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Radek Pietruszewski) #8

Ah, that’s interesting! This would make enum cases less like values, and more like their own types.

(Or maybe not — it’s still a property of the enum itself, and the definitions must cover all cases. But it does suggest a different way of thinking about what enum cases are. Maybe it would be a good thing to have enum cases be more like types, and have their own properties and stuff? I don’t know…)

— Radek

···

On 23 Mar 2016, at 11:13, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }


(Taras Zakharko) #9

This is what happens if you don’t follow the swift-evolution list for a while… I have send a very similar suggestion to the list today (but mine focuses more on methods). Didn’t mean to plagiarise your idea, sorry! Maybe we should bring the two of them together.

Best,

Taras

···

On 23 Mar 2016, at 11:13, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           switch(self) {
           case .Hearts:
               return ":heart:️"
           case .Spades:
               return ":spades:️"
           case .Diamonds:
               return ":diamonds:️"
           case .Clubs:
               return ":clubs:️"
           }
       }

       var isRed: Bool {
           switch(self) {
           case .Hearts, .Diamonds:
               return true
           case .Spades, .Clubs:
               return false
           }
       }
   }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all = [ Hearts, Spades, Diamonds, Clubs ]

       var description: String {
       case .Hearts:
           return ":heart:️"
       case .Spades:
           return ":spades:️"
       case .Diamonds:
           return ":diamonds:️"
       case .Clubs:
           return ":clubs:️"
       }

       var isRed: Bool {
       case .Hearts, .Diamonds:
           return true
       case .Spades, .Clubs:
           return false
       }
   }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Kenny Leung) #10

I much prefer the Java style of enums, which are essentially classes limited to a fixed set of instances. In Java, Suit would look like this:

public enum Suit {
   Hearts(":heart:", true),
   Spades(":spades:", false),
   Diamonds(":diamonds:", true),
   Clubs(":clubs:", false);

   String description;
   boolean isRed;

   Suit(String description, boolean isRed) {
      this.description = description;
      this.isRed = isRed;
   }
}

The class java.lang.Enum already provides handy methods like
    name(),
    ordinal(),
    toString(), and its inverse, valueOf()
    values() - to get all values

-Kenny

···

On Mar 23, 2016, at 3:13 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           switch(self) {
           case .Hearts:
               return ":heart:️"
           case .Spades:
               return ":spades:️"
           case .Diamonds:
               return ":diamonds:️"
           case .Clubs:
               return ":clubs:️"
           }
       }

       var isRed: Bool {
           switch(self) {
           case .Hearts, .Diamonds:
               return true
           case .Spades, .Clubs:
               return false
           }
       }
   }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all = [ Hearts, Spades, Diamonds, Clubs ]

       var description: String {
       case .Hearts:
           return ":heart:️"
       case .Spades:
           return ":spades:️"
       case .Diamonds:
           return ":diamonds:️"
       case .Clubs:
           return ":clubs:️"
       }

       var isRed: Bool {
       case .Hearts, .Diamonds:
           return true
       case .Spades, .Clubs:
           return false
       }
   }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Srstka) #11

Oh my, I absolutely *love this.* This would be a godsend for making ErrorType enums that bridge nicely to NSErrors, with code and userInfo properties attached to each case.

+1.

Charles

···

On Mar 23, 2016, at 5:13 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }


(Paul Ossenbruggen) #12

Thanx Maximillian for writing this, saved me from doing it and well put! :slight_smile:

- Paul

···

On Mar 23, 2016, at 5:46 AM, Maximilian Hünenberger via swift-evolution <swift-evolution@swift.org> wrote:

Hi Brent,

First of all, I like your suggestions however there was already a proposal about "Pattern matching partial functions" which could address this and many other issues (See pull request #111; unfortunately the discussion doesn't went on... Hopefully this time).

This proposal addresses the issue of switch statements which return something in every "case". It uses special closures as unapplied switch statements and uses a global "match" function:

func match<T, U>(value: T, closure: T -> U) -> U {
         return closure(value)
}

The case you describe could now be rewritten to:

// no returns
enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           return match(self) {
               case .Hearts: ":heart:️"
               case .Spades: ":spades:️"
               case .Diamonds: ":diamonds:️"
               case .Clubs: ":clubs:️"
           }
       }

       var isRed: Bool {
           return match(self) {
               case .Hearts, .Diamonds: true
               case .Spades, .Clubs: false
           }
       }
   }

// or even without case (which was highly controversial in the discussions)
enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           return match(self) {
               cases .Hearts: ":heart:️", .Spades: ":spades:️", .Diamonds: ":diamonds:️", .Clubs: ":clubs:️"
           }
       }

       var isRed: Bool {
           return match(self) {
               cases .Hearts, .Diamonds: true, .Spades, .Clubs: false
           }
       }
   }

What do you think about this?

Kind regards
- Maximilian

Am 23.03.2016 um 11:13 schrieb Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org <mailto:swift-evolution@swift.org>>:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           switch(self) {
           case .Hearts:
               return ":heart:️"
           case .Spades:
               return ":spades:️"
           case .Diamonds:
               return ":diamonds:️"
           case .Clubs:
               return ":clubs:️"
           }
       }

       var isRed: Bool {
           switch(self) {
           case .Hearts, .Diamonds:
               return true
           case .Spades, .Clubs:
               return false
           }
       }
   }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

   enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all = [ Hearts, Spades, Diamonds, Clubs ]

       var description: String {
       case .Hearts:
           return ":heart:️"
       case .Spades:
           return ":spades:️"
       case .Diamonds:
           return ":diamonds:️"
       case .Clubs:
           return ":clubs:️"
       }

       var isRed: Bool {
       case .Hearts, .Diamonds:
           return true
       case .Spades, .Clubs:
           return false
       }
   }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }
Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org <mailto:swift-evolution@swift.org>
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Brent Royal-Gordon) #13

As "isRed" is declared as "var", I would expect it can be changed, but it's redeclared "let" afterwards…

Sorry, that was a mistake on my part. I originally wrote all of the definitions as `let variable = value`, assuming Swift would optimize that into a single instance shared by all instances of a particular case. But upon further reflection I realized that this wouldn't work properly if the property returned an object, so I decided to change them to computed properties. When I did that, I forgot to change the `let`s to `var`s.

···

--
Brent Royal-Gordon
Architechies


(Brent Royal-Gordon) #14

Type-switching is a “feature” of enums. If you don't want it, you could
consider using protocols instead :slight_smile:

I don't think it's a bad thing that you *can* switch on an enum's type. Rather, I think that it is so commonly necessary that we should consider ways to avoid having to state it explicitly.

To try to get an idea of how common this is, I did a quick survey of the standard library. Of the 35 enum instance members I found:

* 12 had a `switch self` statement at the top level.
* 12 more, mainly in HashedCollections.swift.gyb, had a stdlib-only speed hack followed by a `switch self` containing the rest of the code. In normal user code, these would have just had the `switch self`.
* 11 had other code at the top level.

2/3 of members being entirely inside a `switch self` fits with my general sense of how things go down in user code.

I feel like, at that point, the `switch self` is basically just boilerplate, and we ought to consider whether or not we can get rid of it somehow.

···

--
Brent Royal-Gordon
Architechies


(Brent Royal-Gordon) #15

enum Suit: Int {
       case Hearts, Spades, Diamonds, Clubs

       static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

       var description: String {
           return match(self) {
               case .Hearts: ":heart:️"
               case .Spades: ":spades:️"
               case .Diamonds: ":diamonds:️"
               case .Clubs: ":clubs:️"
           }
       }

       var isRed: Bool {
           return match(self) {
               case .Hearts, .Diamonds: true
               case .Spades, .Clubs: false
           }
       }
   }

This attacks the `return` boilerplate rather than the `switch` boilerplate. That means that, while it helps in this case, it would not help with members which have complicated logic or side effects. (And technically, the boilerplate is still there—it's just a `match(self)` instead of a `switch self`. I feel like they're orthogonal issues.

···

--
Brent Royal-Gordon
Architechies


(Howard Lovatt) #16

+1 for adding Java behaviour, it is something I have missed.

The example would be:

enum Suit: Int, CustomStringConvertible {
       case Hearts {
           var description: String { return “:heart:️" }
       }
       case Spades {
           var description: String { return “:spades:️" }
       }
       case Diamonds {
           var description: String { return “:diamonds:️" }
       }
       case Clubs {
           var description: String { return “:clubs:️" }
       }
  
       // ...
}

The compiler could automatically generate the switch statement if that was better than virtual dispatch.

···

On 25 Mar 2016, at 2:42 AM, David Waite via swift-evolution <swift-evolution@swift.org> wrote:

In Java, the enum type behaves as an abstract class, sealed against the case members (which are singleton instance subclasses.)

While Swift enums aren’t discrete types or singletons, it sounds like what you would like is the ability to have an enum behave as so - to be able to override base (or protocol extension) behavior with a particular enum case, and have that translated most likely into a switch statement (most likely - I suppose if you are using witness tables it could optimize the switch away)

Actually with a protocol default behavior being overridden with a single enum case, this would give you functionality not possible today (referencing that protocol extension method)

In Java, I exploit the enum behavior to implement the State design pattern quite a bit, but am limited as Java enums are singletons and thus should be isolated from state. Swift enums are even more powerful here, but doing this in switch statements is a pain for maintainability.

I like the idea

-DW

On Mar 23, 2016, at 4:13 AM, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

If you've written enums before, you've no doubt noticed the irritating phenomenon of `switch self` being absolutely everywhere. I first discovered this in some of my very first Swift code, code so old we were still using the `T[]` shorthand syntax:

  enum Suit: Int {
      case Hearts, Spades, Diamonds, Clubs

      static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

      var description: String {
          switch(self) {
          case .Hearts:
              return ":heart:️"
          case .Spades:
              return ":spades:️"
          case .Diamonds:
              return ":diamonds:️"
          case .Clubs:
              return ":clubs:️"
          }
      }

      var isRed: Bool {
          switch(self) {
          case .Hearts, .Diamonds:
              return true
          case .Spades, .Clubs:
              return false
          }
      }
  }

It would be nice if we could somehow eliminate that. I have two suggestions:

* Implicitly switch on `self` at the top level of a function or accessor (or at least an enum one with top-level `case` statements).

  enum Suit: Int {
      case Hearts, Spades, Diamonds, Clubs

      static var all = [ Hearts, Spades, Diamonds, Clubs ]

      var description: String {
      case .Hearts:
          return ":heart:️"
      case .Spades:
          return ":spades:️"
      case .Diamonds:
          return ":diamonds:️"
      case .Clubs:
          return ":clubs:️"
      }

      var isRed: Bool {
      case .Hearts, .Diamonds:
          return true
      case .Spades, .Clubs:
          return false
      }
  }

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

  enum Suit: Int {
      var isRed: Bool { return false }

      case Hearts {
          let description: String { return ":heart:️" }
          let isRed: Bool { return true }
      }
      case Spades {
          let description: String { return ":spades:️" }
      }
      case Diamonds {
          let description: String { return ":diamonds:️" }
          let isRed: Bool { return true }
      }
      case Clubs {
          let description: String { return ":clubs:️" }
      }

      static var all = [ Hearts, Spades, Diamonds, Clubs ]
  }

Any thoughts? This has, to be honest, bothered me since approximately the third day I used the language; I'd love to address it sooner or later.

--
Brent Royal-Gordon
Architechies

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Sean Heber) #17

I like this. I might be inclined to do something a bit more like this, though, syntax-wise:

enum Suit {
  let description: String
  var isRed = false

  case hearts {
    description = ":heart:️"
    isRed = true
  }
  case spades {
    description = ":spades:️"
  }
  case diamonds {
    description = “:diamonds:️"
    isRed = true
  }
  case clubs {
    description = “:clubs:️"
  }
}

By the end of the enum {} you must provide a value for every property declared in the enum or else it’d be a compile error. If you declare one as “var”, then you can specify a default value that can be overridden by just a few cases such as the “isRed” example. Declaring a property as “let” means every case must supply their own value for it. (If something is declared as var but each case supplies a value, there’d be a warning to turn the “var” into a “let”.)

However just because the property is declared as var doesn’t mean you can actually arbitrarily change it externally:

var foo = Suit.hearts
foo.isRed = false // error - enum values are immutable

l8r
Sean

···

On Mar 31, 2016, at 7:15 AM, Radosław Pietruszewski via swift-evolution <swift-evolution@swift.org> wrote:

On 23 Mar 2016, at 11:13, Brent Royal-Gordon via swift-evolution <swift-evolution@swift.org> wrote:

* Allow you to attach member definitions to particular cases. It would be an error if they didn't all define the same members, unless there was a top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

Ah, that’s interesting! This would make enum cases less like values, and more like their own types.

(Or maybe not — it’s still a property of the enum itself, and the definitions must cover all cases. But it does suggest a different way of thinking about what enum cases are. Maybe it would be a good thing to have enum cases be more like types, and have their own properties and stuff? I don’t know…)

— Radek

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Charles Constant) #18

Would this still be an issue if switch statements were less verbose? I
don't see why a "switch" statements should be so much more verbose than an
"if statement".

Writing boiler plate for switch statements is by far my #1 irritation at
the moment (now that argument labels have been made beautiful - thanks! -
and Generics are being improved).

As Paul alluded to, this has been discussed with
https://github.com/cacruden/swift-evolution/blob/master/proposals/0024-Pattern-Matching-Partial-Function.md

···

On Fri, Apr 8, 2016 at 2:30 PM, Charles Srstka via swift-evolution < swift-evolution@swift.org> wrote:

On Mar 23, 2016, at 5:13 AM, Brent Royal-Gordon via swift-evolution < > swift-evolution@swift.org> wrote:

* Allow you to attach member definitions to particular cases. It would be
an error if they didn't all define the same members, unless there was a
top-level catchall.

   enum Suit: Int {
       var isRed: Bool { return false }

       case Hearts {
           let description: String { return ":heart:️" }
           let isRed: Bool { return true }
       }
       case Spades {
           let description: String { return ":spades:️" }
       }
       case Diamonds {
           let description: String { return ":diamonds:️" }
           let isRed: Bool { return true }
       }
       case Clubs {
           let description: String { return ":clubs:️" }
       }

       static var all = [ Hearts, Spades, Diamonds, Clubs ]
   }

Oh my, I absolutely *love this.* This would be a godsend for making
ErrorType enums that bridge nicely to NSErrors, with code and userInfo
properties attached to each case.

+1.

Charles

_______________________________________________
swift-evolution mailing list
swift-evolution@swift.org
https://lists.swift.org/mailman/listinfo/swift-evolution


(Vladimir) #19

I also agree that there are some problems with enums in current Swift.

(tl;dr: will Swift 3.0 improve iteration for enum values? Some kind of .next() method for values or .all[] property. Currently we have a problem with this.)

Java's version of enum declaration in this case seems much better IMO. It allows to assign additional parameters to "main" enum value in handy way and in one place(where the main value itself is declared). In Swift to implement the same we have to write these for each param:
    var <param>: <Type> {
        switch self {
            case .<first> : return <for first>
            case .<second> : return <for second>
            case .<etc> : return <for etc>
        }
    }

Actually, as I understand , we can use custom struct as RawValue to have assigned params for each value i.e. something like this:
enum Devices: CGSize {
    case iPhone5 = CGSize(width: 320, height: 568)
    case iPhone6 = CGSize(width: 375, height: 667)
    case iPhone6Plus = CGSize(width: 414, height: 736)
}
and
extension CGSize: StringLiteralConvertible {..}
let a = Devices.iPhone5
let b = a.rawValue
print("string is \(a), width is \(b.width), height is \(b.height)")
(found in Internet)

But personally I don't think this is big problem or if this is totally unusable. I think we can live with this.

The real problem is iteration the enum values.

In initial message for this [Idea], in code sample, they use static property to be able to iterate enum values - array where all values is stored:
static var all = [ Hearts, Spades, Diamonds, Clubs ]

If we add new enum value to this enum - Swift will protect us and force us to add one more case: in each "switch self".
But! It will not protect us from not adding this new value to this "all" array. This is absolutely not-Swift way.

Without such "all" property, how will you iterate values for such enum? I didn't find any good method to iterate enums.

In Delphi(yes, I know :wink: you can iterate enums using such syntax:
i: EnumType
for i := Low(EnumType) to High(EnumType)
i.e. it will always work correctly, don't need to change this if you added value.
and can have such declaration of array where indexes are EnumType:
and it will force you to fill/fix this array if you added/deleted the enum value.

In Swift we need something to normally iterate enum values. Probably some .next()->EnumType? method for each value or built-in .all:[EnumType] property.

Am I wrong somewhere? Are there some plans to improve enums?

···

a: array [EnumType] of String = ('1', '2', '3', '4')

On 08.04.2016 21:13, Kenny Leung via swift-evolution wrote:

I much prefer the Java style of enums, which are essentially classes limited to a fixed set of instances. In Java, Suit would look like this:

public enum Suit {
    Hearts(":heart:", true),
    Spades(":spades:", false),
    Diamonds(":diamonds:", true),
    Clubs(":clubs:", false);

    String description;
    boolean isRed;

    Suit(String description, boolean isRed) {
       this.description = description;
       this.isRed = isRed;
    }
}

The class java.lang.Enum already provides handy methods like
     name(),
     ordinal(),
     toString(), and its inverse, valueOf()
     values() - to get all values

-Kenny


#20

I don't think that removing "switch(self)" would remove much boilerplate. I rather see the returns and cases as such.

It is true your second approach is much more maintainable if you want to add or remove a case. However I think it suffers from readability and you cannot group related logic inside computed properties. So the upcoming boilerplate is much bigger than the one you're about to remove.
In addition if you always use exhaustive switches the maintainability isn't that bad.

- Maximilian

···

Am 24.03.2016 um 02:22 schrieb Brent Royal-Gordon <brent@architechies.com>:

enum Suit: Int {
      case Hearts, Spades, Diamonds, Clubs

      static var all: Suit[] { return [ Hearts, Spades, Diamonds, Clubs ] }

      var description: String {
          return match(self) {
              case .Hearts: ":heart:️"
              case .Spades: ":spades:️"
              case .Diamonds: ":diamonds:️"
              case .Clubs: ":clubs:️"
          }
      }

      var isRed: Bool {
          return match(self) {
              case .Hearts, .Diamonds: true
              case .Spades, .Clubs: false
          }
      }
  }

This attacks the `return` boilerplate rather than the `switch` boilerplate. That means that, while it helps in this case, it would not help with members which have complicated logic or side effects. (And technically, the boilerplate is still there—it's just a `match(self)` instead of a `switch self`. I feel like they're orthogonal issues.

--
Brent Royal-Gordon
Architechies