2010-07-23 17 views
11

Je this class à Scala:Pourquoi l'inférence de type Scala échoue-t-elle ici?

object Util { 
    class Tapper[A](tapMe: A) { 
    def tap(f: A => Unit): A = { 
     f(tapMe) 
     tapMe 
    } 

    def tap(fs: (A => Unit)*): A = { 
     fs.foreach(_(tapMe)) 
     tapMe 
    } 
    } 

    implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap) 
} 

Maintenant,

"aaa".tap(_.trim) 

ne compile pas, donnant l'erreur

error: missing parameter type for expanded function ((x$1) => x$1.trim)

Pourquoi pas le type déduit que String? De l'erreur, il semble que la conversion implicite se déclenche (sinon l'erreur serait sur le modèle de "tap n'est pas un membre de la classe String"). Et il semble que la conversion doit être à Tapper[String], ce qui signifie que le type de l'argument est String => Unit (ou (String => Unit)*).

La chose intéressante est que si je commente soit de tap définitions, alors il compile.

Répondre

17

6.26.3 Overloading Resolution

One first determines the set of functions that is potentially applicable based on the shape of the arguments

...

If there is precisely one alternative in B, that alternative is chosen.

Otherwise, let S1, . . . , Sm be the vector of types obtained by typing each argument with an undefined expected type.

Les deux tap de surcharges sont potentiellement applicables (en fonction de la « forme » des arguments, qui représente l'arité et les constructeurs de type fonction personnaliséeN).

Ainsi le produit de typer comme il le ferait avec:

val x = _.trim 

et échoue.

Un algorithme plus intelligent pourrait prendre la limite supérieure du type de paramètre correspondant de chaque alternative, et l'utiliser comme type attendu. Mais cette complexité n'en vaut pas vraiment la peine, IMO. La surcharge a beaucoup de cas de coin, ceci n'en est qu'un autre.

Mais il y a un truc que vous pouvez utiliser dans ce cas, si vous avez vraiment besoin d'une surcharge qui accepte un seul paramètre:

object Util { 
    class Tapper[A](tapMe: A) { 
    def tap(f: A => Unit): A = { 
     f(tapMe) 
     tapMe 
    } 

    def tap(f0: A => Unit, f1: A => Unit, fs: (A => Unit)*): A = { 
     (Seq(f0, f1) ++ fs).foreach(_(tapMe)) 
     tapMe 
    } 
    } 

    implicit def tapper[A](toTap: A): Tapper[A] = new Tapper(toTap) 

    "".tap(_.toString) 
    "".tap(_.toString, _.toString) 
    "".tap(_.toString, _.toString, _.toString) 
} 
+0

Excellente idée, merci! Je pensais que je devrais les nommer différemment. –

+3

Vous devenez rapidement le nouveau Daniel, Jason! –

+2

@oxbow Encore mieux, il cite souvent les spécifications, ce qui est une bonne chose. –