Erasure elimination in scala : non-variable type argument is unchecked since it is eliminated by erasure
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
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 TypeTag
s 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)
val v = 1 ::"abc" :: true :: Nil
v : List[Any] = List(1,abc,true)
type parameter of List
type has been unified to the greatest common super type of the elements in the List
which is Any
.
Shapeless is to the rescue.
import shapeless._
import HList._
val s = 1 :: "abc" :: true: HNil
s : shapeless.::[Int,shapeless.::[String,shapelsss.::[Boolean,shapeless.HNil]]]
= 1 :: abc :: true :: HNil
With Shapeless HList
you can get compile time safety for a heterogeneous list. you can now filter
in a typesafe manner. e.g.
s.filter[String]