Erasure elimination in scala : non-variable type argument is unchecked since it is eliminated by erasure

BSJ picture BSJ · Aug 9, 2014 · Viewed 25.2k times · Source

I've got a sequence Seq[Any] that has a variety of objects in it (like String, Integer, List[String], etc). I'm trying to sift through the list and break it up into separate lists partitioned based on the class type. The following is a pattern I'm using in the code:

val allApis = mySequence.filter(_.isInstanceOf[String])

This works well and doesn't generate any warnings. However, when I try to do the same for filtering out the objects that are Lists of strings:

val allApis = mySequence.filter(_.isInstanceOf[List[String]])

I get a warning that says non-variable type argument String in type List[String] is unchecked since it is eliminated by erasure. Now, the technique actually works and I'm able to comfortably filter the sequence as desired, but I'm wondering what is the appropriate way to deal with the warning in an idiomatic way so that I know I don't have a serious bug lurking in the background waiting to blow up

Answer

Rex Kerr picture Rex Kerr · Aug 9, 2014

It doesn't work because it will pick out List[Double] or any other list in addition to List[String]. There are a variety of ways of fixing the problem, including wrapping any parameterized types in a non-parameterized case class:

case class StringList(value: List[String])

and then you can just

mySequence.collect{ case StringList(xs) => xs }

to pull out the lists of strings (with the correct type, and type-safely also).

Alternatively, if you want to not wrap objects and want to be sure that they're of the correct type, you can check every element:

mySequence.filter( _ match {
  case xs: List[_] => xs.forall( _ match { case _: String => true; case _ => false })
  case _ => false
})

though even this won't let you know which type empty lists were supposed to be.

Another possibility is to glue TypeTags to everything in your list; this will prevent you needing to manually wrap things. For instance:

import scala.reflect.runtime.universe.{TypeTag, typeTag}
def add[A](xs: List[(Any, TypeTag[_])], a: A)(implicit tt: TypeTag[A]) = (a, tt) :: xs
val mySequence = add(add(add(Nil, List(42)), true), List("fish"))
mySequence.filter(_._2.tpe weak_<:< typeTag[List[String]].tpe)