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.