Вывод типа Scala ломается в очевидной обстановке? - PullRequest
5 голосов
/ 18 июня 2019

У меня есть довольно простой кусок кода, который не компилируется, так как тип, по-видимому, не может быть выведен.Я хотел бы понять, почему это не так, поскольку у меня есть множество аннотаций типов, и это должно работать.Код:

object MyApp {

  trait A { 
    type V
    val value: V
  }

  case class ConcreteA(value: Int) extends A { type V=Int }

  def convert[T<:A](v: T#V): T = ConcreteA(v.asInstanceOf[Int]).asInstanceOf[T] // brief method with casts for illustration purposes only


  def main(args: Array[String]) {
    val i: Int = 10
    val converted: ConcreteA = convert(i)
  }

}

(он компилируется, если я добавляю явный [ConcreteA] на convert -call)

Я получаю ошибку

MyApp.scala:19: error: no type parameters for method convert: (v: T#V)T exist so that it can be applied to arguments (Int)
 --- because ---
argument expression's type is not compatible with formal parameter type;
 found   : Int
 required: ?T#V
    val converted: ConcreteA = convert(i)
                               ^
MyApp.scala:19: error: type mismatch;
 found   : Int
 required: T#V
    val converted: ConcreteA = convert(i)
                                       ^
MyApp.scala:19: error: type mismatch;
 found   : T
 required: MyApp.ConcreteA
    val converted: ConcreteA = convert(i)
                                      ^
three errors found

Кто-нибудь может мне это объяснить?

Редактировать: Я ожидал, что Scala сделает вывод, что T здесь ConcreteA.Я сделал это, потому что результат вызова convert входит в тип val, аннотированный как таковой.

1 Ответ

4 голосов
/ 18 июня 2019

Проблема, , насколько я знаю , заключается в том, что вывод типа работает с использованием алгоритма унификации , который просто попытается удовлетворить ограничения для данные значения. Сначала он попытается доказать, что входные данные v удовлетворяют типу T#V, но в этот момент он не имеет никакой информации о T, поэтому он просто не может выполнить проверку перед проверкой, удовлетворяет ли тип результата условию.
Если вы явно укажете, что T равно ConcreteA, то он сможет продолжить.

Однако вы можете заставить его работать, отложив проверку типов аргументов после логического вывода, используя Обобщенные ограничения типов .

def convert[T <: A, V](v: V)(implicit ev: V <:< T#V): T = ???

val converted: ConcreteA = convert(10)
// converted: ConcreteA = ConcreteA(10)

val converted: ConcreteA = convert("hey")
// Compile error:  Cannot prove that String <:< T#V.

В этом случае значение v не имеет никаких ограничений, поэтому компилятор просто связывает V с типом v, который уже знает, что это такое. Затем он проверяет тип возвращаемого значения T, который, как он знает, должен быть ConcreteA, который удовлетворяет условию, что он должен быть подтипом A, таким образом, он проходит.
После решения логического вывода компилятор пытается решить имплики . В этом случае ему необходимо получить (доказать) , что V <:< T#V, что в первом случае работает, но во втором не получается.

...