OCaml variance (+'a, -'a) and invariance

Your type t basically allows two operations: getting and setting. Informally, getting has type 'a t -> 'a list and setting has type 'a t -> 'a list -> unit. Combined, 'a occurs both in a positive and in a negative position.

[EDIT: The following is a (hopefully) clearer version of what I wrote in the first place. I consider it superior, so I deleted the previous version.]

I will try to make it more explicit. Suppose sub is a proper subtype of super and witness is some value of type super which is not a value of type sub. Now let f : sub -> unit be some function which fails on the value witness. Type safety is there to ensure that witness is never passed to f. I will show by example that type safety fails if one is allowed to either treat sub t as a subtype of super t, or the other way around.

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. *)

So treating super t as a subtype of sub t cannot be allowed. This shows covariance, which you already knew. Now for contravariance.

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. *)

So, treating sub t as a subtype of super t cannot be allowed either, showing contravariance. Together, 'a t is invariant.


I think that the easiest explanation is that a mutable value has two intrinsic operations: getter and setter, that are expressed using field access and field set syntaxes:

type 'a t = {mutable data : 'a}

let x = {data = 42}

(* getter *)
x.data

(* setter *)
x.data <- 56

Getter has a type 'a t -> 'a, where 'a type variable occurs on the right-hand side (so it imposes a covariance constraint), and the setter has type 'a t -> 'a -> unit where the type variable occurs to the left of the arrow, that imposes a contravariant constraint. So, we have a type that is both covariant and contravariant, that means that type variable 'a is invariant.