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

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.

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.

Scala - pattern matching traits that have "self type" as type argument gives unchecked warning

I'm trying to define "immutable setter traits", and generic functions for those.
I have a working implementation, but i'm bit disturbed about the "unchecked" warnings from the pattern matching. I'm not really sure what i can do about it.
type Point = (Double, Double)
trait Sizable[A] {
this: A =>
def size: Point
/* immutable object value setter, returns a new of the same object*/
def size(point: Point): A with Sizable[A]
}
def partitionToSizable[T](elements: List[T]):
(List[T], List[T with Sizable[T]]) =
elements.foldLeft((List[T](), List[T with Sizable[T]]()))((p, c) =>
c match {
case a: T with Sizable[T] => (p._1, p._2 ++ List(a))
case a => (p._1 ++ List(a), p._2)
})
The code above demonstrates the problem.
I'm not even sure how big of an issue is that T being unchecked, since all the elements in the list will have a type of T, and the point of the pattern matching is not to determine if it's type is T since we already know that.
In theory Sizable will always have type of T because of the signature of it's enclosing function.
If it's there is no other solution i'd at least like to suppress the warning. #unchecked annotations does not seem to suppress the warning.
If i modify case a: T with Sizable[T] to case a: Sizable[_] it will not compile, since the result type will obviously not confirm to T.
ClassTags or TypeTags might solve the warning, but i suspect they are not necessary really. (also that might have a performance overhead and TypeTags don't work with Scala.js)
I think just case a: Sizable[T #unchecked] => a.size((15,20)) should work.
The main problem here is in partitionToSizable. It's taking a List[T] and then trying to partition it into a List[T] and a List[T with Sizable[T]]. You are taking a list of a single type and then saying that one type T is actually two different types T and T with Sizable[T]. This indicates an issue with your type design. I would recommend solving that first.
One solution might be to recognise that things that are sizable and things that are not sizable should be represented as two different types. Then you can use the following signature:
def partitionToSizable[T1, T2 <: Sizable[T2]](
elements: List[Either[T1, T2]]): (List[T1], List[T2])
Then you don't need to cast; you can pattern match or fold on the Either.

Selector of pattern match being exhaustive

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.

How to account for all cases of an enum on the right-hand side of a pattern match

Exhaustive pattern matching is great, but it only appears to work on the left-hand side of the case (=>) operator.
I am curious if there is a way that a person can verify that the output of a function (or expression) can be bound to that enumeration. The goal would be to have the compiler tell me when I forget to output an item from the enumeration.
In my example, I make use of the following enumeration containing three items:
object MyEnum {
sealed trait t
case object E1 extends t
case object E2 extends t
case object E3 extends t
}
And here is a pattern-match expression that would produce a compile-time warning (as we already know):
def foo( e : MyEnum.t ) : Boolean =
e match {
case MyEnum.E1 => true
case MyEnum.E2 => false
case MyEnum.E3 => true // if we leave this line out, we get a warning
}
Scala would complain if we left MyEnum.E3 out of the pattern matching expression, citing a non-exhaustive pattern match. This is profoundly beneficial, but I wonder if the reverse is possible.
Can we account for all cases of MyEnum.t on the right-hand side of =>?
Here is an example that highlights this:
def bar( s : String ) : Option[MyEnum.t] =
s match {
case "a" => Some(MyEnum.E1)
case "b" => Some(MyEnum.E2)
case "c" => Some(MyEnum.E3) // if we leave this out, no warning
case _ => None
}
In this example, if we leave out the line with MyEnum.E3, then the compiler just carries on as if nothing is wrong. The assertion I would like to make, is:
forall MyEnum.t (aliased as e) there exists Some(e) | None
I understand that this could be easily covered by a run-time test, but I'm curious if there is a way to check this statically.
Thanks.
I am going to push my luck and claim it is not possible (let's keep this a secret from Odersky). If the Scala compiler was able to detect such situations, I think it would be even slower than it already is ;)
The only way I could see is to define foo similarly to how you have done, and define bar by making it iterate over foo to make a reverse map, but that doesn't make much sense to me, and might not work in your specific case.
I think your case is a very good example for when unit tests are useful, so why not just write one?
No, it is not possible, because this is equivalent to the Halting Problem, which was proven unsolvable in 1936.

Scala: Case class unapply vs a manual implementation and type erasure

I'm trying to understand what Scala does with Case Classes that makes them somehow immune to type erasure warnings.
Let's say we have the following, simple class structure. It's basically an Either:
abstract class BlackOrWhite[A, B]
case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]
case class White[A,B]( val right: B ) extends BlackOrWhite[A,B]
And you're trying to use it like this:
object Main extends App {
def echo[A,B] ( input: BlackOrWhite[A,B] ) = input match {
case Black(left) => println( "Black: " + left )
case White(right) => println( "White: " + right )
}
echo( Black[String, Int]( "String!" ) )
echo( White[String, Int]( 1234 ) )
}
Everything compiles and runs without any problems. However, when I try implementing the unapply method myself, the compiler throws a warning. I used the following class structure with the same Main class above:
abstract class BlackOrWhite[A, B]
case class Black[A,B]( val left: A ) extends BlackOrWhite[A,B]
object White {
def apply[A,B]( right: B ): White[A,B] = new White[A,B](right)
def unapply[B]( value: White[_,B] ): Option[B] = Some( value.right )
}
class White[A,B]( val right: B ) extends BlackOrWhite[A,B]
Compiling that with the -unchecked flag issues the following warning:
[info] Compiling 1 Scala source to target/scala-2.9.1.final/classes...
[warn] src/main/scala/Test.scala:41: non variable type-argument B in type pattern main.scala.White[_, B] is unchecked since it is eliminated by erasure
[warn] case White(right) => println( "White: " + right )
[warn] ^
[warn] one warning found
[info] Running main.scala.Main
Now, I understand type erasure and I've tried to get around the warning with Manifests (to no avail so far), but what is the difference between the two implementations? Are case classes doing something that I need to add in? Can this be circumvented with Manifests?
I even tried running the case class implementation through the scala compiler with the -Xprint:typer flag turned on, but the unapply method looks pretty much like I expected:
case <synthetic> def unapply[A >: Nothing <: Any, B >: Nothing <: Any](x$0: $iw.$iw.White[A,B]): Option[B] = if (x$0.==(null))
scala.this.None
else
scala.Some.apply[B](x$0.right);
I cannot give a complete answer, but I can tell you that even though the compiler generates an unapply method for case classes, when it pattern matches on a case class it does not use that unapply method. If you try -Ybrowse:typer using both builtin case matching and your unapply method, you will see a very different syntax tree is produced (for the match) depending on which is used. You can also browse the later phases and see that the difference remains.
Why Scala does not use the builtin unapply I am not sure, though it may be for the reason you bring up. And how to get around it for your own unapply I have no idea. But this is the reason Scala seems to magically avoid the problem.
After experimenting, apparently this version of unapply works, though I'm a bit confused about why:
def unapply[A,B](value: BlackOrWhite[A,B]): Option[B] = value match {
case w: White[_,_] => Some(w.right)
case _ => None
}
The difficulty with your unapply is that somehow the compiler has to be convinced that if a White[A,B] extends a BlackOrWhite[C,D] then B is the same as D, which apparently the compiler is able to figure out in this version but not in yours. Not sure why.
I can't give you the answer on the difference between case class match and unapply. However in their book (Odersky, Spoon, Venners) "Programming in Scala" 2nd chptr 26.6 "Extractors versus case classes" they write:
"they (case classes) usually lead to more efficient pattern matches
than extractors, because the Scala compiler can optimize patterns over
case classes much better than patterns over extractors. This is
because the mechanisms of case classes are fixed, whereas an unapply
or unapplySeq method in an extractor could do almost anything. Third,
if your case classes inherit from a sealed base class, the Scala
compiler will check our pattern matches for exhaustiveness and will
complain if some combination of possible values is not covered by a
pattern. No such exhaustiveness checks are available for extractors."
Which says to me that the two are more different than one would expect at first glance, however without being specific on what the exact differences are.