2013-02-06 8 views
11

Ich habe einige Probleme mit Scala, um aus einer Typprojektion den richtigen Typ abzuleiten.Wie kann man aus einem Projektionstyp den richtigen Typparameter ableiten?

Beachten Sie Folgendes:

trait Foo { 
    type X 
} 

trait Bar extends Foo { 
    type X = String 
} 

def baz[F <: Foo](x: F#X): Unit = ??? 

Dann folgendes kompiliert fein:

val x: Foo#X = ???  
baz(x) 

aber die folgenden nicht kompiliert:

val x: Bar#X = ???  
baz(x) 

Scala „zugrunde liegender Typ String sieht "für x, aber hat die Information verloren, dass x ist ein Bar#X. Es funktioniert gut, wenn ich den Typ mit Anmerkungen versehen:

baz[Bar](x) 

Gibt es eine Möglichkeit, die richtige Art Parameter für baz zu machen Scala ableiten?
Wenn nicht, was ist die allgemeine Antwort, die es unmöglich macht?

+2

Keine Antwort, aber es ist erwähnenswert, dass, wenn Sie 'x' mit einem Typenbezeichner statt einer Typprojektion eingeben, es funktioniert - einschließlich z. 'Objekt BAR erweitert Bar; val x: BAR.X = "a"; baz (x) '. –

+3

Auch erwähnenswert: Sie können den Compiler davon überzeugen, dass Sie wirklich _do_ wollen 'x' als etwas mehr oder weniger wie' Bar # X' mit dem unglaublich hässlichen 'val x: bX forSome {val b: Bar} =" a ": bX für etwas {val b: Bar}'. –

+0

Scheint wie seltsamer Anwendungsfall. Warum willst du das machen? –

Antwort

2

Das Programm kompiliert durch diese implizite Konvertierung im Kontext fügt hinzu:

implicit def f(x: Bar#X): Foo#X = x 

Da diese implizite Konvertierung für jeden F <: Foo korrekt ist, frage ich mich, warum der Compiler nicht von selbst tut.

1

Sie können auch:

trait Foo { 
    type X 
} 
trait Bar extends Foo { 
    type X = String 
} 
class BarImpl extends Bar{ 
    def getX:X="hi" 
} 
def baz[F <: Foo, T <: F#X](clz:F, x: T): Unit = { println("baz worked!")} 
val bi = new BarImpl 
val x: Bar#X = bi.getX 
baz(bi,x) 

aber:

def baz2[F <: Foo, T <: F#X](x: T): Unit = { println("baz2 failed!")} 
baz2(x) 

nicht mit:

test.scala:22: error: inferred type arguments [Nothing,java.lang.String] do not conform to method baz2's type parameter bounds [F <: this.Foo,T <: F#X] 
baz2(x) 
^ 
one error found 

ich im Grunde denken, F <: Foo sagt dem Compiler, der F muss sein ein Untertyp von Foo, aber wenn es ein X bekommt, weiß es nicht, welche Klasse Ihr insbesondere X kommt von. Ihr X ist nur eine Zeichenfolge und verwaltet keine Informationen, die auf "Bar" verweisen.

Beachten Sie, dass:

def baz3[F<: Foo](x : F#X) = {println("baz3 worked!")} 
baz3[Bar]("hi") 

auch funktioniert. Die Tatsache, dass Sie ein val x definiert haben: Bar # X = ??? bedeutet nur, dass ??? ist auf das beschränkt, was Bar # X zur Compilierzeit sein könnte ... der Compiler weiß, dass Bar # X String ist, also ist der Typ von x nur ein String, der sich von keinem anderen String unterscheidet.

+0

Ja, der Compiler sieht nur den 'String' und verliert den 'Foo'-Kontext, obwohl er den Typ mit' Bar # X' anstelle von 'String' annotiert hat. Meine einzige Lösung ist bisher, immer über "Foo" zu abstrahieren und "Bar" am Ende zu liefern. Aber das ist wirklich ein Problem für mich, da es einschränkt, was ich tun kann "baz" ... (in meinem Fall ist "baz" auch ein implizites) – betehess

+0

Richtig. Man könnte es sich so vorstellen: Typ X ist ein Mitglied von Foo, ähnlich wie bei anderen statischen vs Member-Variablen in Java. Der Versuch, ohne eine Instanz darauf zuzugreifen, ist wie der Versuch, auf eine Membervariable ohne Instanz zuzugreifen. – Brian

+0

In: 'Merkmal HasType {Typ X} Klasse Foo (Wert t: HasType) {Typ A = t.X}'. Du könntest viele 'Foo's mit verschiedenen' A's haben, jede abhängig von dem bestimmten übergebenen 't' (Instanz von' HasType'). Der Typ ist dynamisch und muss irgendwie angegeben werden. Foo # A im Beispiel hier sagt Ihnen oder dem Compiler nicht mehr über 'A', als Person.age würde Sie über das Alter einer Personeninstanz informieren. Eine Instanz "t" liefert die Typinformation. – Brian