Разница между выводом типа метода и параметрами типа класса при сопоставлении с образцом

9

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

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

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

Марио Галич
источник

Ответы:

4

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

Компилятор Scala работает с GADT (обобщенными алгебраическими типами данных) очень специфическим образом. Некоторые случаи решаются с особой обработкой, некоторые случаи не решены. Дотти пытается заполнить большую часть дыр, и она уже решила множество связанных с этим проблем, однако есть еще немало открытых .

Типичный пример специальной обработки 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которой есть a Base[Int]).

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

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

slouc
источник