Proposal draft for `is case` (pattern-match boolean expressions)

I think this is certainly a problem worth solving, and this looks like a great simple solution.

I've thought about this before, and had a different approach in mind. I don't know if it's better or worse, but I'll toss it out there:

I thought it would be nice if each case of an enum also produced a new corresponding type, as a subtype of the enum. Here are some of the features this allows:

  1. Detecting a case ignoring any associated values

    (Akin to is case .success(_))

    Take Result for an example. It might have two subtype auto-generated: Result.Success and Result.Failure. You would be able to use conventional dynamic type checking with is, like:

    if result is Result.Success { ... }
    
  2. Accessing associated values

    This subtype can act like a tuple, with one named property per associated value of the enum case. This could be used in if statements like with any other value:

    if let success = result as Result.Success, success.value == 123 { ... }
    
    
  3. Extracting functions that operate on a single case

    Suppose you had a switch over an enum value, with long case bodies. Today, these cases are hard to extract, because you lose the specific case information:

    switch someEnum {
    case a: handleCaseA(a) // case information is lost in this call, going back to SomeEnum
    ...
    }
    
    func handleCaseA(_ a: SomeEnum) {
        guard case .a(payload) = a else { return } // Need to manually narrow down the type again
        
        print(payload)
        // some long body
    }
    

    If each case got its own type, this would have a very nice solution:

    switch someEnum {
    case a: handleCaseA(a) // case information is lost in this call, going back to SomeEnum
    ...
    }
    
    func handleCaseA(_ a: SomeEnum.A) { // Only a "SomeEnum.a" can make it here
        // Only a `SomeEnum.a` can make it here, no checking needed
        
        print(a.payload)
        // some long body
    }
    

Some loose ends:

  1. What would these subtypes be called?
    • ...and how do we keep those names from colliding with type param names of generic enums?
  2. How do you access unnamed associated values?
    • like a tuple? .0, .1, .2, ...
    • if there's only one, perhaps some standard name, like .value?
2 Likes