Reputation: 1124
My code uses Scala and the Breeze linear algebra library. I have objects of types DenseVector[Double]
, DenseVector[Int]
, etc... where DenseVector
is an array-like container with dedicated methods for numerical computations. I sometimes need to use pattern matching against the contained type. Type-erasure forced me to introduce a trait and "wrapping" case classes:
sealed trait DenseVectorRoot
case class DenseVectorReal(val data: DenseVector[Real]) extends DenseVectorRoot
case class DenseVectorMatrixReal(val data: DenseVector[DenseMatrix[Real]]) extends DenseVectorRoot
(Where Real
is just an alias for Double
).
Pattern matching would look like:
def print(data: DenseVectorRoot) =
data match {
case DenseVectorMatrixReal(_) => println("Contains real matrices")
case DenseVectorReal(_) => println("Contains real scalars")
}
I would like to get rid of the DenseVectorRoot
trait. I attempted this:
def print2(data: DenseVector[_ <: Any]) =
data match {
case _: DenseVector[Double] => println("Contains real matrices")
case _: DenseVector[Int] => println("Contains real scalars")
}
But the type arguments get erased.
How should I modify print2
using ClassTags so that pattern matching works ? For example by printing the correct output in the following code:
val v0 = DenseVector(1.2, 1.5, 1.6)
val v1 = DenseVector(3, 4, 5)
val a = Array(v0, v1)
a.map(print2)
EDIT
The main reason why I need to manage an Array
with varying containers is that my code need to manage various types of data (for example, parsing input would be different for a DenseVector[Real]
and for a DenseVector[Matrix[Real]]
). My current design is to store everything in an Array[DenseVectorRoot]
, and then process the data using high-order functions like .map()
. Each of this function will, on an element-to-element basis, pattern-match to know if the data is a DenseVectorReal
or a DenseVectorMatrixReal
, and act accordingly.
That might not be the optimal design to solve my problem, but I do not know at compile time what types of data are provided by a user. I would be happy to know any better design !
Upvotes: 1
Views: 259
Reputation: 28676
TypeTag
You can request that the compiler infers the parameter type, and generates a TypeTag
for you.
You can then use the TypeTag
to check for a certain type, or you can print
it for debugging purposes.
import scala.reflect.runtime.universe._
def printType[A: TypeTag](a: List[A]): Unit =
println(if(typeTag[A] == typeTag[Double]) "Double" else "other")
printType(List(1.0))
printType(List(1))
>Double
>other
Upvotes: 1
Reputation: 40508
This type of thing is better done with type classes:
trait DenseContent[T] {
def compute(v: DenseVector[T]): String
}
object DenseContent {
implicit object _Real extends DenseContent[Real] {
def compute(v: DenseVector[Real]) = "real"
}
implicit object _Int extends DenseContent[Int] {
def compute(v: DenseVector[Int]) = "int"
}
// etc ...
}
def print2[T : DenseContent](data: DenseVector[T]) = println(
implicitly[DenseContent[T]].compute(data)
)
Upvotes: 1