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 separa
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]
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)