ejemplos clases function scala methods implicit-conversion

function - clases - Eta-expansión entre métodos y funciones con métodos sobrecargados en Scala



clases en scala (1)

Me gustaría entender por qué la expansión eta (§6.26.5) no funciona para métodos sobrecargados. Por ejemplo, si tengo dos métodos siguientes:

def d1(a: Int, b: Int) {} def r[A, B](delegate: (A, B) ⇒ Unit) {}

Puedo hacer esto:

r(d1)

Pero, cuando se sobrecarga r , ya no funcionará:

def r[A, B](delegate: (A, B) ⇒ Unit) {} def r[A, B, C](delegate: (A, B, C) ⇒ Unit) {} r(d1) // no longer compiles

y tengo que convertir explícitamente el método en una función parcialmente aplicada:

r(d1 _)

¿Hay alguna manera de lograr seguir con la conversión explícita?

def r[A, B](delegate: (A, B) ⇒ Unit) {} def r[A, B, C](delegate: (A, B, C) ⇒ Unit) {} def d1(a: Int, b: Int) {} def d2(a: Int, b: Int, c: Int) {} r(d1) // only compiles with r(d1 _) r(d2) // only compiles with r(d2 _)

Hay una pregunta algo similar, pero no está completamente explicada.


Implícito es el término correcto, y la sección es 6.26.2 en la especificación, y esta debe ser una pregunta duplicada (o al menos uno pensaría: esto es un comportamiento estable).

La pregunta vinculada también responde que el tipo esperado debe ser una función.

Me arriesgaré y diré que cuando se sobrecarga, la aplicabilidad se socava porque no hay un tipo esperado (6.26.3, infame). Cuando no está sobrecargado, se aplica 6.26.2 (expansión eta) porque el tipo del parámetro determina el tipo esperado. Cuando está sobrecargado, el arg se escribe específicamente sin tipo esperado, por lo tanto, 6.26.2 no se aplica; por lo tanto, ninguna variante sobrecargada de d se considera aplicable.

De la resolución de sobrecarga 6.26.3

De lo contrario, deje S 1,. . . , S m sea el vector de tipos obtenidos escribiendo cada argumento con un tipo esperado indefinido.

Aquí están las "conversiones implícitas" (así llamadas) disponibles cuando nombra un método sin argumentos, como en r(d1) . El párrafo sobre la expansión eta se aplica aquí.

6.26.2 Conversiones de método

Las siguientes cuatro conversiones implícitas se pueden aplicar a métodos que no se aplican a una lista de argumentos.

Evaluación. Un método sin parámetros m de tipo => T se convierte siempre en tipo T al evaluar la expresión a la que m está vinculado.

Aplicación implícita. Si el método solo toma parámetros implícitos, los argumentos implícitos se pasan siguiendo las reglas de §7.2.

Eta Expansion. De lo contrario, si el método no es un constructor, y el tipo esperado pt es un tipo de función (Ts) ⇒ T, eta-expansion (§6.26.5) se realiza en la expresión e.

Solicitud vacía De lo contrario, si e tiene el tipo de método () T, se aplica implícitamente a la lista de argumentos vacía, produciendo e ()

Más explicación después del chequeo verde ...

El siguiente ejemplo demuestra preferir la aplicación a la expansión eta en presencia de sobrecarga. Cuando eta-expansion no se aplica, la "aplicación vacía" es la última implícita para probar en 6.26.2. En otras palabras, cuando se sobrecarga (lo cual es confuso y lo suficientemente malo a la vista), es natural tomar f como f() según el principio de acceso uniforme, pero no es natural o extraño tomar f como f _ menos que Estoy bastante seguro de que se espera un tipo de función.

scala> object Bar { | def r(f: () => Int) = 1 | def r(i: Int) = 2 | } defined module Bar scala> def f() = 4 f: ()Int scala> Bar.r(f) res4: Int = 2 scala> Bar.r(f _) res5: Int = 1

Los candidatos para la resolución de sobrecarga son pre-seleccionados por "forma". La prueba de forma encapsula la intuición de que eta-expansion nunca se usa porque args se tipean sin un tipo esperado. Este ejemplo muestra que eta-expansion no se utiliza incluso cuando es "la única forma de que la expresión escriba check".

scala> object Bar { | def bar(f: () => Int) = 1 | def bar(is: Array[Int]) = 2 | } defined object Bar scala> def m() = 7 m: ()Int scala> m _ res0: () => Int = <function0> scala> Bar.bar(m) <console>:10: error: overloaded method value bar with alternatives: (is: Array[Int])Int <and> (f: () => Int)Int cannot be applied to (Int) Bar.bar(m) ^

Cualquiera que lea hasta aquí tendrá curiosidad acerca de un problema relacionado con estas dos conversiones .