2016-08-19 6 views
3

Pourquoi le compilateur traite t1 ++ t2 comme liste [Tout]? La concaténation de deux listes de type S doit renvoyer une liste de type S uniquement.Paramètres du sous-type générique Scala

// compiles 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 

// type mismatch; found List[Any] required S 
def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 

Répondre

3

Voici ce que je crois se passe. Tout d'abord, Sest le même type partout, il n'y a pas de magie ici. Regardons le premier exemple:

scala> def cons[S <: List[Any]](t1: S, t2: S): S = if(t1.isEmpty) t1 else t2 
cons: [S <: List[Any]](t1: S, t2: S)S 

scala> cons(List(1), List(2.0)) 
res21: List[AnyVal] = List(2.0) 

Comme vous pouvez le voir Scala le plus proche a constaté à juste ancêtre commun pour Int et Double, et il est AnyVal. Donc, dans ce cas S est AnyVal.

Maintenant, nous allons essayer ceci:

scala> def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 
<console>:11: error: type mismatch; 
found : List[Any] 
required: S 
     def cons[S <: List[Any]](t1: S, t2: S): S = t1 ++ t2 
                ^

Quel est le problème? Ce message d'erreur signifie que le résultat de ++ est en quelque sorte List[Any] au lieu de prévu S. Pourquoi donc? Regardons la signature ++ (simplifié, est plus vraie signature):

def ++[B >: A](other: List[B]): List[B] = ??? 

Alors Scala doit trouver l'ancêtre le plus proche de A et le paramètre de type réel de other. Le seul problème est: il doit trouver B au point où vous définissez cons, pas où vous l'appliquez plus tard (B n'est pas un paramètre libre pour cons). La seule information est la limite supérieure de S, et c'est List[Any], donc la seule solution sûre pour B au point de définition de cons est la plus générique, c'est-à-dire Any. Ce qui signifie que le résultat de ++ est List[Any], et il ne rentre pas S. D'où l'erreur.

Troisième exemple:

scala> def cons[S <: Any](t1: List[S], t2: List[S]): List[S] = t1 ++ t2 
cons: [S](t1: List[S], t2: List[S])List[S] 

scala> cons(List(1), List(1.0)) 
res0: List[AnyVal] = List(1, 1.0) 

Pourquoi ce travail? Ici, les deux t1 et t2 ont exactement le même type, peu importe ce que S est (et S peut être inféré plus tard). Donc B == S et le résultat est List[S]. Encore dans ce cas particulier S est l'ancêtre commun le plus proche de Int et Double.

+0

Dans votre troisième exemple, vous dites "Ici, t1 et t2 ont exactement le même type, peu importe ce que S est". Comment cela peut-il être? Liste [Int] Liste [Double] sont de différents types arent ils? – Samar

+0

Je pense que la confusion se produit parce que différents types peuvent être initialement passés en arguments, mais le type de S sera l'ancêtre commun le plus proche des différents types passés. – Samar

+0

Je seconde les questions de Samar. Je ne vois pas de différence entre '(t1: S, t2: S)' et '(t1: liste [S], t2: liste [S])'. Dans les deux cas t1 et t2 ont exactement le même type. Bien sûr, vous pouvez passer 'List [Int]' et 'List [String]', mais dans ce cas, l'ancêtre le plus proche est 'List [Any]', donc c'est ce que 'S' est. Pouvez-vous donner un exemple pour 'def contre [S <: List [Any]] (t1: S, t2: S): S' où nous aurions une erreur (d'où le compilateur ne le permet pas)? Quoi que vous passiez, le compilateur déduira que 'S' est l'ancêtre le plus proche et ** c'est ce qui sera retourné **. Type inféré 'S'. Ça devrait marcher avec 'S', non? – slouc

1

List[Any] ++ List[Any] est un List[Any] ne garantit pas S, un sous-type de List[Any] aussi ont la propriété de S ++ S est un S, de sorte que le complier retombe à List[Any].

+0

Je pense que la réponse de Victor clarifie vraiment ce qui se passe. Je ne suis pas encore sûr à 100%, mais je pense que lorsque les deux arguments actuels t1/t2 sont passés à la méthode des inconvénients, les types des arguments passés sont remontés à leur ancêtre commun le plus proche et c'est le type de S. le type passé de l'argument. – Samar