Selector of pattern match being exhaustive - scala

Looking at the Scala doc for sealed classes, it says:
If the selector of a pattern match is an instance of a sealed class, the compilation of pattern matching can emit warnings which diagnose that a given set of patterns is not exhaustive, i.e. that there is a possibility of a MatchError being raised at run-time.
I don't quite understand what they meant in this paragraph. My understanding is that if a switch case, doesn't cover all the possibilities, then we'll get a warning at compile time, saying we might get an error at run time. Is this correct?
I find it strange, because how can we cover ALL the scenarios in a switch case? We would have to match all possible strings, which is just silly, so I take it my understanding is incorrect. Someone care to elucidate, please?

What the paragraph is saying is that in-case you have a fixed hierarchy structure like this:
sealed trait Foo
class Bar extends Foo
class Baz extends Foo
class Zab extends Foo
Then when you pattern match on it, the compiler can infer if you've attempted to match on all possible types extending the sealed trait, in this example:
def f(foo: Foo) = foo match {
| case _: Bar => println("bar")
| case _: Baz => println("baz")
| }
<console>:13: warning: match may not be exhaustive.
It would fail on the following input: Zab()
def f(foo: Foo) = foo match {
^
f: (foo: Foo)Unit
Note the beginning of the document says:
A sealed class may not be directly inherited, except if the inheriting
template is defined in the same source file as the inherited class.
This is unique to Scala, and can't be done in Java. A final class in Java cannot be inherited even if declared inside the same file. This is why this logic won't work for String in Scala, which is an alias for java.lang.String. The compiler warning may only be emitted for Scala types that match the above criteria.

I find it strange, because how can we cover ALL the scenarios in a switch case? We would have to match all possible strings
Yes, if the selector has type String (except it isn't a sealed class, because that's a Scala concept and String is a Java class).
which is just silly
No. For strings, you just need a catch-all case, e.g.
val x: String = ...
x match {
case "a" => ...
case "b" => ...
case _ => ...
}
is an exhaustive match: whatever x is, it matches one of the cases. More usefully, you can have:
val x: Option[A] = ...
x match {
case Some(y) => ...
case None => ...
}
and the compiler will be aware the match is exhaustive even without a catch-all case.

The wildcard character allows us to cover all the scenarios.
something match {
case one => ...
case two => ...
case _ => ...
}
It is selected whenever all other cases don't match; that is, it is the default case. Further information here.

Related

Match may not be exhaustive warning is incorrect

So the scala compiler is complaining that a pattern match might not be exhaustive for the method foo and I wonder why. This is the code:
abstract class Foo {
def foo(that: Foo): Unit = (this, that) match {
case (Foo_1(), Foo_1()) => //case 1
case (Foo_1(), Foo_2()) => //case 2
case (Foo_2(), Foo_1()) => //case 3
case (Foo_2(), Foo_2()) => //case 4
// Compiler warning
}
def fooThis(): Unit = this match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
def fooThat(that: Foo): Unit = that match {
case Foo_1() => //do something
case Foo_2() => //do something
// Works fine
}
}
case class Foo_1() extends Foo
case class Foo_2() extends Foo
And this is the error:
Warning:(5, 32) match may not be exhaustive.
It would fail on the following inputs: (Foo(), _), (Foo_1(), _), (Foo_2(), _), (_, Foo()), (_, Foo_1()), (_, Foo_2()), (_, _)
def foo(that: Foo): Unit = (this, that) match {
Since this and that are of type Foo, and Foo can only be of type Foo_1 or Foo_2, the cases in foo are all possible combinations.
I added fooThis and fooThat for sake of completeness and to show that matching Foo_1 and Foo_2 suffices. The compiler message suggests that there are other types that can be matched (i.e. Foo and _).
So why is this warning shown?
Related:
Scala bug: fixed in 2.12.0-M4. My scala welcome message:
Welcome to Scala 2.12.1 (Java HotSpot(TM) Client VM, Java 1.8.0_131).
Scala bug: false match not exhaustive warning on (unsealed, sealed) tuple
EDIT
The compiler seems to complain as soon as you use tuples. If we add a dummy variable to fooThis as follows
def fooThis(): Unit = (this, Foo_1()) match {
case (Foo_1(),_) => //do something
case (Foo_2(),_) => //do something
}
we get the following compiler warning
Warning:(13, 27) match may not be exhaustive.
It would fail on the following input: (_, _)
def fooThis(): Unit = (this, Foo_1()) match {
The Scala compiler won't give exhaustive match warnings for non-sealed traits (like your Foo). This explains why fooThis and fooThat compile without warnings.
If you want warnings here (and you should, because they're better than MatchError exceptions at runtime) you have a couple of options:
Make Foo sealed. This creates an ADT, which is safe to pattern match against in the sense that you'll get exhaustivity warnings when you forget a case. Option is an ADT that you're probably familiar with from the standard library. Here, you've already got cases for both Foo_1 and Foo_2, so you won't get an exhaustivity warning. But if you ever forget either case, you will. You probably want to make Foo_1 and Foo_2 final while you're at it.
Leave Foo unsealed, use Typelevel Scala and enable its -Xlint:strict-unsealed-patmat warnings.
On the other hand, the Scala compiler will give exhaustive match warnings for final case classes like Tuple2, which is what you're matching against in your foo method.
To answer "why is the warning shown?", consider what happens if we do this:
case class Foo3() extends Foo
val foo3 = Foo3()
foo3.foo(foo3)
(Answer: it throws a MatchError at runtime.)
The warning is the Scala compiler's way of helping you avoid the exception at runtime. If you want to make the warning go away, you could:
Make Foo sealed (again, creating an ADT), preventing Foo3 from sneaking in elsewhere.
Add a wildcard case _ => ....
Make the match unchecked: ((this, that): #unchecked) match { ....
Don't do number 3, because it leaves you vulnerable to MatchErrors at runtime when someone introduces Foo3.
So, perhaps the question isn't really "why does the match in foo generate a warning", but "why doesn't the match in fooThis and fooThat generate a warning".
It seems making the abstract class sealed at least makes the compiler warning go away:
sealed abstract class Foo {
Though I'm not too sure why. It might be related to: https://issues.scala-lang.org/browse/SI-9351
Figuring out all subclasses of a class is called Class Hierarchy Analysis, and doing static CHA in a language with dynamic code loading is equivalent to solving the Halting Problem.
Plus, one of the goals of Scala is separate compilation and deployment of independent modules, so the compiler simply cannot know whether or not a class is subclassed in another module, because it never looks at more than one module. (After all, you could compile a module against the interface of some other module without that module even existing on your system!) That's why sealed requires all subclasses to be defined in the same compilation unit.
That's why the compiler doesn't show warnings, because its aware of the existing subclasses.

Why scala compiler sometimes give warning on "pattern matching", sometimes not?

Define a list first:
val list = List(1,2,3)
Scala compiler gives warning (even if it can match):
list match {
case head :: tail => println(s"h:${head} ~ t: ${tail}")
}
Scala compiler won't give warning (even if it can't match):
list match {
case List(a,b) => println("!!!")
}
I can't understand the second one
The "match may not be exhaustive" warning is only given when pattern matching on a type that is a sealed class and you have cases for only a subset of the subclasses or objects. List is a sealed class with a subclass :: and a subobject Nil, something like:
sealed abstract class List[+T]
class ::[+T] extends List[+T]
object Nil extends List[Nothing]
If you have a match and don't have a case for :: and one for Nil, and also don't have a case that might match any List, Scala knows the match isn't exhaustive and will report it. A case _ would match anything and will prevent the warning. But List(a, b) also prevents the warning, because Scala doesn't know if it only matches some of the subclasses.
When you use List as an extractor, as in List(a, b), you are using the extractor List.unapplySeq to take apart the matched value. Scala doesn't try to make assumptions about how the extractor behaves, and therefore doesn't know that the match isn't exhaustive. Without knowing the implementation details of List.unapplySeq, there is no way to know it won't happily match everything and return the required two values.

Zero-arg pattern matches when one arg expected

Given this definition in Scala:
class Foo(val n: Int)
object Foo {
def unapply(foo: Foo): Option[Int] = Some(foo.n)
}
This expression compiles and returns ok:
new Foo(1) match {
case Foo() => "ok"
}
Why does this even compile? I would expect that an extractor with Option[T] implies matching patterns with exactly one argument only.
What does the pattern Foo() mean here? Is it equivalent to Foo(_)?
In other words, what is the language rule that enables the experienced behavior.
Today (in 2.11 milestone) you get the error:
<console>:15: error: wrong number of patterns for object Foo offering Int: expected 1, found 0
case Foo() => "ok"
^
I encountered this when adding Regex.unapply(c: Char). At some point, the case you point out was accepted, then later rejected. I remember I liked the idea that if my extractor returns Some(thing), then the Boolean match case r() would work the same as case r(_).
What works is in the scaladoc of unapply(Char) :
http://www.scala-lang.org/files/archive/nightly/docs-master/library/#scala.util.matching.Regex
Section 8.18 of the Scala Language Reference discusses this type of pattern matching. According to the reference, for a pattern like Foo(), it should only match if unapply returns a boolean. If unapply returns Option[T] for some T that isn't a tuple, then the pattern must include exactly one parameter, e.g. Foo(_). Unless I'm really misunderstanding what is happening here, it looks like this is an edge case where the compiler violates the spec.

create type from pattern matching in scala

I'm new using Scala and what I need is to create like a dynamic type based on some pattern matching function, like
type defType = "value1" match {
case "value0" => typeOf[String]
case "value1" => typeOf[Integer]
case _ => typeOf[Double]
}
val test5 : defType = 4
This is just an example given that I'd to work with more complex structures, but gives the idea of what I want to do.
I probably don't understand what you're doing, but this seems problematic to me:
val test5 : defType = 4
The compiler needs to be able to know defType at compile time, otherwise it can't type check. Based on that, I think what you want to do is do this pattern matching at compile time by utilizing Scala macros.
However, I have a feeling that's not the right solution either just because this whole scenario seems very strange. If you give more detail about what the bigger picture is of what you're trying to do maybe we can suggest a better solution.
I fear this is not possible, for fundamental conceptual reasons. As type rules are checked at compile time, it is not possible to derive types based on values that might be unknown until runtime.
That being said, you might be able to tackle your design problem by defining a family of case classes, each being a wrapper for a value of a specific type. By giving these classes a common base class, you can store a value of any type you wish into the same variable and extract it through type-safe pattern matching:
class Base
case class AString(val value: String) extends Base
case class AnInt(val value: Int) extends Base
case class ADouble(val value: Double) extends Base
val a : Base = "value1" match {
case "value0" => AString(…)
case "value1" => AnInt(…)
case "value0" => ADouble(…)
}
a match {
case AString(s) => …
case AnInt(i) => …
case ADouble(i) => …
…
}

How to pattern match multiple values in Scala?

Let's say I want to handle multiple return values from a remote service using the same code. I don't know how to express this in Scala:
code match {
case "1" => // Whatever
case "2" => // Same whatever
case "3" => // Ah, something different
}
I know I can use Extract Method and call that, but there's still repetition in the call. If I were using Ruby, I'd write it like this:
case code
when "1", "2"
# Whatever
when "3"
# Ah, something different
end
Note that I simplified the example, thus I don't want to pattern match on regular expressions or some such. The match values are actually complex values.
You can do:
code match {
case "1" | "2" => // whatever
case "3" =>
}
Note that you cannot bind parts of the pattern to names - you can't do this currently:
code match {
case Left(x) | Right(x) =>
case null =>
}
The other answer correctly says that currently there is no way to pattern-match multiple alternatives while extracting values at the same time.
I'd like to share a coding pattern with you that comes close to doing this.
Scala allows you to pattern-match alternatives without extracting values, e.g. case Dog(_, _) | Cat(_, _) => ... is legal. Using this, you can simply extract the values yourself within the case block.
Here's a somewhat contrived example:
abstract class Animal
case class Dog(age: Int, barkLevel: Int) extends Animal
case class Cat(apparentAge: Int, cutenessLevel: Int) extends Animal
val pet: Animal = Dog(42, 100)
// Assume foo needs to treat the age of dogs and the apparent age
// of cats the same way.
// Same holds for bark and cuteness level.
def foo(pet: Animal): Unit = pet match {
case animal#(Dog(_, _) | Cat(_, _)) =>
// #unchecked suppresses the Scala warning about possibly
// non-exhaustiveness even though this match is exhaustive
val (agelike, level) = (animal: #unchecked) match {
case Dog(age, barkLevel) => (age, barkLevel)
case Cat(apparentAge, cutenessLevel) => (apparentAge, cutenessLevel)
}
???
}
Assume that ??? actually stands for doing something that is equal for dogs and cats. Without this coding pattern, you would need to have two cases, one for dogs and one for cats, forcing you to duplicate code or at least to outsorce code into a function.
Generally, the coding pattern above is suitable if you have sibling case classes that share fields that behave identically only for some algorithms. In those cases, you cannot extract those fields to a common superclass. Still, you would like to pattern-match in a uniform way on those fields in the algorithms that treat them equally. This you can do as shown above.