9

Tengo un código que no puedo comportarme de la manera que me gustaría. Tengo una clase definida de la siguiente manera (simplificada para esto):Tipo de argumento de cierre de parámetro múltiple no deducido

class Behaviour[T](private val rule: Time => T) { 
    def map1[U, V](behaviour: Behaviour[U], func: (T, U) => V): Behaviour[V] = { 
    new Behaviour(time => func(this.at(time), behaviour.at(time))) 
    } 
} 

Cuando se juega un poco con esta clase intenté algo que pensé que sería trivial:

val beh = Behaviour(time => 5) 
val beh2 = Behaviour(time => 5) 
beh.map1(beh2, (a, b) => a + b) 

Para el último línea de recibo el siguiente error:

<console>:13: error: missing parameter type 
      beh.map1(beh2, (a, b) => a + b) 
          ^

puedo, por supuesto, especificar los tipos de parámetros de cierre y funciona correctamente, pero por qué no escribe trabajo inferencia aquí? Por supuesto, también podría especificar los tipos genéricos para la función (ver a continuación ambas soluciones).

Pensé que Scala llevó a cabo un 'escaneo' para inferir tipos y vería beh2 y pasó a la función y supone U aquí para que sea Int. ¿Hay alguna manera de solucionarlo sin especificar los tipos de los parámetros de entrada (para el cierre o los genéricos)?

EDIT: Ejemplos de las dos correcciones que tengo:

beh.map1[Int, Int](beh2, (a, b) => a + b) 
beh.map1(beh2, (a, b : Int) => a + b) 

Respuesta

19

Consulte this scala-debate thread para una discusión de lo que está pasando aquí. El problema es que la inferencia de tipo de Scala ocurre por lista de parámetros, no por parámetro.

Como señala Josh Suereth en ese hilo, hay una buena razón para el enfoque actual. Si Scala tenía inferencia de tipo por parámetro, el compilador no podría inferir un límite superior entre los tipos en la misma lista de parámetros. Considere lo siguiente:

trait X 
class Y extends X 
class Z extends X 

val y = new Y 
val z = new Z 

def f[A](a: A, b: A): (A, A) = (a, b) 
def g[A](a: A)(b: A): (A, A) = (a, b) 

f(y, z) funciona exactamente como es de esperar, pero g(y)(z) da una coincidencia de tipos, ya que en el momento en que el compilador llega a la segunda lista de argumentos que ya ha elegido Y como el tipo de A.

4

Una de las maneras de solucionar este problema es definir múltiples listas de argumentos. Por lo que su método map1 se definiría así:

def map1[U, V](behaviour: Behaviour[U])(func: (T, U) => V): Behaviour[V] = ... 

y se puede utilizar de esta manera:

beh.map1(beh2)((a, b) => a + b) 
beh.map1(beh2)(_ + _) 

no estoy completamente seguro de qué tipo de inferencia no funciona en su caso, pero cree que tiene algo que ver con el uso del parámetro de tipo U. Lo está usando dos veces, para el primer y segundo argumento. Probablemente sea demasiado complicado para que el compilador lo resuelva. En el caso de 2 listas de argumentos, U se inferiría durante la compilación de la primera lista de argumentos, y la segunda lista de argumentos usará el tipo ya inferido.

+0

¡Muchas gracias que funciona muy bien! Aceptaré la respuesta si ninguno puede venir con una explicación de lo que está sucediendo en el día siguiente. – seadowg

Cuestiones relacionadas