lunes, 24 de agosto de 2009

Covariancia y contravariancia (II)

Las listas y otros contenedores

Las transformaciones de tipos más simples, y las que veremos aquí, son las de construcción de nuevos tipos. Vamos a empezar por un constructor de tipos simple. Pasar de un tipo T a una lista de este tipo L(T).

Entonces, una lista de muebles lm de es de tipo L(M) y una lista de sillas ls de tipo L(S). ¿Puedo usar una donde la otra? ¿Y al revés?

Está claro que puedo usar una lista de sillas cuando me pidan una lista de muebles, pero no al revés. Así que:

S    <: M
L(S) <: L(M)

Llegando a la conclusión de que esta transformación de tipos es covariante. En general esto pasa con todos los contenedores.

Los argumentos de las funciones

Las funciones son otros constructores de tipos. Si tengo un tipo T1 y otro tipo T2, el tipo de una función que toma un argumento T1 y devuelve un valor T2 será  T1 -> T2.

¿Son las funciones covariantes o contravariantes? Empezaremos por una función simple a ver qué ocurre.

fs :: S -> Int  //fs es una función que toma una silla y devuelve un Int
fm :: M -> int  //fm es una función que toma un mueble y devuelve un Int

Tengo las siguientes posibilidades:

fm(m) //OK: Mismo tipo
fs(m) //Uso fs donde usaba fm: Mal, requiero una silla y me dan un mueble
fs(s) //OK: Mismo tipo
fm(s) //Uso fm donde usaba fs: Bien, requiero un mueble y me dan una silla

Es decir, que puedo usar "fm" por "fs" pero no al revés.

S        <: M
S -> Int :> M -> int

Así que las funciones son contravariantes si nos fijamos en sus argumentos.

Los retornos de las funciones

Si hacemos lo propio con los retornos, ¿qué pasará?

fs :: Int -> S  //fs es una función que toma un Int y devuelve una silla
fm :: Int -> M  //fm es una función que toma un Int y devuelve un M
s=fs(1) //OK mismo tipo
s=fm(1) //Mal, no todos los muebles son sillas
m=fm(1) //OK mismo tipo
m=fs(1) //Bien, una silla es un mueble

Entonces

S        <: M
S -> Int <: M -> int

Así que las funciones son covariantes si nos fijamos en sus retornos.

Es importante destacar a partir de ahora que la variancia dependerá de en qué argumento del constructor de tipos nos fijamos. Así pues diremos que el constructor de funciones -> es contravariante en el tipo de su izquierda (el argumento) y covariante en el de la derecha (el retorno).

En la siguiente parte veremos qué ocurre si usamos funciones que tomen o devuelvan funciones. Lo que se denominan funciones de alto nivel.

0 comentarios:

Publicar un comentario