2013-08-07 11 views
5

Ich versuche zu verstehen, warum können nicht die Scala-Compiler die folgende Einschränkung auf einem Pfad abhängigen Typen ableiten:Scala - Untere Grenze Inferenz in pfadabhängigen Arten

trait MyTrait 
class MyTraitImpl extends MyTrait 
trait MyTrait2[A <: MyTrait] { 
    type MyTraitType = A 
} 
class MyTrait2Impl[A <: MyTrait] extends MyTrait2[A] 

val obj: MyTrait2[_] = new MyTrait2Impl[MyTraitImpl] 

def myMethod[A <: MyTrait](t2: MyTrait2[A]) = println("Hi!") 

myMethod[obj.MyTraitType](obj) 
// <console>:14: error: type arguments [obj.MyTraitType] do not conform to method myMethod's type parameter bounds [A <: MyTrait] 
//    myMethod[obj.MyTraitType](obj) 

Für mich intuitiv, MyTraitType kann nichts anderes sein als eine Unterklasse von MyTrait, da die Grenze direkt an A in MyTrait2 ist. Wenn ja, kannst du mir ein Beispiel geben oder mich darauf hinweisen, wo dieses Code-Snippet falsch ist?

Wenn dies eine Scala-Compiler-Einschränkung ist, kann mir jemand einen Weg zeigen, dies mit dem Typsystem zu erreichen? Beachten Sie, dass:

  • Ich habe keinen MyTrait Gegenstand haben, auch nicht myMethod ein empfangen;
  • Ich brauche nicht myMethod zu wissen, die konkrete Art von A; alles, was es wissen muss, ist, dass A es ein Subtyp von MyTrait ist und dass t2 auf A parametriert ist;
  • Der Unterstrich in obj ist absichtlich; wo ich myMethod rufe, kenne ich den konkreten Typ von A nicht (oder sonst wäre es kein Problem);
  • Ich bevorzuge Lösungen, wo ich myMethod nicht ändern muss. verwenden Einschränkungen für Typ-Member

Antwort

3

Sie sollten nur statt Schranken für Typ-Parameter in MyTrait2 Erklärung:

trait MyTrait 
class MyTraitImpl extends MyTrait 
trait MyTrait2 { // Remove [A <: MyTrait] 
    type MyTraitType <: MyTrait // add <: MyTrait 
} 
class MyTrait2Impl[A <: MyTrait] extends MyTrait2 { type MyTraitType = A } 

val obj: MyTrait2 = new MyTrait2Impl[MyTraitImpl] 

def myMethod[A <: MyTrait](t2: MyTrait2{ type MyTraitType = A }) = println("Hi!") 

myMethod[obj.MyTraitType](obj) 

Sie werden einen Kompilierungsfehler auf falschen Typen bekommen, genauso wie erwartet:

scala> val otherObj: MyTrait2 = new MyTrait2Impl[MyTraitImpl] 
otherObj: MyTrait2 = [email protected] 

scala> myMethod[obj.MyTraitType](otherObj) 
<console>:15: error: type mismatch; 
found : otherObj.type (with underlying type MyTrait2) 
required: MyTrait2{type MyTraitType = obj.MyTraitType} 
       myMethod[obj.MyTraitType](otherObj) 
             ^

Proof funktioniert es mit List[MyTrait2]:

scala> for { 
    | obj <- List[MyTrait2](
    |   new MyTrait2Impl[MyTraitImpl], 
    |   new MyTrait2Impl[MyTraitImpl] 
    |   ) 
    | } myMethod[obj.MyTraitType](obj) 
Hi! 
Hi! 
+0

So funktioniert es zwar, aber es ist eine Art Workaround für den Unterstrich in der ursprünglichen Frage.In Ihrer Lösung funktioniert der Aufruf, weil der Compiler den konkreten Typ "A" in "obj" kennt, während er in meinem Beispiel explizit unterdrückt wurde. Stellen Sie sich vor, ich hätte eine 'List [MyTrait2]' und möchte 'myMethod' mit einem der Elemente aufrufen; Es wird nicht mit beiden Lösungen funktionieren. Meiner Meinung nach sollte es meiner Meinung nach auch so sein, denn es gibt konkrete Grenzen ('<: MyTrait') für welche Typen man' A' nehmen kann. –

+0

@RuiGoncalves: Meine Lösung arbeitet mit 'List [MyTrait2]' ganz gut. – senia

+0

@ RuiGonçalves: Ich habe einen Beweis zu meiner Antwort hinzugefügt, siehe Update. – senia

1

Wenn Sie wirklich die allgemeinen Parameter behalten wollen (ich zustimme, würden abstrakte Typen in diesem Fall schöner sein), können Sie Folgendes tun:

den Platzhalter Typen obj einschränken:

val obj: MyTrait2[_ <: MyTrait] = new MyTrait2Impl[MyTraitImpl] 

rufen sie myMethod ohne Typ-Parameter und lassen sie die Compiler Abbildung es aus:

myMethod(obj) 

Dies ist nichts mehr als zu helfen, den Compiler ein wenig mit dem Typinferenz aus. Ihre Argumentation über MyTrait2#MyTraitType ist natürlich richtig.

Eine andere Lösung (in der gleichen Kategorie gehen) sinkt die Art der obj:

val obj = new MyTrait2Impl[MyTraitImpl] 

Aber natürlich, dass nicht in Ihrem realen Anwendungsfall zutreffen könnte.

Verwandte Themen