Почему модуль может быть назначен на кортеж без ошибки компиляции? - PullRequest
3 голосов
/ 15 января 2020

Проверьте этот сеанс REPL в разделе Scala 2.12.10:

scala> val a = 3 match { case 3 => 1 case 4 => println("why") }
a: AnyVal = 1

scala> val a: Int = 3 match { case 3 => 1 case 4 => println("why") }
<console>:11: error: type mismatch;
 found   : Unit
 required: Int
       val a: Int = 3 match { case 3 => 1 case 4 => println("why") }

scala> val (a, b) = 3 match { case 3 => (1, 2) case 4 => println("why") }
a: Any = 1
b: Any = 2

scala> val (a: Int, b) = 3 match { case 3 => (1, 2) case 4 => println("why") }
a: Int = 1
b: Any = 2

scala> val (a, b) = 4 match { case 3 => (1, 2) case 4 => println("why") }
why
scala.MatchError: () (of class scala.runtime.BoxedUnit)
  ... 36 elided

Я бы ожидал, что фрагменты с кортежами не будут компилироваться, как если бы модуль когда-либо возвращался из второго совпадения, тогда вы всегда получаете ошибка во время выполнения. Почему модуль успешно совпадает с Tuple2 в представлении компилятора?

Ответы [ 2 ]

2 голосов
/ 15 января 2020

Unit нельзя присвоить Tuple

val t: (Int, Int) = ()  // Error: type mismatch; found: Unit required: (Int, Int)

Однако следующий синтаксис представляет собой сопоставление с образцом

val (a: Int, b: Int) = ...

, который в вашем случае приводит к тому, что что-то подобное

val x: Any = 3 match {
  case 3 => (1, 2)
  case 4 => println("why")
}

val a: Int = x match {
  case t: (_, _) if t._1.isInstanceOf[Int] => t._1.asInstanceOf[Int]
}

val b: Int = x match {
  case t: (_, _) if t._2.isInstanceOf[Int] => t._2.asInstanceOf[Int]
} 

Обратите внимание на те asInstanceOf[Int], которые убеждают тип компилятора c типа a и b равен Int, однако, что произойдет во время выполнения это другая история. Например, рассмотрим

val t: (Int, Int) = println("why").asInstanceOf[(Int, Int)]

, который компилируется, но не работает во время выполнения.


Анализ -Xprint:jvm вывода

lazy val (a: Int, b: Int) = (): Any 

, мы имеем приблизительно

val t: Tuple2 = {
  val x1: Any = ()
  if (x1.isInstanceOf[Tuple2]) {
    val x2: Tuple2 = x1.asInstanceOf[Tuple2]
    val a: Any = x2._1
    val b: Any = x2._2
    if (a.isInstanceOf[Int]) {
      val x3: Int = scala.Int.unbox(a)   // in effect asInstanceOf[Int]
      if (b.isInstanceOf[Int]) {
        val x4: Int = scala.Int.unbox(b) // in effect asInstanceOf[Int]
        new Tuple2(x3, x4)
      } else throw new MatchError(x1)
    } else throw new MatchError(x1)
  } else throw new MatchError(x1)
}

def a: Int = t._1
def b: Int = t._2

, в то время как

lazy val (a: Int, b: Int) = ()

не компилируется, следовательно, если выражение справа от =, в определении значения шаблона, набирает Any, это имеет значение.

2 голосов
/ 15 января 2020

Поскольку ваша частичная функция может возвращать либо Tuple2[Int, Int], либо Unit, компилятор считает свой тип возвращаемого значения "наименьшим общим супертипом" из этих двух типов, то есть Any:

scala> val x = 4 match { case 3 => (1, 2) case 4 => println("why") }
why
x: Any = ()

Обратите внимание, что возвращаемое значение равно x: Any = (), а не x: Unit = ().

То, что вы делаете при извлечении кортежа, эквивалентно этому, который компилируется (поскольку Any является супертипом Tuple2) , но выдает ошибку MatchE:

scala> val (a, b) = ().asInstanceOf[Any]
scala.MatchError: () (of class scala.runtime.BoxedUnit)
  ... 28 elided
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...