Associated Enum Cases as Types

This is a bit of a follow-up from my Enums with Associated Data vs. Structs. If you haven’t seen that, some of this might be a little out-of-context.

Another issue I run into with working with enums with associated data is actually getting the data out of them! Let’s take a look at the following code sample:

enum Token  {
    case Keyword(keyword: String, offset: Int)
    case Identifier(identifier: String, offset: Int)
}

//
// The declarations; no real issues here.
//

let importkey = Token.Keyword(keyword: "import", offset: 0)
let funckey = Token.Keyword(keyword: "func", offset: 0)

//
// The ways to match on a value and get some data out. Ugh!
//

if case Token.Keyword("import", _) = importkey {
    print("import keyword")
}

if case let Token.Keyword(keyword, _) = importkey {
    if keyword == "import" { print("import keyword") }
    if keyword == "func" { print("func keyword") }
}

switch importkey {
case let .Keyword(keyword): print("\(keyword) keyword")
case let .Identifier(identifier): print("\(identifier)")
}

All of those suck, in my opinion. There are a few of problems:

  1. If I want to store the result, I end up with a really stupid construct.
  2. If there are multiple associated data fields, I get lots of _ in the matches.
  3. They use this = somevar syntax that is completely unlike anything else in Swift.

What I wish had happened was to treat the case values as types. I’ve modelled what that would look like here:

enum Token  {
    case Keyword(keyword: String, offset: Int)
    case Identifier(identifier: String, offset: Int)

    var Keyword_ : (keyword: String, offset: Int)? {
        if case let .Keyword(keyword, offset) = self {
            return (keyword, offset)
        }
        return nil
    }

    var Identifier_ : (identifier: String, offset: Int)? {
        if case let .Identifier(identifier, offset) = self {
            return (identifier, offset)
        }
        return nil
    }
}

Imagine that Keyword_ and Identifier_ were implicitly created by the compiler. The idea is to make use of the other constructs of the language, in this case, Optional to allow us to access the inner details of the enum.

// It's a tuple, so access by index is allowed.
if importkey.Keyword_?.0 == "import" {
    print("import keyword")
}

// These are named tuples, so access by name is allowed.
if importkey.Keyword_?.keyword == "import" {
    print("import keyword")
}

if let keyword = importkey.Keyword_?.keyword {
    print("\(keyword) keyword")
}

Now, here’s the part I really don’t understand. Swift is already essentially doing this, we just don’t have access to it in any way other than the case pattern matching stuff. I think it would have been more preferable to do this:

if case ("import", _) = importkey.Keyword {
    print("import keyword")
}

To me, this makes it clear that tuples are all matched the exact same way. Instead, we have associated data in enums (which is essentially a tuple) matched one way, and tuples, that may have the exact same structure, matched a completely differently way.

rdar://22704262

Associated Enum Cases as Types