2010-07-23 18 views
11

tengo this class en Scala:¿Por qué la inferencia tipo Scala falla aquí?

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

Ahora,

"aaa".tap(_.trim) 

no compila, dando el error

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

¿Por qué no se infiere el tipo como String? A partir del error, parece que la conversión implícita se dispara (de lo contrario, el error estaría en la línea de "tap no es miembro de la clase String"). Y parece que la conversión debe ser Tapper[String], lo que significa que el tipo del argumento es String => Unit (o (String => Unit)*).

Lo interesante es que si hago un comentario ya sea de tap definiciones, entonces se compila.

Respuesta

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.

Ambos sobrecargas de tap son potencialmente aplicables (basado en la 'forma' de los argumentos, que representa la aridad y constructores de tipos FunctionN).

Así que los typer procede como lo haría con:

val x = _.trim 

y falla.

Un algoritmo más inteligente podría tomar el límite superior mínimo del tipo de parámetro correspondiente de cada alternativa y usarlo como el tipo esperado. Pero esta complejidad realmente no vale la pena, IMO. La sobrecarga tiene muchos casos de esquina, esto no es más que otro.

Pero hay un truco que puedes usar en este caso, si realmente necesita una sobrecarga que acepta un solo parámetro:

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

Excelente idea, gracias! Pensé que tendría que nombrarlos de manera diferente. –

+3

¡Te estás convirtiendo rápidamente en el nuevo Daniel, Jason! –

+2

@oxbow Aún mejor, a menudo cita las especificaciones, lo cual es algo bueno. –

Cuestiones relacionadas