Upon reading the unknown patterns section, I believe the reasons given there are misleading.
Let's start with the running example that originally uses the unknown
case syntax:
switch excuse {
case .eatenByPet
// …
unknown: // warning: not all known enum cases have been matched
// …
}
Now let's assume we introduced a ._
pattern having the following semantics:
- it successfully matches any case of a non-frozen enum;
- there will be a warning if it can match a statically known case of that enum.
First, rewrite the aforementioned example to become familiar with the new pattern matching syntax:
switch excuse {
case .eatenByPet
// …
case ._: // warning: not all known enum cases have been matched
// …
}
Then rewrite the example given in the unknown patterns section of SE-0192, but make it shorter for clarity:
switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true):
// …
case (._, true): // warning: not all known enum cases have been matched
// …
default:
// …
}
As defined by the rule (2), the warning should be emitted because there exists a statically known enum case that the ._
pattern can match.
Adding more cases is tedious, because the cases in switch are matched one after another:
switch (excuse, notifiedTeacherBeforeDeadline) {
case (.eatenByPet, true): // 1
// …
case (._, true): // 2 - warning: not all known enum cases have been matched
// …
case (.thoughtItWasDueNextWeek, _): // 3
// …
case (_, false): // 4
// …
}
In particular, the input (.thoughtItWasDueNextWeek, true)
would result in case 2 being chosen rather than case 3. However this behavior is not as surprising as presumed in SE-0192, because ._
pattern is almost the same as _
pattern except for a warning the former pattern can possibly produce.
Does this proposal fit well with the feel and direction of Swift?
No, unknown
patterns would fit with them much better, or it was not demonstrated that such patterns don't fit.
Another indicator of potential problems is that SE-0192 'only affects C enums and enums in the standard library and overlays', but not user-defined Swift enums. So what if I use a third-party framework written in pure Swift and the authors of that framework are going to add more cases to their enums?
For that, I imagine having a case _
syntax for defining non-frozen enum in pure Swift:
enum Excuse {
case eatenByPet
case thoughtItWasDueNextWeek
case _
}
The final case _
is not a real enum case, but it serves as an indicator that more cases can be added to that enum in the future. Such a syntax would perfectly complement ._
pattern matching, I suppose.
One more question is how to pass an unknown case value to an third-party API that expects a value of a non-frozen enum type. An unknown case value could be either an invalid value, or a "private case" value, or a case value defined in the future version of that API.
Obviously, the initializer of a non-frozen enum should be non-failable, and it should preserve the rawValue
passed to it.
In addition, it could be possible to define an unknown value as an associated value of a non-frozen enum type, so that anyone can inspect it with pattern matching.
That said, a enum with a "private case" can be fully defined in pure Swift:
enum PaperSize: Int { // a enum with the raw type
// init(rawValue:) is non-failable for non-frozen enums
case usLetter = 0
case a4 = 1
case photo4x6 = 2
case _(Int) // an associated value of a non-frozen enum
}
extension PaperSize {
// a "private case" of a non-optional type
static let stickyNote: PaperSize = PaperSize(rawValue: 255)
}
struct Example {
var paperSize: PaperSize {
get {
return .stickyNote
}
set {
switch newValue {
case .usLetter:
print("usLetter")
case .a4:
print("a4")
case ._(_): // warning: not all known enum cases have been matched
print("an unknown case with an associated value")
}
}
}
}