2012-05-15 6 views
23

Creo que se puede definir la covarianza (al menos para los objetos) como "la capacidad de usar un valor de un (sub) tipo más estrecho en lugar de un valor de algún tipo (super) más amplio", y esa contravarianza es la exacta opuesto a esto.¿Por qué la función [-A1, ..., + B] no trata de permitir ningún supertipo como parámetro?

Aparentemente, las funciones de Scala son instancias de la función [-A1, ..., + B] para los tipos de parámetros contravariantes A1, etc. y el tipo de retorno covariante, B. Si bien es útil para subtipar funciones, no debería la definición anterior significa que puedo pasar cualquier supertipo como parámetros?

Por favor avise dónde me equivoco.

Respuesta

56

Covarianza y contravarianza son cualidades de la clase no cualidades de los parámetros . (Son cualidades que dependen de los parámetros, pero hacen afirmaciones acerca de la clase.)

Así, Function1[-A,+B] significa que una función que tiene superclases de A puede ser visto como una subclase de la función original.

Vamos a ver esto en la práctica:

class A 
class B extends A 
val printB: B => Unit = { b => println("Blah blah") } 
val printA: A => Unit = { a => println("Blah blah blah") } 

Ahora supongamos que usted requiere una función que sabe cómo imprimir un B:

def needsB(f: B => Unit, b: B) = f(b) 

Usted podría pasar en printB. Pero puede también pasar en printA, ya que también sabe cómo imprimir B s (¡y más!), Como si A => Unit fuera una subclase de B => Unit. Esto es exactamente lo que significa la contravarianza. ¡No significa que puede pasar Option[Double] en printB y obtener nada más que un error de tiempo de compilación!

(covarianza es el otro caso:. M[B] <: M[A] si B <: A)

+0

Gracias, eso fue muy claro. Intentar (re) definir: 'co/contra-varianza son propiedades que dictan la relación de subtipos entre tipos, sujetos a la naturaleza de la misma relación entre sus tipos de componentes'. Resumen, lo sé, pero prefiero tener una definición desprovista de ejemplos (aunque la tuya fue muy útil). – bjt38

+0

gracias por la A, ¿podría explicar por qué es + B en la Función1 [-A, + B] – liango

+0

@liango - Anexplicación por ejemplo: si devuelve un 'String' ciertamente devuelve un' Objeto', entonces '. .. => String' debe ser una subclase de '... => Object'. Eso es lo que significa '+ B'. –

4

Aquí hay dos ideas por separado. Uno está utilizando la subtipificación para permitir que se pasen argumentos más específicos a una función (llamada subsunción). El otro es cómo verificar la subtipificación en las funciones mismas.

Para comprobar el tipo de los argumentos de una función, solo tiene que comprobar que los argumentos dados son subtipos de los tipos de argumentos declarados. El resultado también tiene que ser un subtipo del tipo declarado. Aquí es donde realmente verificas la subtipificación.

El/covarianza contra de los parámetros & resultado único factor en cuando se quiere comprobar si una función dada tipo es un subtipo de otro tipo de función. Entonces, si un parámetro tiene el tipo Function[A1, ... ,B], entonces el argumento tiene que ser una función tipo Function[C1, ..., D] donde A1 <: C1 ... y D <: B.

Este razonamiento no es específico de Scala y se aplica a otros lenguajes tipados estáticamente con subtipado.

-1

covariante medios de conversión de más amplio (super) para más estrecho (sub).Por ejemplo, tenemos dos clases: una es animal (súper) y la otra es cat, luego usa covariante, podemos convertir animal en cat.

Contra-variante es todo lo contrario de covariante, lo que significa gato a animal.

Invariant significa que no puede convertir.

18

Esta pregunta es antigua, pero creo que una explicación más clara es invocar el Principio de sustitución de Liskov: todo lo que es cierto acerca de una superclase debería ser cierto para todas sus subclases. Debería poder hacer con un SubFoo todo lo que puede hacer con un Foo, y tal vez más.

Supongamos que tenemos Calico <: Gato <: Animal y Husky <: Perro <: Animal. Veamos un Function[Cat, Dog]. ¿Qué afirmaciones son ciertas sobre esto? Hay dos:

(1) se puede pasar en cualquier gato (por lo que cualquier subclase de gato)

(2) Puede llamar a cualquier método de perro en el valor devuelto

Lo mismo sucede con sentido Function[Calico, Dog] <: Function[Cat, Dog] maquillaje ? No, las afirmaciones que son verdaderas de la superclase no son verdaderas de la subclase, es decir, la declaración (1). No puedes pasar ningún gato a una función que solo tome gatos Calico.

¿Pero tiene sentido Function[Animal, Dog] <: Function[Cat, Dog]? Sí, todas las afirmaciones sobre la superclase son verdaderas de la subclase. Todavía puedo pasar a cualquier gato; de hecho, puedo hacer aún más que eso, puedo pasar a cualquier animal, y puedo llamar a todos los métodos de perro en el valor devuelto.

Así A <: B implica Function[B, _] <: Function[A, _]

Ahora bien, ¿Function[Cat, Husky] <: Function[Cat, Dog] sentido? Sí, todas las afirmaciones sobre la superclase son verdaderas de la subclase; Todavía puedo pasar un Cat, y todavía puedo llamar a todos los métodos Dog en el valor devuelto; de hecho, puedo hacer incluso más que eso, puedo llamar a todos los métodos de Husky sobre el valor devuelto.

¿Pero tiene sentido Function[Cat, Animal] <: Function[Cat, Dog]? No, las afirmaciones que son verdaderas de la superclase no son verdaderas de la subclase, es decir, la declaración (2). No puedo llamar a todos los métodos disponibles en Dog en el valor devuelto, solo los disponibles en Animal.

Así que con un Function[Animal, Husky] puedo hacer todo lo que puedo hacer con un Function[Cat, Dog]: puedo pasar cualquier Cat, y puedo llamar a todos los métodos Dog en el valor devuelto. Y puedo hacer aún más: puedo pasar otros animales, y puedo llamar a los métodos disponibles en Husky que no están disponibles en Dog. Entonces tiene sentido: Function[Animal, Husky] <: Function[Cat, Dog]. El primer parámetro de tipo puede ser reemplazado por una superclase, el segundo con una subclase.

+0

¿Cómo puedo verificar <: programáticamente con los ejemplos que me dio? –

Cuestiones relacionadas