Scala : cannot check match for unreachibility even with default value - scala

I'm working on a compiler for a simple language for a course and I ran into this warning. The pattern matching in question is simple so I don't get why this pops up. Here's the pattern matching in question :
(tcExpr(lhs), tcExpr(rhs)) match {
case (TInt, TInt) | (TString, TString) | (TBoolean, TBoolean) | (TIntArray, TIntArray) | (TObject(_), TObject(_)) => TBoolean
case _ => TError
with tcExpr signature being def tcExpr(expr: ExprTree, expected: Type*): Type, ExprTree representing an expression and the Type being case objects. All except TObject() are simple objects, and TObject() takes a symbol representing a class as argument.
What I don't get is that there are not that many possibilities. Why can't the compiler figure it out ? Is there something I have overlooked, or misunderstood ?
Thank you very much for your answers,
Nicolas

I could reproduce "warning: Cannot check match for unreachability." with the following code (I don't know how many types you have. I took 32 + 1 as upper bound):
trait Type
case object T0 extends Type
case object T1 extends Type
case object T2 extends Type
case object T3 extends Type
case object T4 extends Type
...
<generated code, 32 types in total>
...
case object T31 extends Type
def foo: Type = T0
(foo, foo) match {
case (T0,T0) | <total 32 types> | (T31,T31) => println("true")
case _ => println("false")
}
The reason seems to be that it simply runs out of memory trying to check all the possible cases. The advice given by the warning is to add the option -Dscalac.patmat.analysisBudget=off when running the compiler. Just tried to compile with this option: it works, no more warnings.
EDIT: Just found out by bisection that without the option, it stops working with >= 5 types... This is not much memory indeed.
My proposal would be to just remove the eternally long enumeration, and replace it by a short and concise equation:
(foo, foo) match {
case (Obj(_), Obj(_)) => println("true")
case (x, y) if (x == y) => println("true") // Order is important, thx #Nico
case _ => println("false")
}
Then the warning disappears, and the code stops spilling over the 80 characters / line limit.

Related

Scala method type parameterization

I am trying to get a better handle on understanding methods parameterized by types and have this piece of code -
def inferType[T, U](x: T, y: U): Unit = y match {
case _: T => println("T")
case _ => println("Not T")
}
inferType("0", 11) // call 1
inferType(0, 11) // call 2
I would have expected call 1 to have printed "Not T" and call 2 to have printed "T". However, its printing "T" in both cases. Obviously I am missing something here. Can someone help me with why pattern matching is not matching the generic type T here?
The JVM is performing type-erasure, so at runtime all generic types are actually Object, and your match is checking whether y is of type T = Object, which it is.
You can get around this using TypeTag, but this is an anti-pattern in most cases.

When is case syntactically significant?

A/a case, not case case.
Apparently case a matches anything and binds it to the name a, while case A looks for an A variable and matches anything == considers equal to A. This came as quite a surprise to me; while I know Scala is case sensitive, I never expected identifier case to affect the parsing rules.
Is it common for Scala's syntax to care about the case of identifiers, or is there only a small number of contexts in which this happens? If there's only a small number of such contexts, what are they? I couldn't find anything on Google; all I got were results about pattern matching.
There is one more that is similar in nature, called a type pattern. In a type pattern, a simple identifier that starts with a lower case letter is a type variable, and all others are attempt to match actual types (except _).
For example:
val a: Any = List(1, 2, 3)
val c = 1
// z is a type variable
a match { case b: List[z] => a }
// Type match on `Int`
a match { case b: List[Int] => a }
// type match on the singleton c.type (not a simple lower case identifier)
// (doesn't actually compile because c.type will never conform)
a match { case b: List[c.type] => a }
Type matching like the the first example is lesser-known because, well, it's hardly used.

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.

How to use objects as modules/functors in Scala?

I want to use object instances as modules/functors, more or less as shown below:
abstract class Lattice[E] extends Set[E] {
val minimum: E
val maximum: E
def meet(x: E, y: E): E
def join(x: E, y: E): E
def neg(x: E): E
}
class Calculus[E](val lat: Lattice[E]) {
abstract class Expr
case class Var(name: String) extends Expr {...}
case class Val(value: E) extends Expr {...}
case class Neg(e1: Expr) extends Expr {...}
case class Cnj(e1: Expr, e2: Expr) extends Expr {...}
case class Dsj(e1: Expr, e2: Expr) extends Expr {...}
}
So that I can create a different calculus instance for each lattice (the operations I will perform need the information of which are the maximum and minimum values of the lattice). I want to be able to mix expressions of the same calculus but not be allowed to mix expressions of different ones. So far, so good. I can create my calculus instances, but problem is that I can not write functions in other classes that manipulate them.
For example, I am trying to create a parser to read expressions from a file and return them; I also was trying to write an random expression generator to use in my tests with ScalaCheck. Turns out that every time a function generates an Expr object I can't use it outside the function. Even if I create the Calculus instance and pass it as an argument to the function that will in turn generate the Expr objects, the return of the function is not recognized as being of the same type of the objects created outside the function.
Maybe my english is not clear enough, let me try a toy example of what I would like to do (not the real ScalaCheck generator, but close enough).
def genRndExpr[E](c: Calculus[E], level: Int): Calculus[E]#Expr = {
if (level > MAX_LEVEL) {
val select = util.Random.nextInt(2)
select match {
case 0 => genRndVar(c)
case 1 => genRndVal(c)
}
}
else {
val select = util.Random.nextInt(3)
select match {
case 0 => new c.Neg(genRndExpr(c, level+1))
case 1 => new c.Dsj(genRndExpr(c, level+1), genRndExpr(c, level+1))
case 2 => new c.Cnj(genRndExpr(c, level+1), genRndExpr(c, level+1))
}
}
}
Now, if I try to compile the above code I get lots of
error: type mismatch;
found : plg.mvfml.Calculus[E]#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr(c, level+1))
And the same happens if I try to do something like:
val boolCalc = new Calculus(Bool)
val e1: boolCalc.Expr = genRndExpr(boolCalc)
Please note that the generator itself is not of concern, but I will need to do similar things (i.e. create and manipulate calculus instance expressions) a lot on the rest of the system.
Am I doing something wrong?
Is it possible to do what I want to do?
Help on this matter is highly needed and appreciated. Thanks a lot in advance.
After receiving an answer from Apocalisp and trying it.
Thanks a lot for the answer, but there are still some issues. The proposed solution was to change the signature of the function to:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
I changed the signature for all the functions involved: getRndExpr, getRndVal and getRndVar. And I got the same error message everywhere I call these functions and got the following error message:
error: inferred type arguments [Nothing,C] do not conform to method genRndVar's
type parameter bounds [E,C <: plg.mvfml.Calculus[E]]
case 0 => genRndVar(c)
Since the compiler seemed to be unable to figure out the right types I changed all function call to be like below:
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
After this, on the first 2 function calls (genRndVal and genRndVar) there were no compiling error, but on the following 3 calls (recursive calls to genRndExpr), where the return of the function is used to build a new Expr object I got the following error:
error: type mismatch;
found : C#Expr
required: c.Expr
case 0 => new c.Neg(genRndExpr[E,C](c, level+1))
So, again, I'm stuck. Any help will be appreciated.
The problem is that Scala is not able to unify the two types Calculus[E]#Expr and Calculus[E]#Expr.
Those look the same to you though, right? Well, consider that you could have two distinct calculi over some type E, each with their own Expr type. And you would not want to mix expressions of the two.
You need to constrain the types in such a way that the return type is the same Expr type as the Expr inner type of your Calculus argument. What you have to do is this:
def genRndExpr[E, C <: Calculus[E]](c: C, level: Int): C#Expr
If you don't want to derive a specific calculus from Calculus then just move Expr to global scope or refer it through global scope:
class Calculus[E] {
abstract class Expression
final type Expr = Calculus[E]#Expression
... the rest like in your code
}
this question refers to exactly the same problem.
If you do want to make a subtype of Calculus and redefine Expr there (what is unlikely), you have to:
put getRndExpr into the Calculus class or put getRndExpr into a derived trait:
trait CalculusExtensions[E] extends Calculus[E] {
def getRndExpr(level: Int) = ...
...
}
refer this thread for the reason why so.