Scala: Wie man eine Fallklassenkopie erstellt, bewahrt manifestierte Informationen auf

8

Eine Fallklassen copy() -Methode soll eine identische Kopie der Instanz erstellen und alle Felder nach Namen ersetzen. Dies scheint fehlzuschlagen, wenn die Fallklasse Typparameter mit Manifesten enthält. Die Kopie verliert alle Kenntnisse über die Arten ihrer Parameter.

%Vor%

Warum verliert Foo.copy das Manifest in den Parametern und wie kann ich es beibehalten?

    
drhagen 11.08.2012, 22:02
quelle

2 Antworten

15

Mehrere Scala-Besonderheiten interagieren, um dieses Verhalten zu erzeugen. Die erste Sache ist, dass Manifest s nicht nur an die geheime implizite Parameterliste im Konstruktor, sondern auch an die Kopiermethode angehängt wird. Es ist bekannt, dass

case class Foo[+A : Manifest](a: A)

ist nur syntaktischer Zucker für

case class Foo[+A](a: A)(implicit m: Manifest[A])

, aber das betrifft auch den Kopierkonstruktor, der so aussehen würde

def copy[B](a: B = a)(implicit m: Manifest[B]) = Foo[B](a)(m)

Alle diese implicit m s werden vom Compiler erstellt und über die implizite Parameterliste an die Methode gesendet.

Das wäre in Ordnung, solange man die Methode copy an einer Stelle verwendet, an der der Compiler den Parameter Foo s type kennt. Dies funktioniert beispielsweise außerhalb der Bar-Klasse:

%Vor%

Das funktioniert, weil der Compiler folgert, dass foo ein Foo[Int] ist, also weiß es, dass foo.a ein Int ist, so dass es copy wie folgt aufrufen kann:

val aCopy = foo.copy()(manifest[Int]())

(Beachten Sie, dass manifest[T]() eine Funktion ist, die eine Manifest-Repräsentation vom Typ T erzeugt, zB Manifest[T] mit einem Großbuchstaben "M". Nicht gezeigt ist das Hinzufügen des Standardparameters in copy .) Es funktioniert auch in der Klasse Foo , da es bereits das Manifest enthält, das beim Erstellen der Klasse übergeben wurde. Es würde ungefähr so ​​aussehen:

%Vor%

Im ursprünglichen Beispiel schlägt es jedoch in der Klasse Bar aufgrund der zweiten Besonderheit fehl: Während die Typparameter von Bar innerhalb der Klasse Bar bekannt sind, sind die Typparameter der Typparameter nicht . Es weiß, dass A in Bar ein Foo oder ein SubFoo oder SubSubFoo ist, aber nicht, wenn es sich um Foo[Int] oder Foo[String] handelt. Dies ist natürlich das bekannte Problem beim Löschen in Scala, aber es scheint hier ein Problem zu sein, auch wenn es nicht so aussieht, als würde die Klasse irgendetwas mit dem Typ% code% s type Parameter tun. Aber vergessen Sie nicht, dass jedes Mal, wenn foo aufgerufen wird, eine Manifestation heimlich injiziert wird, und diese Manifeste überschreiben diejenigen, die vorher dort waren. Da die copy -Klasse keine Ahnung hat, war der Typparameter von Bar , er erstellt nur ein Manifest von foo und sendet das wie folgt:

Any

Wenn man die def fooCopy = foo.copy()(manifest[Any]) -Klasse kontrolliert (zB ist es nicht Foo ), dann kann man es umgehen, indem man das Kopieren in der Foo-Klasse durchführt, indem man eine Methode hinzufügt, die das korrekte Kopieren ausführt, wie List oben, und gebe das Ergebnis zurück:

%Vor%

Eine andere Lösung besteht darin, den Parameter localCopy s type als manifest type-Parameter von Foo hinzuzufügen:

%Vor%

Aber das skaliert schlecht, wenn die Klassenhierarchie groß ist (d. h. mehr Mitglieder haben Typparameter, und diese Klassen haben auch Typparameter), da jede Klasse die Typparameter jeder darunter liegenden Klasse haben müsste. Es scheint auch, dass die Typinferenz bei dem Versuch, ein Bar zu konstruieren, ausflippt:

%Vor%     
drhagen 11.08.2012, 22:02
quelle
1

Es gibt zwei Probleme, die Sie identifiziert haben. Das erste Problem ist das Problem beim Löschen des Typs innerhalb von Bar , wobei Bar den Typ des Manifests von Foo nicht kennt. Ich würde persönlich die localCopy Workaround verwenden, die Sie vorgeschlagen haben.

Das zweite Problem besteht darin, dass ein weiterer impliziter Code heimlich in copy eingefügt wird. Dieses Problem wird behoben, indem der Wert explizit erneut in copy übergeben wird. Zum Beispiel:

%Vor%

Wir sehen, dass die Kopie das Int manifest behalten hat, aber der Typ von fooCopy und res2 ist Manifest[Any] wegen Löschung.

Da ich Zugriff auf den impliziten Beweis benötigte, um copy auszuführen, musste ich die explizite Syntax implicit (hah) anstelle der kontextgebundenen Syntax verwenden. Aber die Verwendung der expliziten Syntax verursachte Fehler:

%Vor%

WTF? Wie funktioniert die kontextgebundene Syntax und das explizite implicit nicht? Ich grub herum und fand eine Lösung für das Problem: die @uncheckedVariance Annotation.

AKTUALISIEREN

Ich habe etwas mehr gegraben und festgestellt, dass in Scala 2.10 Fallklassen in nur geändert wurden. Kopieren Sie die Felder aus der ersten Parameterliste in copy() .

  

Martin sagt: Fallklasse-Ness wird nur dem ersten Argument verliehen   Liste den Rest sollte nicht kopiert werden.

Details zu dieser Änderung finden Sie unter Ссылка .

    
sourcedelica 14.08.2012 11:45
quelle