ocaml covariance contravariance invariance

Varianza OCaml(+ ''a,-''a) e invariancia



covariance contravariance (2)

Creo que la explicación más sencilla es que un valor mutable tiene dos operaciones intrínsecas: captador y definidor, que se expresan mediante acceso de campo y sintaxis de conjunto de campos:

type ''a t = {mutable data : ''a} let x = {data = 42} (* getter *) x.data (* setter *) x.data <- 56

Getter tiene un tipo ''at -> ''a , donde ''a variable de tipo aparece en el lado derecho (por lo que impone una restricción de covarianza), y el establecedor tiene el tipo ''at -> ''a -> unit donde la variable de tipo Ocurre a la izquierda de la flecha, que impone una restricción contravariante. Entonces, tenemos un tipo que es tanto covariante como contravariante, lo que significa que el tipo variable ''a es invariante.

Después de escribir este pedazo de código

module type TS = sig type +''a t end module T : TS = struct type ''a t = {info : ''a list} end

Me di cuenta de que necesitaba info para ser mutable.

Yo escribí, entonces:

module type TS = sig type +''a t end module T : TS = struct type ''a t = {mutable info : ''a list} end

Pero, sorpresa,

Type declarations do not match: type ''a t = { mutable info : ''a list; } is not included in type +''a t Their variances do not agree.

Oh, recuerdo haber oído hablar de la varianza . Era algo sobre covarianza y contravarianza . Soy una persona valiente, encontraré mi problema solo!

¡Encontré estos dos artículos interesantes ( here y here ) y entendí!

puedo escribir

module type TS = sig type (-''a, +''b) t end module T : TS = struct type (''a, ''b) t = ''a -> ''b end

Pero entonces me preguntaba. ¿Cómo es que los tipos de datos mutables son invariantes y no solo covariantes?

Quiero decir, entiendo que una ''A list puede considerarse como un subtipo de una lista (''A | ''B) list porque mi lista no puede cambiar. Lo mismo para una función, si tengo una función de tipo ''A | ''B -> ''C ''A | ''B -> ''C puede considerarse como un subtipo de una función de tipo ''A -> ''C | ''D ''A -> ''C | ''D porque si mi función puede manejar ''A y ''B '' s, puede manejar solo ''A '' s y si solo devuelvo ''C '' s puedo esperar ''C ''D o ''D '' s (pero yo solo obtener ''C '' s).

¿Pero para una matriz? Si tengo una ''A array no puedo considerarla como una ''A array (''A | ''B) array porque si modifico un elemento en la matriz poniendo una ''B , mi tipo de matriz es incorrecto porque realmente es una (''A | ''B) array y ya no un ''A array . Pero ¿qué pasa con una (''A | ''B) array como una ''A array ? Sí, sería extraño porque mi matriz puede contener ''B pero, extrañamente, pensé que era lo mismo que una función. Tal vez, al final, no entendí todo, pero quería poner mis pensamientos al respecto aquí porque me tomó mucho tiempo entenderlo.

TL; DR :

persistente: +''a

funciones: -''a

mutable: invariante ( ''a )? ¿Por qué no puedo forzar que sea -''a ?


Su tipo t básicamente permite dos operaciones: obtener y configurar. Informalmente, get tiene el tipo ''at -> ''a list y la configuración tiene el tipo ''at -> ''a list -> unit . Combinado, ''a ocurre tanto en una posición positiva como en una negativa.

[EDITAR: La siguiente es una (con suerte) una versión más clara de lo que escribí en primer lugar. Lo considero superior, así que borré la versión anterior.]

Intentaré hacerlo más explícito. Supongamos que sub es un subtipo adecuado de super y witness es algún valor de tipo super que no es un valor de tipo sub . Ahora sea f : sub -> unit una función que falla en el valor witness . La seguridad de tipo está ahí para garantizar que el witness nunca pase a f . Mostraré con el ejemplo que la seguridad de tipos falla si se permite que uno trate a un subtipo como un subtipo de super t , o al revés.

let v_super = ({ info = [witness]; } : super t) in let v_sub = ( v_super : sub t ) in (* Suppose this was allowed. *) List.map f v_sub.info (* Equivalent to f witness. Woops. *)

Por lo tanto, no se puede permitir tratar el super t como un subtipo de sub t . Esto muestra la covarianza, que ya sabías. Ahora por contravarianza.

let v_sub = ({ info = []; } : sub t) in let v_super = ( v_sub : super t ) in (* Suppose this was allowed. *) v_super.info <- [witness]; (* As v_sub and v_super are the same thing, we have v_sub.info=[witness] once more. *) List.map f v_sub.info (* Woops again. *)

Por lo tanto, tratar el sub t como un subtipo de super t tampoco se puede permitir, lo que demuestra la contravarianza. Juntos, ''at es invariante.