2010-09-08 17 views
11

En Scala, la varianza se puede definir con operadores de varianza como + y - en el argumento de tipo genérico. Por ejemplo, el tipo List es covariante en la biblioteca estándar.Al usar anotaciones de covarianza o límites genéricos en Scala

class List[+A] 

lo tanto una función con una lista covariante puede definirse así:

def foo[A](list : List[A]) 

También varianza puede ser emulada con los límites genéricos. Así también podemos escribir esta

def foo[A](list : List[_:< A]) 

por supuesto esto no tiene sentido, porque ya está list covariante. Pero el mismo truco podría hacerse para los tipos que no son covariantes. (como Stack). Por supuesto, también se podrían crear nuevos tipos a partir de la pila (herencia de la agregación) que sea covariante.

Así que mis preguntas:

  1. Cuando debe ser Utilizar límites genéricos para la varianza? ¿Y cuándo deberíamos crear un nuevo tipo de covariante?
  2. Los límites genéricos solo son útiles para la varianza, o pueden declarar más (conceptos de lenguaje).
  3. Si solo son útiles para la varianza, ¿son límites solo para la compatibilidad con Java?

THX de antemano :)

+1

Doblo esta pregunta: la varianza es la parte más dura de Scala y en realidad no la entiendo bastante bien. "Blah-blah covariant blah apareció en la posición contravariante bla-bla": p – Jeriho

+2

Entendí la varianza cuando miré las definiciones de algunos de los rasgos de la Función (Función1, Función2, etc.) Los rasgos de la función son todos covariantes en el tipo de salida y contravariante en los tipos de entrada (parámetro). Por ejemplo, una función de tipo 'Any => Unit' puede usarse donde se espera una función de tipo' String => Unit', porque la función 'Any => Unit' puede tomar una cadena como su entrada (porque puede tome algo como su entrada.) Esta es la razón por la cual los tipos de parámetros del método son una "posición contravariante". –

Respuesta

13

Si un tipo es naturalmente covariante o contravariante, debe declararlo así. Tus usuarios te lo agradecerán. La varianza del sitio de uso es mayormente debido a Java. Más precisamente, un tipo como Array[T <: Number] es tratada como una abreviatura para un tipo existencial:

ArrayBuffer[T] forSome { type T <: Number } 

tipos existenciales tienen una sintaxis bastante voluminoso en Scala. Eso es algo intencional, porque no recomendamos que los uses demasiado. ¿Cuándo necesitarías un tipo existencial?

  1. Para escribir el análogo de un tipo de Java con comodines, como List<? extends Number>.
  2. Para escribir el análogo de un tipo de raw de Java, como List.

En Java, los tipos crudos y los tipos de comodines no son lo mismo y ninguno es igual a un tipo existencial (aunque sabemos lo que no son, es bastante difícil decir exactamente qué son).Pero están lo suficientemente cerca de los existenciales en la práctica, por lo que Scala se sale con la tarea de asignarlos a este tipo de tipo.

6
  1. Al crear un nuevo tipo genérico, dicen Foo [T], que debe esforzarse para determinar si ese tipo es covariante, contravariant o invariante, y os lo Foo [ + T], Foo [-T] o Foo [T] respectivamente. Es cierto que esto puede ser un poco difícil. Sin embargo, libera al usuario de Foo de tomar esa decisión cada vez que necesita usar un Foo usando límites genéricos. En resumen: prefiere la varianza del sitio de declaración sobre la varianza del sitio de llamada cuando la varianza es una propiedad del tipo en sí.

BTW, el libro Programación en Scala de Martin Odersky, Lex Spoon y Bill Venners tiene algunas grandes diferencias sobre la varianza. Consulte el Capítulo 19 Tipo de parametrización.

Cuestiones relacionadas