5
object Test {
  def main(args: Array[String]) {
    val list: List[Double] = List(1.0, 2.0, 3.0, 4.0)
    val none = None

    case class Test()

    val test = Test()

    def f(x: Any) = x match {
        case _: Some[Test] => println("_ matched")
        case None => println("None matched")
    }

    f(list)
    f(none)
    f(test)
  }
}

Trying to compile the above code leads to an "eliminated by erasure" compile-time warning.

   $>scalac Test.scala
    Test.scala:11: warning: non-variable type argument Test in type pattern
 Some[Test] is unchecked since it is eliminated by erasure
            case _: Some[Test] => println("_ matched")
                    ^
    one warning found

I read this highly regarded Stackoverflow post, but I don't understand the type erasure here.

Community
  • 1
  • 1
Kevin Meredith
  • 41,036
  • 63
  • 209
  • 384

2 Answers2

9

This is warning you that there is no way at runtime to determine if the value is Some[Test] and not, say, Some[Int], or Some[anything else]. This is due to the JVM not knowing about type parameters (i.e. the same reason that we have type erasure in Java). The post you reference shows a way provided in Scala to get around this type erasure issue, should you really need to ensure that you have a Some[Test] and not any other sub-type of Some. In your case, that does not appear to be relevant, so I wouldn't sweat the warning.

On the other hand, more idiomatic (and practical!) would be the following:

def f(x: Any) = x match {
    case Some(y) => println(s"x matched to Some - wrapped value is $y")
    case None => println("None matched")
}

This matches to Some[Any], but also provides the wrapped value for you to use directly inside the case block.

If you do need to be sure your value is of type Test, either try to wrap your head around TypeTags (the link you mentioned, or see here, for example) or you could resort to the somewhat nasty isInstanceOf method:

case Some(y) if (y.isInstanceOf[Test]) => ...

EDIT: or

case Some(e: Test) => ...

as per @senia's answer.

Community
  • 1
  • 1
Shadowlands
  • 14,994
  • 4
  • 45
  • 43
  • Why is `instanceof` considered nasty? – Kevin Meredith Sep 04 '13 at 03:19
  • 3
    @Kevin: If you want to use `y` as instance of `Test` you should check if it's one using `isInstanceOf` and then cast it to `Test` using `asInstanceOf`. It's verbose. And `asInstanceOf` is not type-safe: compiler will not force you to use `isInstanceOf` before `asInstanceOf`. Structural pattern matching is type-safe: you'll get a warning if you are doing something dangerous. – senia Sep 04 '13 at 03:34
9

In runtime in jvm there is no Some[Test] or Some[String] there is only Some[Any]. So you can't match on Some[Test].

In this case you could match on content of Some like this:

case Some(e: Test) => println(s"$e matched")
senia
  • 37,745
  • 4
  • 88
  • 129
  • So at runtime the JVM doesn't know the difference between Some(Int) and Some(String), but it can look up the `type` of the x in Some(x)? – Kevin Meredith Sep 04 '13 at 03:15
  • 3
    @Kevin: Yes, just like with any other variable type. Scala compiler rewrites `case Some(e: Test)` to something like `if (arg.isInstanceOf[Some[_]] && arg.asInstanceOf[Some[Any]].get.isInstanceOf[Test]) { val e = arg.asInstanceOf[Some[Any]].get.asInstanceOf[Test] ... }`. – senia Sep 04 '13 at 03:26