General Recommendations
TypeTag
s are generated at compile time (which may have significant compile time overhead due to macro expansion at each use-site) and employed at runtime, also generating some potential runtime overhead, depending on the exact nature of their use. So even in Scala 2, they should probably only be used a last resort (and we hope to address all such issues here, so that a last resort isn't necessary). By comparison, things that use instanceOf
, directly or indirectly, are extremely fast.
Use Java reflection
instanceOf
is super fast. classOf
(i.e. Java's getClass
) is nearly as fast.
Use ClassTag
Referential equality on ClassTag
s should also be very fast.
Use a wrapped type as an instantiation of a type class
When possible, you may want to consider wrapping your type in a class, to give it a concrete "Java" type. While there will often be overhead, you can possibly use value classes.
Type classes on the wrapped class are then often a good way to expose functionality. As an aside, as @Blaisorblade pointed out, " type tags just a lawless type class (with methods typeName: String
and tpe: Type
) + instance materialization". Which brings us to the next option:
Uses macros if needed
(currently not supported in Dotty, but planned)
Although perhaps a little harder to get used to, the end result should be cleaner syntax in the code employing the macro than if you were using a TypeTag
. Also, macros have uses far beyond TypeTag
.
Selected Examples
Matching on a collection's type parameter
Example
A typical use case for TypeTag
, taken from the popular post Scala: What is a TypeTag and how do I use it? is to perform ad-hoc polymorphism on a collection type:
import scala.reflect.runtime.universe._
def meth[A : TypeTag](xs: List[A]) = typeOf[A] match {
case t if t =:= typeOf[String] => "list of strings"
case t if t <:< typeOf[Foo] => "list of foos"
}
scala> meth(List("string"))
res67: String = list of strings
scala> meth(List(new Bar))
res68: String = list of foos
Unlike ClassTag
, TypeTag
is runtime reflection. Maybe I'm using it wrong here, though the behavior is quite surprising. At least in a REPL, I don't receive any warnings with the below:
def meth[A : ClassTag](xs: List[A]) = xs match {
case xs: List[String] => "list of strings"
case xs: List[Foo] => "list of foos"
}
meth(List(new Bar))
res10: String = "list of strings"
Solution
This is from @smarter on gitter (assumes we don't need to handle empty lists of different types separately):
def meth[A](xs: List[A]) = xs match {
case Nil => "nil"
case (_: String) :: _ => "list of strings"
case (_: Foo) :: _ => 'list of foos"
}
This uses instanceOf
, so it should be extremely fast.