Новое поведение в Scala 2.10

Вот два сеанса REPL (вдохновленные этим вопросом, хотя мой вопрос отличается):

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

и

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)
...

Единственное различие заключается в том, что первый - это Scala 2.9.2, а второй - 2.10.0.

Может ли кто-нибудь указать на изменения в 2.10, которые приводят к этому новому поведению?

Я знаю, что приведение к Nothing - это глупое дело, и что ответ может быть "это все undefined поведение, поэтому просто прекратите это делать", но оно похоже на то, что может потенциально имеют последствия для обновителей, и я не помню, чтобы прибегать к обсуждению изменений, которые могли бы объяснить это.

Ответ 1

Так как Scala рассматривает null по-разному от случая None для опции, даже значение null Nothing проблематично - должны быть ровно нулевые экземпляры Nothing, а не один экземпляр которые могут или не могут сломаться в зависимости от того, как вы его используете.

Таким образом, я не вижу, как старое поведение - это всего лишь ошибка. В примечаниях к выпуску следует отметить, что он был исправлен, но полагаться на .asInstanceOf[Nothing], чтобы сделать что-либо, кроме того, чтобы выбросить исключение, достаточно противоречит типу, что я не думаю, что больше ничего не требуется. (На самом деле, я даже не думаю, что примечание к выпуску необходимо.)

Ответ 2

Это похоже на проблему только с консолью, а не с языком. Если вы запустите это небольшое приложение, которое вызывает тот же самый метод, scala 2.10 не имеет проблемы с ним.

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

  def takesString(a: String) = 42
}

Чтобы упростить свой пример сверху, вы можете просто ввести

null.asInstanceOf[Nothing]

и консоль даст вам ту же ошибку. Я предполагаю, что это имеет какое-то отношение к распечатке типа.

Обновление: Похоже, я случайно побежал против 2.9.2. Все еще не работает как script в 2.10 RC5, как указывает автор в комментарии.

Ответ 3

Я знаю, что вы не ожидаете ответа "это все undefined поведение, поэтому (...)", но когда вы добавляете "вещь, которая потенциально может иметь последствия для обновлений", я должен помнить (даже если это очевидно), что люди не могут полагаться или ожидать чего-либо из результата вещи, которая имеет поведение undefined, своим собственным определением.

В конкретном случае, о котором вы говорили, я не думаю, что это поведение undefined: оно должно вызывать исключение. Nothing является подклассом Null, а не наоборот - мое первое ожидание, без тестирования, состояло в том, что строка null.asInstanceOf[Nothing] выбрала бы ClassCastException, так как Null не является Nothing. Однако вы можете видеть, что Null является специальным экземпляром (как в Java). Попробуйте запустить:

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>)

Я предполагаю, что это происходит потому, что внутри, obj.asInstanceOf[T] вызывает obj.getClass(), чтобы проверить приведение во время выполнения. Как вызов любого метода на Null выдает a NullPointerException, это исключение выдается перед ClassCastException.

Возвращаясь к вашему конкретному вопросу, кажется, что Scala 2.9.2 обрабатывает особым образом особый случай. Выполнение еще нескольких тестов:

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

Вы можете видеть, что аргумент всегда оценивается, кроме вашего случая. Scala 2.10 определенно имеет наиболее последовательное поведение. Однако эта проблема не должна влиять на обновление разработчика до Scala 2.10; Я не вижу случая, когда obj.asInstanceOf[Nothing] является правильным кодом.