2010-07-23 16 views
11

Ho this class a Scala:Perché l'inferenza di tipo Scala fallisce qui?

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) 
} 

Ora,

"aaa".tap(_.trim) 

non si compila, dando l'errore

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

Perché il tipo non viene dedotto come String? Dall'errore sembra che la conversione implicita funzioni (altrimenti l'errore sarebbe lungo le linee di "tap non è un membro della classe String"). E sembra che la conversione deve essere a Tapper[String], il che significa che il tipo dell'argomento è String => Unit (o (String => Unit)*).

La cosa interessante è che se io commento sia di tap definizioni, allora si compila.

risposta

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.

Entrambi i sovraccarichi di tap sono potenzialmente applicabili (basata sulla 'forma' degli argomenti, che rappresenta l'arietà e costruttori di tipo funzione personalizzataN).

Così il ricavato typer come farebbe con:

val x = _.trim 

e non riesce.

Un algoritmo più intelligente potrebbe richiedere il minimo superiore del tipo di parametro corrispondente di ogni alternativa e utilizzarlo come tipo previsto. Ma questa complessità non ne vale la pena, IMO. Il sovraccarico ha molti casi d'angolo, questo è solo un altro.

Ma c'è un trucco si può usare in questo caso, se si ha realmente bisogno di un overload che accetta un unico parametro:

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

Ottima idea, grazie! Pensavo che avrei dovuto nominarli diversamente. –

+3

Stai rapidamente diventando il nuovo Daniel, Jason! –

+2

@oxbow Ancora meglio, cita spesso le specifiche, che è una buona cosa. –