Diferencia entre la inferencia de tipo del método y los parámetros de tipo de clase en la coincidencia de patrones

9

¿Por qué la coincidencia de patrones funciona de manera diferente cuando el parámetro tipo proviene de un método de cierre en lugar de una clase de cierre? Por ejemplo,

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

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

da error

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

mientras se compila con éxito cuando Aes un parámetro de tipo de método

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

La pregunta se basa en el análisis de Daniel , que solía intentar responder a una pregunta similar.

Mario Galic
fuente

Respuestas:

4

No tengo la respuesta 100% completa, pero tengo un puntero que podría ser suficiente para ti.

El compilador Scala trata los GADT (tipos de datos algebraicos generalizados) de una manera muy particular. Algunos casos se resuelven con un manejo especial, algunos casos no se resuelven. Dotty está tratando de llenar la mayoría de los agujeros, y ya ha resuelto muchos problemas relacionados, sin embargo, todavía hay bastantes abiertos .

El ejemplo típico de manejo especial de GADT en el compilador Scala 2 está muy relacionado con su caso de uso. Si echamos un vistazo a:

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

y declaramos explícitamente que el tipo de retorno es A:

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

se compilará muy bien. Su IDE puede quejarse, pero el compilador lo dejará pasar. El método dice que devuelve un A, pero el caso de coincidencia de patrón se evalúa en un Int, que en teoría no debería compilarse. Sin embargo, el manejo especial de los GADT en el compilador dice que está bien, porque en esa rama de coincidencia de patrones en particular Ase ha "arreglado" para que sea un Int(porque coincidimos en Derivedcuál es un Base[Int]).

El parámetro de tipo genérico para GADT (en nuestro caso A) debe declararse en alguna parte. Y aquí está la parte interesante: el manejo especial del compilador solo funciona cuando se declara como parámetro de tipo del método de inclusión . Si proviene de un miembro de tipo o un parámetro de tipo del rasgo / clase adjunto, no se compila, como lo observó usted mismo.

Es por eso que dije que no es una respuesta 100% completa: no puedo señalar un lugar concreto (como las especificaciones oficiales) que documente esto correctamente. Fuentes en el manejo de GADTs en Scala descendido a un par de entradas del blog , que son grandes por cierto, pero si quieres más de que va a tener que excavar en el código de compilador de sí mismo. Intenté hacer exactamente eso, y creo que todo se reduce a este método , pero si realmente quieres profundizar, es posible que quieras hacer ping a alguien más experimentado con la base de código del compilador Scala.

slouc
fuente