Почему неявное преобразование числовых типов несовместимо между выражениями «для / понимания» по сравнению с (!) Операциями присваивания? - PullRequest
1 голос
/ 28 марта 2012

Почему десагеринг и неявное преобразование числовых типов несовместимо между выражениями "for / compception" по сравнению с (!) Операциями присваивания?

Я уверен, что есть много общих точек зрения на это, но я не мог найти краткое и логичное объяснение текущего поведения. [Ref: «Поведение Скалы для / понимания ...» ] Для корректности все приведенные ниже переводы были сгенерированы компилятором scala ("scalac -Xprint: typer -e")

Например, во время неявного преобразования числового присвоения доминирует Тип назначения :

Источник: var l:Long = 0
Результат: val l: Long = 0L

Источник: var l:Long = 0.toInt
Результат: var l: Long = 0.toInt.toLong

Во время неявного преобразования выражений «для / понимания» Тип источника является доминирующим:

Источник: for (i:Long <- 0 to 1000000000L) { }
Результат: 0.to(1000000000L).foreach(((i: Long) => ()))

Источник: for (i <- 0L to 1000000000L) { }
Результат: scala.this.Predef.longWrapper(0L).to(1000000000L).foreach[Unit](((i: Long) => ()))

Ответы [ 2 ]

5 голосов
/ 28 марта 2012

Происходят две совершенно разные вещи. Во-первых, назначение:

val l: Long = 0

У нас есть Int, который назначается на Long. Это не должно быть возможно, если не существует неявного преобразования из Int в Long, которое мы можем проверить следующим образом:

scala> implicitly[Int => Long]
res1: Int => Long = <function1>

Поскольку существует такое преобразование, это преобразование применяется.

Далее, для понимания:

for (i:Long <- 0 to 1000000000L) { }

Это не работает, потому что метод to, вызываемый на Int (фактически вызванный на scala.runtime.RichInt, через неявное преобразование) допускает только аргумент Int, а не Long аргумент.

Метод to, вызываемый для Long (RichLong), допускает аргумент Long, но есть две причины, по которым он не применим к приведенному выше выражению:

  1. Чтобы получить метод RichLong to, Int должен быть сначала преобразован в Long, а затем в RichLong, и Scala не применяет два цепных неявных преобразования, Когда-либо. Он может конвертировать только Int в RichInt или Long, но не Int в Long в RichLong.
  2. Чтобы применить такое преобразование, должно быть какое-то указание, что Long требуется в первую очередь, а это не так. i: Long не относится к типу 0 to 1000000000L, тогда как l: Long относится к типу 0 в первом примере.
2 голосов
/ 28 марта 2012

Я не уверен, что вы подразумеваете под "типом назначения" и "типом источника", но я не вижу никаких проблем.

Если у вас есть Int, вы можете назначить его набыть Long.Это нормально, потому что диапазон Int является подмножеством диапазона Long.Вы не можете передать Long, когда ожидаете Int, потому что Long имеет больший диапазон и, следовательно, может генерировать недопустимые значения Int.И ответ о to методах дан в вашем другом вопросе .

Рассматривая ваши дела:

var l:Long = 0         // fine because Int up-casts to Long

var l:Long = 0.toInt   // fine because Int up-casts to Long

for (i:Long <- 0 to 1000000000L) { } // bad because...
0 to 1000000000L // bad because RichInt.to doesn't accept a Long argument

for (i <- 0L to 1000000000L) { }     // fine because...
0L to 1000000000L // fine because RichLong.to accepts a Long argument, and produces a Range of Longs

Случаи в ваших for примерах имеютничего общего с for.Это связано только с тем, что вы вызываете метод to.Объяснение:

0 to 1000000000L является синтаксическим сахаром для 0.to(1000000000L), поскольку to - это всего лишь метод.Когда вы звоните to на Int, происходит неявное преобразование в RichInt, поэтому вы действительно звоните (new scala.runtime.RichInt(0)).to(1000000000L).Но, поскольку метод RichInt to принимает только аргумент Int, передача Long (то есть 1000000000L) недопустима, поскольку Long нельзя привести к Int (так как он можетсодержат значения, выходящие за пределы диапазона Int.

0L to 1000000000L - синтаксический сахар, опять же, для 0L.to(1000000000L). Но теперь, поскольку метод to вызывается для Longнеявное преобразование в RichLong: (new scala.runtime.RichLong(0L)).to(1000000L). И поскольку метод RichLong to принимает Long в качестве параметра, то все в порядке, потому что это то, что вы ему даете.

РЕДАКТИРОВАТЬ на основании ваших комментариев:

Кажется, что ваше замешательство здесь проистекает из вашей уверенности в том, что = и to должны работать одинаково.нет и не должно. Оператор присваивания, =, является очень специальным ключевым словом в Scala (и любом языке), тогда как to вовсе не является ключевым словом - это просто метод, который может быть найден в обоих RichInt и RichLong.

Тем не менее, по-прежнему нет противоречий. Вот почему:

Scala aПозволяет автоматически кастовать вверх , но не вниз .Причина этого очень проста: если B является разновидностью A, то B может заменить A без страха.Обратное неверно.

Предположим, у вас есть два класса:

class A
class B extends A

val a: A = null
val b: B = null

Итак, подумайте о назначениях:

val x: A = b  // fine, since B is a subtype of A, so b *is* an A
val x: B = a  // error, since As aren't necessarily Bs

Не давайте смотреть на вызовы функций:

def f(x: A) {}  // only accept As
f(b)            // fine, because a B *is* an A

def g(x: B) {}  // only accept Bs
g(a)            // error, because As aren't necessarily Bs

Итак, вы можете видеть, как для операторов присваивания, так и для функций, вы можете заменить подтип его супертипом.Если вы думаете о Int как о Long (с более ограниченным диапазоном), то это совершенно аналогично.Теперь, поскольку методы в значительной степени являются просто функциями, сидящими в классе, мы ожидаем, что поведение будет таким же в отношении аргументов.

Итак, подумайте о том, что вы просите, когда говорите, что RichInt.to(Int) должен быть в состоянии принять Long.Это будет означать, что Scala выполнит некоторое автоматическое и безопасное преобразование из Long в Int, что не имеет смысла.

В заключение: если ваша настоящая проблема заключается в том, что вы просто думаете, что RichInt должноесть метод to(Long), тогда, я думаю, это то, на что жаловаться дизайнерам языков.Но они, вероятно, просто скажут вам использовать .toLong и продолжить свою жизнь.

...