Search code examples
scalalazy-evaluationscala-option

Some constructor with asInstanceOf


When I was writing my recent answer I also tried to solve the problem in more "functional" way, but stuck with the following problem:

scala> "1".asInstanceOf[Int]
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
        ...
but
scala> Some("1".asInstanceOf[Int])
res29: Some[Int] = Some(1)
and only
scala> res29.get
java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
    at scala.runtime.BoxesRunTime.unboxToInt(Unknown Source)
        ...

It looks like the Some's argument is lazy evaluated, but I can't find any clue in the sources. The x parameter in the Some constructor is strict.

Why does Some together with asInstanceOf has such a strange behavior?


Solution

  • The constructor argument is not lazily evaluated. The moment you get the error is the moment when the REPL try to display the result, as an Int (unboxToInt). If you look further in the stack, you find scala_repl_result.

    I believe the problem is that asInstanceOf[Int] does no check at all at runtime. I don't know whether it is according to spec (for value types) or a bug. The compiler is tricked into accepting that "1" is an Int (or boxed Int), pending a runtime check implied by asInstanceOf, but which will not happen.

    As Option/Some are not specialized, at runtime there is only Some[Object]. So for the JVM, there is a call to a constructor new Some(Object o), which is accepted at code verification and at runtime. The toString (called by the REPL on the freshly built Some) is inside the generic code, where T is treated as Object too (or AnyRef), so it works, and Some(1) is displayed. On the other hand, every time there is an access to the value in the context where the compiler knows the type of the generic parameter, a cast is inserted in the code (+ unboxing in case of a value type). This is when it actually fails (here REPL does unboxing before displaying).

    Edit This was in Scala 2.8.1. According to comment by @incrop above, this is now fixed.