2016-12-05 5 views
5
trait A { 
    type T 
    def test(t: T): Unit 
} 

case class B[S <: A](a: S, t : S#T) { 
    def test() = a.test(t) // Error: type mismatch; 
    // found : B.this.t.type (with underlying type S#T) 
    // required: B.this.a.T 
} 

Ai-je tort de m'attendre à ce que ce qui précède soit compilé? Est-ce que mon code peut être corrigé?Pourquoi scala n'unifie pas ce type lambda avec le type sous-jacent?

Répondre

8

compilateur n'a pas de preuves suffisantes que S#T peut être utilisé comme argument pour test dans l'exemple concret.

Considérons cet exemple hypotecical pour compilateur scala affaibli

trait A2 extends A{ 
    type T <: AnyRef 
} 

class A3 extends A2{ 
    override type T = Integer 

    def test(t: Integer): Unit = println(t * 2) 
} 

Alors B[A2] devrait accepter instance de A3 ainsi que tout ce qui est <: AnyRef tandis que A3 besoins exactement Integer pour sa propre test mise en œuvre

Vous pouvez attraper le béton tapez dans la définition de B, pour vous assurer quel type sera utilisé

case class B[S <: A, ST](a: S {type T = ST}, t: ST) { 
    def test() = a.test(t) 
} 
+2

Mais 'B [A2] (nouveau A3()," ")' ne compile pas si ... vouliez-vous dire que ce serait avec mon code? Sinon, votre solution est exactement ce dont j'avais besoin. – JbGi

+0

Ha, un truc intelligent! Je ne savais pas qu'on peut utiliser le paramètre de type sur la vue de la main droite dans le raffinement. – Haspemulator

+0

@JbGi Oui, j'ai beaucoup pensé comment le démontrer avec un exemple de compilation, mais le compilateur scala est assez restrictif ici. Alors gardons ceci juste un exemple imaginaire pour un compilateur imaginaire ressemblant à scala, qui pourrait compiler la source originale. – Odomontois

2

je pourrais venir avec encodages (supprimé les paramètres de type de simplification):

scala> :paste 
// Entering paste mode (ctrl-D to finish) 

def test0(a: A)(t : a.T) = a.test(t) 

abstract class B{ 
    val a: A 
    val t: a.T 
    def test = a.test(t) 
} 

// Exiting paste mode, now interpreting. 

test0: (a: A)(t: a.T)Unit 
defined class B 

Ce d'autre part ne fonctionnait pas avec les classes de cas arguments (ni classes' pour cette question).

L'une des raisons pour lesquelles votre encodage ne fonctionnerait pas:

scala> def test1(a: A)(t : A#T) = a.test(t) 
<console>:12: error: type mismatch; 
found : t.type (with underlying type A#T) 
required: a.T 
     def test1(a: A)(t : A#T) = a.test(t) 

L'important est required: a.T (contre A#T). La méthode d'essai dans A ne prend pas de T, il faut T this.T, ou en d'autres termes, le T appartenant à un cas particulier de A.

+0

Merci! ça m'a aidé à mieux comprendre! Donc 'a.T' est ce qu'on appelle un 'type dépendant du chemin'? – JbGi

+0

Exactement, c'est un type dépendant du chemin. – pedrofurla

1

Au lieu d'une projection de type que vous pouvez utiliser le type dépendant a.T:

trait A { 
    type T 
    def test(t: T): Unit 
} 

case class B[S <: A](a: S)(t : a.T) { 
    def test() = a.test(t) 
}