Unterschied zwischen Typinferenz von Methoden- und Klassentypparametern beim Mustervergleich


9

Warum funktioniert der Mustervergleich anders, wenn der Typparameter von einer umschließenden Methode stammt und nicht von einer umschließenden Klasse? Zum Beispiel,

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

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

gibt Fehler

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

während es erfolgreich kompiliert wird, wann Aist Methodentyp Parameter

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

Die Frage basiert auf Daniels Analyse , mit der ich versucht habe, eine Antwort auf eine ähnliche Frage zu geben.

Antworten:


4

Ich habe keine 100% vollständige Antwort, aber ich habe einen Zeiger, der für Sie ausreichend sein könnte.

Der Scala-Compiler befasst sich auf ganz besondere Weise mit GADTs (Generalized Algebraic Data Types). Einige Fälle werden mit spezieller Behandlung gelöst, andere sind ungelöst. Dotty versucht, die meisten Löcher zu füllen, und es hat bereits viele verwandte Probleme gelöst , aber es gibt auch noch einige offene .

Ein typisches Beispiel für eine spezielle GADT-Behandlung im Scala 2-Compiler hängt stark mit Ihrem Anwendungsfall zusammen. Wenn wir uns Folgendes ansehen:

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

und wir deklarieren den Rückgabetyp ausdrücklich als A:

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

es wird gut kompiliert. Ihre IDE könnte sich beschweren, aber der Compiler lässt es durch. Die Methode gibt an A, dass ein zurückgegeben wird , aber der Mustervergleichsfall wird zu einem ausgewertet Int, der theoretisch nicht kompiliert werden sollte. Eine spezielle Behandlung von GADTs im Compiler besagt jedoch, dass dies in Ordnung ist, da in diesem bestimmten Mustervergleichszweig A"behoben" wurde, um ein zu sein Int(weil wir übereinstimmten, Derivedwelches ein ist Base[Int]).

Der generische Typparameter für das GADT (in unserem Fall A) muss irgendwo deklariert werden. Und hier ist der interessante Teil: Die spezielle Compiler-Behandlung funktioniert nur, wenn sie als Typparameter der umschließenden Methode deklariert ist . Wenn es von einem Typelement oder einem Typparameter des einschließenden Merkmals / der Klasse stammt, wird es nicht kompiliert, wie Sie selbst gesehen haben.

Aus diesem Grund habe ich gesagt, dass es sich nicht um eine 100% vollständige Antwort handelt. Ich kann nicht auf einen konkreten Ort (wie die offizielle Spezifikation) verweisen, der dies ordnungsgemäß dokumentiert. Quellen über den Umgang mit der GADTs in Scala kommen zu einem nach unten Paar von Blogeinträge , die durch die Art und Weise groß sind, aber wenn Sie wollen mehr als das wird Sie in den Compiler Code selbst graben. Ich habe genau das versucht, und ich denke, es kommt auf diese Methode an , aber wenn Sie wirklich tiefer gehen möchten, möchten Sie vielleicht jemanden anpingen, der mehr Erfahrung mit der Scala-Compiler-Codebasis hat.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.