Diferença entre inferência de tipo de método e parâmetros de tipo de classe na correspondência de padrões

9

Por que a correspondência de padrões funciona de maneira diferente quando o parâmetro type é proveniente de um método de fechamento em oposição a uma classe de fechamento? Por exemplo,

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

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

dá erro

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

enquanto compila com êxito quando Aé o parâmetro do tipo de método

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

A pergunta é baseada na análise de Daniel , que eu costumava tentar responder a perguntas semelhantes.

Mario Galic
fonte

Respostas:

4

Não tenho a resposta 100% completa, mas tenho um ponteiro que pode ser suficiente para você.

O compilador Scala lida com GADTs (tipos de dados algébricos generalizados) de uma maneira muito particular. Alguns casos são resolvidos com tratamento especial, outros não são resolvidos. Dotty está tentando preencher a maioria dos buracos e já resolveu muitos problemas relacionados, no entanto, ainda existem alguns abertos .

O exemplo típico de manipulação especial do GADT no compilador Scala 2 está muito relacionado ao seu caso de uso. Se dermos uma olhada em:

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

e declaramos explicitamente o tipo de retorno como A:

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

ele irá compilar muito bem. Seu IDE pode reclamar, mas o compilador o deixará passar. O método diz que retorna um A, mas o caso de correspondência de padrões é avaliado em um Int, que teoricamente não deveria ser compilado. No entanto, o tratamento especial de GADTs no compilador diz que não há problema, porque nesse ramo específico de correspondência de padrões Afoi "corrigido" para ser um Int(porque combinamos com o Derivedque é a Base[Int]).

O parâmetro de tipo genérico para o GADT (no nosso caso A) deve ser declarado em algum lugar. E aqui está a parte interessante - o tratamento especial do compilador só funciona quando é declarado como o parâmetro de tipo do método envolvente . Se for proveniente de um membro de tipo ou de um parâmetro de tipo da característica / classe anexa, não será compilado, como você testemunhou.

É por isso que eu disse que não é uma resposta 100% completa - não posso apontar para um local concreto (como a especificação oficial) que documenta isso corretamente. As fontes de manipulação de GADTs no Scala se resumem a alguns posts do blog , o que é ótimo, a propósito, mas se você quiser mais do que isso, precisará pesquisar o código do compilador. Eu tentei fazer exatamente isso, e acho que se resume a esse método , mas se você realmente quiser ir mais fundo, poderá executar ping em alguém mais experiente com a base de código do compilador Scala.

slouc
fonte