Ich habe eine Frage bezüglich Typinferenzierung auf Scala Typ-Konstruktoren. Ich benutze Scala 2.9.1 ...
Angenommen, ich habe Tree definiert:
%Vor%Und einen BinaryTree basierend auf meiner Baumdefinition definiert:
%Vor%Ich kann jetzt einen BinaryTree von ganzen Zahlen als solche definieren:
%Vor% Das Problem dabei ist, dass ich die Typ-Parameter immer angeben muss, wenn ich Node
instanziiere.
Also wenn du das tust:
%Vor%Ich bekomme den Fehler:
%Vor% Gibt es eine Möglichkeit, den Typ-Checker so zu zwingen, dass ich die Typen von Node
nicht explizit angeben muss?
Danke!
Wenn ich richtig verstehe, die Aussage
%Vor%in meiner ursprünglichen Frage funktioniert nicht, da diese Paardeklaration nur syntaktischer Zucker für einen Tuple2-Typkonstruktor ist (der zwei Typparameter benötigt). Dies führt dazu, dass der Typinferenzer fehlschlägt.
Wenn ich meine eigene Pair-Klasse deklariere (wie didierd in seiner Antwort andeutet), gelingt es mir, dass der Tree korrekt funktioniert.
%Vor%Dann kann ich das tun ...
%Vor%Ich weiß, dass Didierd diese Lösung im Vorbeigehen erwähnt hat, aber das scheint sich so zu verhalten, wie ich es möchte. Bitte lassen Sie mich wissen, was Sie denken!
Es gibt ein Problem, mit dem man C[X] = (X,X)
ableiten kann. Angenommen, Sie übergeben (String, String)
irgendwo, der Compiler erwartet C[String]
und muss auf C
schließen, C[X]
könnte (X, X)
, (X, String)
, (String, X)
oder sogar (String, String)
mit X
phantom sein.
Wenn Sie den Alias Pair
deklariert haben, hilft das nicht. Ich glaube, Sie müssen case class Pair[A](_1: A, _2: A)
- applied, interrogation C[X] = Pair[String]
und X
phantom erklären, wäre immer noch möglich, aber zum Glück macht der Compiler das nicht.
Wenn Sie Tree(1, Pair(Leaf(2), Leaf(3))
schreiben, wird das C
in Leaves nicht abgeleitet. Ich bin mir nicht sicher warum. Aber auf keinen Fall könnte man daraus schließen, wenn man einfach val l = Leaf(2)
schreibt.
Ich denke, Sie können etwas erreichen, indem Sie alles kovariant machen
%Vor%Mit Covarianz entfernen Sie C aus Leaf, so dass es nicht abgeleitet werden muss
%Vor%Eine Nebenbemerkung, wäre es nicht sinnvoller
zu haben %Vor% statt Leaf
. Mit Leaf
gibt es Formen von Binärbäumen, die Sie nicht erhalten können.
Aktualisieren in Bezug auf Ihre Kommentare:
Berühre den Phantomtyp nicht zu sehr, ich hätte es nicht erwähnen sollen. Wenn Sie einen Typ T[X]
definieren und X
in der Definition von T nicht anders erscheint, wird dies als Phantomtyp bezeichnet. Cleverer Code könnte damit geschrieben werden, um sicherzustellen, dass einige Eigenschaften zum Zeitpunkt der Kompilierung bewiesen werden, aber das ist hier nicht der Punkt.
Wenn der scala-Compiler einige Typen T und X erhält und auf C schließen muss, so dass C [X] (ein Supertyp von) T ist - in meinem Beispiel also T = (String , String) und X = String - es funktioniert nur, wenn T (oder ein Supertyp) eine Parametrisierung eines Typs mit genau einem Typparameter ist. Allgemeiner, so viele Typparameter wie der Typparameter C hat. Da C einen hat und Tuple2 zwei hat (Sie haben einen Alias definiert Pair zählt nicht), kann es nicht funktionieren.
Ich habe versucht zu zeigen, dass der Compiler ohne eine solche Regel viele Möglichkeiten für C
hätte. Wenn ich weiß, dass (String, Int)
ein C[String]
ist, und dass ich raten muss, was C
ist, dann würde ich denken, dass C[X]
(X, Int)
ist. Wenn Sie schreiben, wenn übergeben (String, Int), würde das nicht, wenn Sie (Any, Any) folgern, es ergibt keinen Sinn, da es versucht, auf einen Typkonstruktor zu schließen. Die Antwort muss C[X] = something with X
sein (mit Ausnahme der Möglichkeit, dass X Phantom ist). Völlig anders wäre Pair[X] = (String, Int)
zu haben und auf X
zu schließen. Dann würde es tatsächlich X = Any
ableiten. Also gegeben C [String] = (String, String), C [X] = (X, String) ist so viel eine Lösung wie C [X] = (X, X) ist.
Bei Ihrem zweiten Kommentar bezüglich List
ist es das gleiche Problem, das auch mit Pair
existiert, wenn Sie case class Pair
(dritter Absatz in der obigen Antwort) definiert haben, nämlich dass es nicht folgert, was C
ist Wenn Sie Leaf(2)
schreiben, erscheint C
nicht. Hier setzt die Kovarianz ein und verzichtet auf den Parameter C in Leaf.
Tags und Links scala generics type-inference higher-kinded-types gadt