2012-04-27 13 views
6

Este problema surgió en un módulo que estoy escribiendo, pero he hecho un caso mínimo que muestra el mismo comportamiento.¿Por qué no funciona aquí la inferencia?

class Minimal[T](x : T) { 
    def doSomething = x 
} 

object Sugar { 
    type S[T] = { def doSomething : T } 
    def apply[T, X <: S[T]] (x: X) = x.doSomething 
} 

object Error { 
    val a = new Minimal(4) 
    Sugar(a) // error: inferred [Nothing, Minimal[Int]] does not fit the bounds of apply 
    Sugar[Int, Minimal[Int]](a) // works as expected 
} 

El problema es que el compilador arregla para averiguar el parámetro interior para Minimal (Int), pero a continuación, establece la otra ocurrencia de T-Nothing, lo que obviamente no coincide apply. Estos son definitivamente los mismos T, ya que la eliminación del primer parámetro hace que el segundo se queje de que T no está definido.

¿Hay alguna ambigüedad que signifique que el compilador no puede inferir el primer parámetro o es un error? ¿Puedo solucionar esto con gracia?

Más información: Este código es un ejemplo simple de un intento de azúcar sintáctico. El código original intenta hacer que |(a)| signifique el módulo de a, donde a es un vector. Claramente |(a)| es mejor que escribir |[Float,Vector3[Float]](a)|, pero desafortunadamente no puedo usar unary_| para hacerlo más fácil.

el error real:

inferred type arguments [Nothing,Minimal[Int]] do not conform to method apply's type parameter bounds [T,X <: Sugar.S[T]]

Respuesta

9

Esto no es un error del compilador de Scala, pero sin duda es una limitación de la inferencia de tipo de Scala. El compilador desea determinar el límite en X, S[T], antes de resolver X, pero el límite menciona la variable de tipo T que por lo tanto se corrige en Nothing y procede de allí. No vuelve a visitar T una vez que X se ha resuelto por completo ... actualmente, la inferencia de tipo siempre procede de izquierda a derecha en este tipo de caso.

Si su ejemplo representa con precisión su situación real, entonces no es una solución simple,

def apply[T](x : S[T]) = x.doSomething 

Aquí T se desprenderán de tal manera que se ajusta a MinimalS[T] directamente en lugar de a través de una variable de tipo acotada intermediario.

actualización

solución de Joshua también evita el problema de inferir el tipo T, pero de una manera completamente diferente.

def apply[T, X <% S[T]](x : X) = x.doSomething 

desugars a,

def apply[T, X](x : X)(implicit conv : X => S[T]) = x.doSomething 

Las variables de tipo T y X ahora se pueden resolver para independiente (porque T ya no se menciona en X 's unido).Esto significa que X se deduce como Minimal inmediatamente, y T se resuelve como parte de la búsqueda implícita de un valor de tipo X => S[T] para satisfacer el argumento implícito conv. conforms en scala.Predef fabrica valores de esta forma, y ​​en contexto garantizará que dado un argumento del tipo Minimal, T se deducirá como Int. Puede ver esto como una instancia de functional dependencies en funcionamiento en Scala.

+0

Sí, esta solución funciona bien en mi caso. También es más limpio que la solución de Joshua (lo siento Joshua!). ¿Puedes explicar por qué la solución de Joshua funciona, entonces? – Dylan

+0

Respuesta actualizada para dar una explicación de por qué la solución de Joshua también funciona. –

4

Hay algo de rareza con límites en tipos estructurales, intente utilizar una vista unido en S [T] en lugar.

def apply[T, X <% S[T]] (x: X) = x.doSomething funciona bien.

+1

Genial, esto funciona, pero seguramente no debería haber diferencia entre los enfoques? Tomar una vista en este caso es simplemente lanzar a una superclase, ¿no es así? ¿Esto significa que esta solución es solo una solución para un error en el compilador? – Dylan

+0

Ah, ahora veo, 'S' no es una superclase, es solo una vista (en cierto sentido). Por lo tanto, un límite de visualización es más apropiado, a pesar de que no se requiere en la mayoría de los casos. – Dylan

Cuestiones relacionadas