Let the following setup:
sealed trait Test[@specialized T] {
implicit protected def tag: ClassTag[T]
def xs: Array[T]
def count(condition: T => Boolean): Int = {
@tailrec
def innerLoop(index: Int, acc: Int): Int =
if (index == xs.length) acc
else {
val c: Int = if (condition(xs(index))) 1 else 0
innerLoop(index + 1, acc + c)
}
innerLoop(0, 0)
}
def freq(elem: T): Int = count(_ == elem)
}
object Test {
def apply[@specialized T: ClassTag](xs: Array[T]): Test[T] = if(xs.length % 2 == 0) TestA(xs) else TestB(xs)
final case class TestA[@specialized T: ClassTag](xs: Array[T]) extends Test[T] {
protected def tag: ClassTag[T] = classTag[T]
}
final case class TestB[@specialized T: ClassTag](xs: Array[T]) extends Test[T] {
protected def tag: ClassTag[T] = classTag[T]
}
}
In this case, both count
and freq
methods can be computed in a generic way, though other methods might exist that are specific to TestA
or TestB
.
When I benchmark the count
method, then there's no performance increase and my feeling is, that somewhere specialized
annotation is lost. The same happens with freq
too. When the count
method is defined in the case classes, then it works as expected and boxing-unboxing is avoided. But this approach will introduce a lot of code duplication.
Even when count
is abstract and freq
is defined in the trait
sealed trait Test[@specialized T] {
implicit protected def tag: ClassTag[T]
def xs: Array[T]
def count(condition: T => Boolean): Int
def freq(elem: T): Int = count(_ == elem)
}
and count
is overridden in the case classes, the freq
method won't be specialized either.
What am I missing? Is there a way to define some methods already in the trait and get them specialized?