Krzysztof Wende
Krzysztof Wende

Reputation: 3257

Scala type filtering

I wanted to make an algorithm that filters elements by type

I did something like that

trait Valor
case class A() extends Valor
case class B() extends Valor
case class C() extends Valor

val list : List[Valor] = List(
  A(), A(),
  B(), B(), B(),
  C()
)
def getOfType[T](list: List[_]) = {
  list.filter {
    case _ : T => true
    case _ => false
  }
}
getOfType[A](list)

But the output of it is

List[Any] = List(A(), A(), B(), B(), B(), C())

I'd understand if it wouldn't compile at all. But why does it compile and give such strange result?

Upvotes: 0

Views: 181

Answers (2)

mucaho
mucaho

Reputation: 2169

(If you are interested in possible solution; does not fit in comment)


First, we need some imports

import scala.reflect.ClassTag

Then we can tell the compiler to generate a ClassTag for the type parameter T at compile-time.
Using this ClassTag we can compare the desired class with the class of each element.

def getOfType[T](list: List[_])(implicit tag: ClassTag[T]): List[_] = {
  list.filter { elem => tag.runtimeClass.equals(elem.getClass) }
}

Now, we can go even further and return a List of Ts instead of a List of Any

def getOfType[T](list: List[_])(implicit tag: ClassTag[T]): List[T] = {
  list.collect {
    case elem if tag.runtimeClass.equals(elem.getClass) => elem.asInstanceOf[T]
  }
}

Interestingly, pattern matching on the type magically works after introducing the implicit ClassTag evidence, which lets us simplify things a lot

def getOfType[T : ClassTag](list: List[_]): List[T] = {
  list.collect {
    case elem : T => elem
  }
}

This works for these simple classes A, B & C. However, if you want to filter out polymorphic classes like List[String], you will need to use TypeTags. You can have a look at this example gist which demonstrates polymorphic type filtering.


We could probably add an additional, explicit Class[T] argument to the function instead, which we could use to do the comparison with.

Upvotes: 4

dhg
dhg

Reputation: 52701

It's because the : T is eliminated by erasure (there's a warning) since the generic aspect is lost after compilation, meaning that there's no way for it to check the generic type at runtime. So basically it's not doing a check at all. The parameter is a list of anything (List[_]), and the filter doesn't filter at all, so it's producing a list of anything.

Upvotes: 3

Related Questions