Разница между выводом типа метода и параметрами типа класса при сопоставлении с образцом - PullRequest
9 голосов
/ 06 марта 2020

Почему сопоставление с образцом работает по-разному, когда параметр типа приходит из метода включения, а не из класса включения? Например,

trait Base[T]
case class Derived(v: Int) extends Base[Int]

class Test[A] {
  def method(arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

выдает ошибку

constructor cannot be instantiated to expected type;
 found   : A$A87.this.Derived
 required: A$A87.this.Base[A]
      case Derived(_) => 42
           ^

, в то время как он успешно компилируется, когда A является параметром типа метода

class Test {
  def method[A](arg: Base[A]) = {
    arg match {
      case Derived(_) => 42
    }
  }
}

Вопрос основан на анализе Даниила , который я использовал, чтобы попытаться дать ответ на аналогичный вопрос.

1 Ответ

4 голосов
/ 15 марта 2020

У меня нет 100% полного ответа, но у меня есть указатель, который может быть достаточным для вас.

Scala компилятор работает с GADT (Generalized Algebrai c Types) в очень особенный способ. Некоторые случаи решаются с особой обработкой, некоторые случаи не решены. Дотти пытается заполнить большую часть дыр, и она уже решила множество связанных проблем, однако есть еще немало открытых .

Типичный пример специальной обработки GADT в компиляторе Scala 2 очень связан с вашим вариантом использования. Если мы посмотрим на:

def method[A](arg: Base[A]) = {
  arg match {
    case Derived(_) => 42
  }
}

и мы явно объявим тип возвращаемого значения A:

def method[A](arg: Base[A]): A 

, он будет скомпилирован очень хорошо. Ваша IDE может жаловаться, но компилятор пропустит это. Метод говорит, что возвращает A, но случай сопоставления с образцом оценивается в Int, который теоретически не должен компилироваться. Тем не менее, специальная обработка GADT в компиляторе говорит, что это нормально, потому что в этой конкретной ветке сопоставления с шаблоном A был «зафиксирован», чтобы быть Int (потому что мы соответствовали Derived, что Base[Int]). Параметр типа

Generi c для GADT (в нашем случае A) должен быть где-то объявлен. И вот интересная часть - специальная обработка компилятора работает только тогда, когда она объявлена ​​как параметр типа метода включения . Если это происходит от члена типа или параметра типа включающей черты / класса, он не компилируется, как вы сами убедились.

Вот почему я сказал, что это не 100% полный ответ - я не могу указать конкретное место (например, официальную спецификацию), которое документирует это должным образом. Источники обработки GADT в Scala сводятся к паре из blogposts , которые, кстати, хороши, но если вы хотите большего, вы придется копаться в коде компилятора самостоятельно. Я попытался сделать именно это, и я думаю, что это сводится к этому методу , но если вы действительно хотите go глубже, вы можете захотеть пропинговать кого-то более опытного с Scala базой кода компилятора.

...