I just had a small issue with a switch statement where I for some reason assumed that the where
clause was already applied to every associated value of a merged switch case.
enum Permission {
case notSupported(UInt8)
case readOnly(UInt8)
case readWrite
}
let permission = Permission.notSupported(0)
switch permission {
case .notSupported(let code),
.readOnly(let code) where code > 0:
/* create the right error value */
default:
/* create the default error value */
}
The above example actually will succeed with in the first case instead of landing in the default
branch, because the where
clause is only applied to the second readOnly
case.
There is also a diagnostics bug (SR-8567) in the above switch which prevents from triggering an expected warning that might have helped me finding the issue in that pattern. If we restructure the switch statement then the right diagnostics is triggered and the compiler emits a warning:
case .notSupported(let code), .readOnly(let code) where code > 0: // 'where' only applies to the second pattern match in this case
In my use case the solution was to remove the where
clause entirely and add a guard code > 0 else { break }
below the case since default
case was also breaking out the current control flow.
The issue I'm trying to describe next is trying to avoid the same situation and reduce some of the boilerplate code wile providing a more flexible way of expressing a global condition.
In general you may want to merge multiple cases in a switch statement and match them from left to right, top to bottom. You may also wish to extract an associated value from any merged case. The only constraint that is required from the developer to be applied, is that the associated value has to have the same instance name and be of the same type. If you also need to apply a where
clause to the extracted value in order to return the control flow to the switch so it matches the next case you have to replicate the same where
clause over and over again:
switch something {
case .a(let value) where checkCondition(of: value),
.b(let value) where checkCondition(of: value),
.c(let value) where checkCondition(of: value):
...
case .otherCase:
...
default:
....
}
To reduce the repetition of the where
clause and allow to return the control flow to the switch statement so it can match next case, I propose a new way we can express the where
clause in switch statements that is automatically applied to every associated value in the current branch.
switch something {
case where checkCondition(of: value) in .a(let value), .b(let value), .c(let value):
...
case .otherCase:
...
default:
...
}
If this idea reaches some traction and positive feedback, I'd happily write a draft proposal and see if I can find someone willing to implement that feature.
Thoughts?
Edit: The above solution introduces a syntax by reusing the where
clause in an unambiguous way which leads us to the
case where condition in cases
syntax. However I think we can also go a step further and eliminate the where
clause entirely. Then we'd get
case condition in cases
which is quite similar to
for value in sequence /* where condition */