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

ScalaType Erasure

Scala Problem Overview


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

Scala Solutions


Solution 1 - Scala

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)

Solution 2 - Scala

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]

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBSJView Question on Stackoverflow
Solution 1 - ScalaRex KerrView Answer on Stackoverflow
Solution 2 - ScalaVikas PandyaView Answer on Stackoverflow