Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is type inference not working in this switch statement in Swift 3?

Tags:

swift

UPDATE This is fixed in Swift 3.1

In migrating an if-else to a switch statement, I noticed that type inference wasn't working. Why do I need to specify HKQuantityTypeIdentifier in each case when quantityTypeIdentifier is already of that type?

func process(samples: [HKSample]?, quantityTypeIdentifier: HKQuantityTypeIdentifier) {
    DispatchQueue.main.async { [weak self] in            
        if let quantitySamples = samples as? [HKQuantitySample] {
            for sample in quantitySamples {
                switch quantityTypeIdentifier {
                case HKQuantityTypeIdentifier.distanceWalkingRunning:
                    // code

                case HKQuantityTypeIdentifier.activeEnergyBurned:
                    // code

                case HKQuantityTypeIdentifier.heartRate:
                    // code

                default:
                    fatalError("Quantity Type Identifier not implemented \(quantityTypeIdentifier)")
                }
            }
        }
    }
}

I am able to call the function like:

process(samples: samples, quantityTypeIdentifier: .distanceWalkingRunning)
like image 737
jjatie Avatar asked Oct 18 '22 22:10

jjatie


1 Answers

I think you've found a bug, or at least you have a reasonable case to claim one. The inconsistency is nicely shown by a much shorter example:

let c : UIColor = .red
switch c {
case .red : print ("red") // error
default : break
}

That won't compile. You can say .red on the first line but not on the third line. That seems a clear inconsistency.

Now, having said that, I can certainly explain why the rules are different in the two different places. A case expression is resolved according to the ~= operator and the rules of forming a pattern. Those rules are different from anything else in Swift (hence, for example, there are situations where you say as in a case pattern but would say as? everywhere else). So evidently those are the rules that would need tweaking in order for this to work. They have been tweaked so far as to allow bare enum cases but not bare enum-like struct "cases" (that is, static members of structs that are RawRepresentable where those static members evaluate to an instance of the struct itself).

Finally, here's a skanky workaround that I like to use when case patterns become too onerous:

let c : UIColor = .red
switch true {
case c == .red : print ("red") // heh heh
default : break
}

By switching on true and writing out the entire boolean condition we break the bounds of pattern-matching and reenter the world of normal expressions.

like image 160
matt Avatar answered Oct 21 '22 04:10

matt