2011-12-03 1 views
3

Suite à my other question à propos reduceLeft, la signature de reduceLeft sur Seq estComment l'inférenceur de type fonctionne-t-il sur reduceLeft?

def reduceLeft [B >: A] (f: (B, A) ⇒ B): B 

et nous pouvons l'appeler avec des expressions telles que

List(1,2,3,4) reduceLeft (_ + _) 

Dans cet exemple A est Int, donc reduceLeft attend un Function2[B >: Int, Int, B] . Indépendamment de la façon dont reduceLeft fonctionne (ce qui n'est pas pertinent), comment le type inferencer sait-il que B a une méthode +, alors qu'elle pourrait être de type Any?

Répondre

1

Si B>: X et le compilateur sait X mais ne peut pas résoudre B il suppose simplement B = X.

Il est un peu pratique car il n'a que deux options pour B et un seul est connu. Donc absente sachant quelle super classe elle suppose que B est X. Vous pouvez tester le processus de prise de décision des compilateurs avec le code suivant.

class Y { 
    def bar(y:Y) = this 
} 
case class X(i: Int) extends Y { 
    def foo(x:X)=X(i+x.i) 
} 
val t = new Y bar X(7) 
val t2 = X(8) bar X(7) 
val res = List(X(1),X(2),X(3)) reduceLeft { _ foo _ } 
val res2 = List(X(1),X(2),X(3)) reduceLeft { _ bar _ } // will not compile 
4

Je pense que la section 6.26.4 Inférence de type local du genre de spec explique ce qui se passe. Le compilateur recherchera un type optimal. Lorsque le paramètre type est contravariant, le type choisi sera maximal (dans ce cas Any) et sinon (invariant ou covariant) minimal (dans ce cas Int).

Il y a quelques exemples que je ne peux pas vraiment rapporter à reduceLeft.

Ce que j'ai remarqué est la conclusion semble se produire avant de regarder la fonction anonyme passé:

scala> List(1,2).reduceLeft[Any](_.toString + _) 
res26: Any = 12 

Mais si je n'aiderai pas le type inferencer:

scala> List(1,2).reduceLeft(_.toString + _) 
<console>:8: error: type mismatch; 
found : java.lang.String 
required: Int 
       List(1,2).reduceLeft(_.toString + _) 

Modifier , Je me trompe la fonction anonyme est prise en compte, cela fonctionne:

List(1,2).reduceLeft((_:Any).toString + (_:Any).toString) 

Il y a un compilateur l'option -Ytyper-debug que vous pouvez exécuter sur:

List(1,2).reduceLeft(_+_) 

Il vous montrera que en quelque sorte le compilateur suppose que le type attendu de la fonction anonyme est (Int, Int) => Int, il procède de vérifier la _ + _ contre et réussit, puis déduit B comme Int. Snippet ici:

typed immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B 
adapted immutable.this.List.apply[Int](1, 2).reduceLeft: [B >: Int](f: (B, Int) => B)B to ?, undetparams=type B 
typing ((x$1, x$2) => x$1.$plus(x$2)): pt = (Int, Int) => Int: undetparams=, 
// some time later 
typed ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int 
adapted ((x$1: Int, x$2: Int) => x$1.+(x$2)): (Int, Int) => Int to (Int, Int) => Int, 
typed immutable.this.List.apply[Int](1, 2).reduceLeft[Int](((x$1: Int, x$2: Int) => x$1.+(x$2))): Int 

Je ne sais pas pourquoi, en l'absence de type ascription la fonction anonyme est supposée être (Int, Int) => Int.

Questions connexes