Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Warning about an unchecked type argument in this Scala pattern match?

This file:

object Test extends App {
    val obj = List(1,2,3) : Object
    val res = obj match {
       case Seq(1,2,3) => "first"
       case _ => "other"
    }
    println(res)
}

Gives this warning:

Test.scala:6: warning: non variable type-argument A in type pattern Seq[A]  
is unchecked since it is eliminated by erasure
   case Seq(1,2,3) => "first"

Scala version 2.9.0.1.

I don't see how an erased type parameter is needed to perform the match. That first case clause is meant to ask if obj is a Seq with 3 elements equal to 1, 2, and 3.

I would understand this warning if I had written something like:

case strings : Seq[String] => ...

Why do I get the warning, and what is a good way to make it go away?

By the way, I do want to match against something with static type of Object. In the real code I'm parsing something like a Lisp datum - it might be an String, sequence of datums, Symbol, Number, etc.

like image 825
Rob N Avatar asked Sep 02 '11 01:09

Rob N


1 Answers

Here is some insight to what happens behind the scene. Consider this code:

class Test {
  new Object match { case x: Seq[Int] => true }
  new Object match { case Seq(1) => true }
}

If you compile with scalac -Xprint:12 -unchecked, you'll see the code just before the erasure phase (id 13). For the first type pattern, you will see something like:

<synthetic> val temp1: java.lang.Object = new java.lang.Object();
if (temp1.isInstanceOf[Seq[Int]]()) 

For the Seq extractor pattern, you will see something like:

<synthetic> val temp3: java.lang.Object = new java.lang.Object();
if (temp3.isInstanceOf[Seq[A]]()) {
  <synthetic> val temp4: Seq[A] = temp3.asInstanceOf[Seq[A]]();
  <synthetic> val temp5: Some[Seq[A]] = collection.this.Seq.unapplySeq[A](temp4);
  // ...
}

In both cases, there is a type test to see if the object is of type Seq (Seq[Int] and Seq[A]). Type parameters will be eliminated during the erasure phase. Thus the warning. Even though the second may be unexpected, it does make sense to check the type since if object is not of type Seq that clause won't match and the JVM can proceed to the next clause. If the type does match, then the object can be casted to Seq and unapplySeq can be called.


RE: thoredge comment on the type check. May be we are talking about different things. I was merely saying that:

(o: Object) match {
  case Seq(i) => println("seq " + i)
  case Array(i) => println("array " + i)
}

translates to something like:

if (o.isInstanceOf[Seq[_]]) { // type check
  val temp1 = o.asInstanceOf[Seq[_]] // cast
  // verify that temp1 is of length 1 and println("seq " + temp1(0))
} else if (o.isInstanceOf[Array[_]]) { // type check
  val temp1 = o.asInstanceOf[Array[_]] // cast
  // verify that temp1 is of length 1 and println("array " + temp1(0))
}

The type check is used so that when the cast is done there is no class cast exception.

Whether the warning non variable type-argument A in type pattern Seq[A] is unchecked since it is eliminated by erasure is justified and whether there would be cases where there could be class cast exception even with the type check, I don't know.

Edit: here is an example:

object SeqSumIs10 {
  def unapply(seq: Seq[Int]) = if (seq.sum == 10) Some(seq) else None
}

(Seq("a"): Object) match {
  case SeqSumIs10(seq) => println("seq.sum is 10 " + seq) 
}
// ClassCastException: java.lang.String cannot be cast to java.lang.Integer
like image 189
huynhjl Avatar answered Nov 15 '22 22:11

huynhjl