Suppose I have a type which is either a string or a tuple of strings.
type OneOrTwo = String | (String, String)
Now I want to discriminate between these two types. The runtime representations are distinct (String
vs. Tuple
), so I should be able to do this.
def getFirst(x: OneOrTwo): String =
x match {
// Tuple case
case (a, _) => a
// String case
case y => y
}
Strangely, this doesn't work. The tuple pattern is irrefutable (for tuples), so if that pattern fails, then in the second case y
should be of type String
, based on my understanding. But Scala reports that y
is of type OneOrTwo
and can't be returned from a function expecting a String
.
The strange thing is that Scala seems to understand that this pattern is irrefutable. If we add a : String
annotation
def getFirst(x: OneOrTwo): String =
x match {
// Tuple case
case (a, _) => a
// String case
case y: String => y
}
Then the code compiles successfully and we don't get a non-exhaustiveness warning, since Scala knows that every tuple will match Case 1 and every string will match Case 2.
So why do we need this annotation? Why can't Scala perform that final step of reasoning that "if we don't have a tuple and the type is String | (String, String)
, then it must be a string"?