Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

New behavior in Scala 2.10

Here are two REPL sessions (inspired by this question, although my question is different):

Welcome to Scala version 2.9.2 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def ignore(it: String) = 42
ignore: (it: String)Int

scala> ignore(null.asInstanceOf[Nothing])
res0: Int = 42

And:

Welcome to Scala version 2.10.0 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> def ignore(it: String) = 42
ignore: (it: String)Int

scala> ignore(null.asInstanceOf[Nothing])
java.lang.NullPointerException
        at .<init>(<console>:9)
        at .<clinit>(<console>)
        at .<init>(<console>:7)
        at .<clinit>(<console>)
        at $print(<console>)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
...

The only difference is that the first is Scala 2.9.2 and the second is 2.10.0.

Can someone point to the changes in 2.10 that lead to this new behavior?

I know that casting to Nothing is a silly thing to do, and that the answer might be "this is all undefined behavior so just stop doing that", but it looks like the kind of thing that could potentially have implications for upgraders, and I don't remember running into any discussions of changes that would explain this.

like image 678
Travis Brown Avatar asked Dec 28 '12 14:12

Travis Brown


3 Answers

Since Scala treats null differently from the None case on an option, even a null value of Nothing is problematic--there should be exactly zero instances of Nothing, not one instance that may or may not break depending on how you use it.

Thus, I cannot see how the old behavior is anything but a bug. It should be mentined in the release notes that it was fixed, but relying on .asInstanceOf[Nothing] to do anything save throw an exception is sufficiently contrary to type-sanity that I don't think anything more is needed. (In fact, I don't even think the release note is needed.)

like image 78
Rex Kerr Avatar answered Nov 04 '22 01:11

Rex Kerr


This looks like an issue just with the console, not with the language. If you run this small application which invokes the exact same method, scala 2.10 doesn't have a problem with it.

object Test extends App {
  override def main(args: Array[String]) {
    println(takesString(null.asInstanceOf[Nothing]))
  }

  def takesString(a: String) = 42
}

To simplify your example from above, you could just type

null.asInstanceOf[Nothing]

and the console would give you the same error. I presume it has something to do with printing out the type.

Update: Looks like I accidentally ran against 2.9.2. Still fails as a script in 2.10 RC5 as the author points out in comment.

like image 3
Matt Hughes Avatar answered Nov 04 '22 02:11

Matt Hughes


I know you are not expecting the answer "this is all undefined behavior, so (...)", but when you add "thing that could potentially have implications for upgraders", I must remember (even if it's obvious) that people can't rely or expect anything of the result of a thing that has undefined behavior, by its own definition.

In the specific case you mentioned, I don't think it is undefined behavior: it should throw an exception. Nothing is a subclass of Null, not the other way around - my first expectation, without testing, was that the line null.asInstanceOf[Nothing] would throw a ClassCastException, as null is not a Nothing. However, you can see that null is a special instance (as it is in Java). Try running:

scala> "aaa".asInstanceOf[Nothing]
java.lang.ClassCastException: java.lang.String cannot be cast to scala.runtime.N
othing$
        at .<init>(<console>:8)
        at .<clinit>(<console>)

My guess is that it happens because internally, obj.asInstanceOf[T] calls obj.getClass() in order to check the cast at runtime. As calling any method on null throws a NullPointerException, that exception is thrown before ClassCastException.

Back to your specific question, it seems that Scala 2.9.2 handles in a (very) special way that specific case. Running a few more tests:

scala> ignore(3.asInstanceOf[String])
java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.Stri
ng
        at .<init>(<console>:9)
        at .<clinit>(<console>)
scala> ignore({ println("test"); "aaa" })
test
res6: Int = 42

You can see that the argument is always being evaluated, save for your case. Scala 2.10 definitely has the most consistent behavior. However, this issue should not affect any developer upgrading to Scala 2.10; I can't see any case where obj.asInstanceOf[Nothing] is correct code.

like image 2
Rui Gonçalves Avatar answered Nov 04 '22 01:11

Rui Gonçalves