Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala pattern matching on generic type with TypeTag generates a warning while ClassTag not?

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?

like image 954
Eyal Roth Avatar asked Dec 28 '15 14:12

Eyal Roth


1 Answers

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 TypeTags 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 as ct(_: T), where ct is the ClassTag[T] instance. Type tests necessary before calling other extractors are treated similarly. SomeExtractor(...) is turned into ct(SomeExtractor(...)) if T in SomeExtractor.unapply(x: T) is uncheckable, but we have an instance of ClassTag[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.

like image 50
dk14 Avatar answered Nov 02 '22 03:11

dk14