Différence entre l'inférence de type de la méthode et les paramètres de type de classe dans la correspondance de modèle

9

Pourquoi la correspondance de modèles fonctionne-t-elle différemment lorsque le paramètre de type provient d'une méthode englobante par opposition à une classe englobante? Par exemple,

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

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

donne une erreur

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

tandis qu'il compile avec succès quand Aest le paramètre de type de méthode

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

La question est basée sur l' analyse de Daniel , que j'ai utilisée pour tenter de répondre à une question similaire.

Mario Galic
la source

Réponses:

4

Je n'ai pas la réponse complète à 100%, mais j'ai un pointeur qui pourrait vous suffire.

Le compilateur Scala traite les GADT (Generalized Algebraic Data Types) d'une manière très particulière. Certains cas sont résolus avec un traitement spécial, certains cas ne sont pas résolus. Dotty tente de remplir la plupart des trous, et il a déjà résolu beaucoup de problèmes connexes, mais il y a encore pas mal ouverts ceux aussi bien.

Un exemple typique de gestion spéciale de GADT dans le compilateur Scala 2 est très lié à votre cas d'utilisation. Si nous regardons:

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

et nous déclarons explicitement que le type de retour est A:

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

il compilera très bien. Votre IDE pourrait se plaindre, mais le compilateur le laissera passer. La méthode indique qu'elle renvoie un A, mais le cas de correspondance de modèle est évalué en un Int, qui ne devrait théoriquement pas être compilé. Cependant, la gestion spéciale des GADT dans le compilateur dit que ça va, parce que dans cette branche particulière, la correspondance de modèle Aa été "corrigée" pour être un Int(parce que nous nous sommes appariés sur Derivedce qui est un Base[Int]).

Le paramètre de type générique pour le GADT (dans notre cas A) doit être déclaré quelque part. Et voici la partie intéressante - la gestion spéciale du compilateur ne fonctionne que lorsqu'elle est déclarée comme paramètre type de la méthode englobante . S'il provient d'un membre de type ou d'un paramètre de type de la caractéristique / classe englobante, il ne se compile pas, comme vous l'avez constaté vous-même.

C'est pourquoi j'ai dit que ce n'était pas une réponse complète à 100% - je ne peux pas pointer vers un endroit concret (comme une spécification officielle) qui documente cela correctement. Sources sur la manipulation de GADTs à Scala descendre à un couple de de blogposts , qui sont grands par la voie, mais si vous voulez plus que vous devrez creuser dans le code du compilateur vous. J'ai essayé de faire exactement cela, et je pense que cela revient à cette méthode , mais si vous voulez vraiment aller plus loin, vous voudrez peut-être cingler quelqu'un plus expérimenté avec la base de code du compilateur Scala.

slouc
la source