2012-05-24 41 views
18

Observé una diferencia en la inferencia de tipo de Scala cuando se aplica a def y val.Inferencia de tipo diferente para `def` y` val` en Scala

Usando def, puedo definir un método nulory abstracto const devolviendo algún valor de tipo Int => Int. Al implementar const con un literal de la función, no necesita fuente de alimentación de un tipo de parámetro, como se puede deducir por el compilador:

trait D { 
    def const: Int => Int 
} 
object D extends D { 
    def const = i => i + 1 
} 

Esto está bien. (En el lado negativo, se está creando una nueva instancia de función para cada acceso a D.const.)

Consideremos ahora una construcción análoga utilizando val:

trait V { 
    val const: Int => Int 
} 
object V extends V { 
    val const = i => i + 1 
} 

Esto no se compilará, en su defecto con

error: missing parameter type 
    val const = i => i + 1 
      ^

¿Por qué?

+1

No es una buena idea usar abstract 'val''s en los rasgos (puede conducir a NPE sorprendentes). Mantenga una 'def' en el rasgo y anule con un 'val' en la implementación. O comience con un 'vago val x: X = sys.error (" anularme ")' en el rasgo. – ron

+0

¿Podría tener algo que ver con la coincidencia de patrones? Todo entre el signo 'def' y el signo igual '=' se trata como un identificador, mientras que todo entre el 'val' y el signo igual' = 'se trata como un patrón. Simplemente una conjetura salvaje ... – agilesteel

+0

@ron, gracias por mencionarlo, pero de una manera u otra, la pregunta permanece, ya que el uso de un 'def' en el rasgo y un' val' en la implementación lleva a la misma cuestión. – knuton

Respuesta

1

A partir de Scala 2.9.1, esto es “como speced”. Citando a Martin Odersky desde SI-2742:

Para los métodos, los tipos de retorno en métodos abstractos heredados se toman como tipo esperado del lado derecho. Para los valores no existe tal regla. Entonces esta sería una solicitud de mejora de especificaciones, como yo lo veo.

El ticket es de baja prioridad y se ha mantenido sin cambios desde su primera discusión a finales de 2009, por lo que parece poco probable que cambie pronto.

2

Si se construye este código con la opción -Xprint todo, se verá que:

abstract trait V extends scala.AnyRef { 
<stable> <accessor> def const: Int => Int 
}; 

final object V extends java.lang.Object with V with ScalaObject { 

def this(): object V = { 
    V.super.this(); 
() 
}; 

private[this] val const: <error> => <error> = ((i: <error>) => i.+(1)); 
<stable> <accessor> def const: <error> => <error> = V.this.const 
} 

Así se produce el error en la creación de val privado y de acceso. compilador intenta evaluar el valor afectado a la const val antes de crear el accessor def const.

si mira la configuración de val definida en rasgo, verá que la creación de val privada se ha desactivado porque es solo una definición para el descriptor de acceso def def.

Creo que el tipo de inferencia con la definición anterior (en rasgo o superclase) ocurrió solo cuando intentó crear el descriptor de acceso, no para evaluar un valor.

Y para el último const def, el tipo se basa únicamente en el sector privado [esto] val tipo const: error => Error

+0

Sí ¡Eso debe ser! ¿Puedes juzgar si esto es según la especificación o simplemente un error en el compilador? – knuton

+0

No considero que como un error, creo que en este caso, no tiene ninguna razón para usar un valor porque el valor se define solo en tiempo de compilación, no en tiempo de ejecución (no hay ninguna referencia en el valor de un parámetro o una variable previamente definida, es solo una definición de una función anónima. Pero puede ser que Martin Odersky vea esta publicación y nos dé más explicaciones;) – fp4me

+0

Bueno, esto es solo un ejemplo de juguete. En el código del que lo obtuve, el cuerpo de la función anónima utiliza un nombre del ámbito adjunto. Creo que el hecho de que esta instancia específica se pueda resolver de manera diferente, no debe liberar al compilador. – knuton

Cuestiones relacionadas