I have two very similar methods. The only difference is the use of ClassTag
and TypeTag
:
def matchClass[A: ClassTag](v: Any) =
v match {
case a: A => "it's A"
case _ => "not A"
}
def matchType[A: TypeTag](v: Any) = ... // same code as matchClass
A compile warning will show for matchType
, but not for matchClass
:abstract type pattern A is unchecked since it is eliminated by erasure case a: A
Why is there a warning? why does it show only for TypeTag
and not ClassTag
?
You don't see a warning for classTag
because that check simply works for those:
scala> matchClass[Int]("aaa")
res82: String = not A
scala> matchClass[Int](5)
res83: String = it's A
And doesn't work for typeTag
:
scala> matchType[Int](5)
res84: String = it's A
scala> matchType[Int]("aaa")
res85: String = it's A
The reason is that for pattern matching on classTags (when it's seeing an implicit) compiler generates something like:
case a: A if classTag[A].runtimeClass.isInstance(a) => ...
There is no way to get runtimeClass
for TypeTag
s in general (considering both compile&runtime, see the UPDATE for a specific case allowing to extract it in runtime only), so that's why compiler doesn’t transform them. By default, pattern matching can't match on generic (polymorphic) types because of erasure, so you can see that warning by default:
scala> def matchGeneric[A](v: Any) =
| v match {
| case a: A => "it's A"
| case _ => "not A"
| }
<console>:28: warning: abstract type pattern A is unchecked since it is eliminated by erasure
case a: A => "it's A"
^
matchGeneric: [A](v: Any)String
UPDATE: As @Seth Tisue mentioned when a tag comes from run-time universe (only) you can get a runtime class for it (but you gonna have to create a mirror first).
Reference:
As per scaladocs of ClassTag
itself:
The compiler tries to turn unchecked type tests in pattern matches into checked ones by wrapping a
(_: T)
type pattern asct(_: T)
, wherect
is theClassTag[T]
instance. Type tests necessary before calling other extractors are treated similarly.SomeExtractor(...)
is turned intoct(SomeExtractor(...))
ifT
inSomeExtractor.unapply(x: T)
is uncheckable, but we have an instance ofClassTag[T]
.
TypeTag
scaladocs and language spec itself don't mention any such functionality for TypeTags
Speculative explanation
There is no way to know for certain why some features are implemented or not, so any speculation would be opinionated (and out of SO scope, and isn't even related to your question directly, but to answer @Tom's comment). Nevertheless (as of 2.12)...
This is probably because "ClassTags provide access only to the runtime class of a type" and are part of scala-library (even though their package is scala.reflect.
), while TypeTags are a part of a separate (and quite wide-broad) reflection API thus are meant to refer to either compile or run time depending on the universe
they're in, so writing the additional checks (during synthesis!!) for those would be not just confusing (for users) but also hard for language developers: synthesis itself is in a different (compiler) module and [synthesis] doesn't depend on reflection (only scala-library) as pattern matching synthesis is happening on early "patmat" stage. Also, scala spec (12.3.4.2 Variance) mentions usage of ClassTag's for synthetic adjustments of array instantiations which (very speculatively) could mean that ClassTags are more integrated with the "syntax sugar"-features.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With