写完这段代码后
module type TS = sig
type +'a t
end
module T : TS = struct
type 'a t = {info : 'a list}
end
我意识到我需要info
变得可变。
我写道,然后:
module type TS = sig
type +'a t
end
module T : TS = struct
type 'a t = {mutable info : 'a list}
end
但是,出乎意料,
Type declarations do not match:
type 'a t = { mutable info : 'a list; }
is not included in
type +'a t
Their variances do not agree.
哦,我记得听说过 variance 。它是关于协方差和逆变的东西。我是一个勇敢的人,我会独自找到我的问题!
我可以写
module type TS = sig
type (-'a, +'b) t
end
module T : TS = struct
type ('a, 'b) t = 'a -> 'b
end
但后来我想知道。为什么 mutable 数据类型是不变的而不仅仅是协变?
我的意思是,我了解'A list
可以被视为('A | 'B) list
的子类型,因为我的列表无法更改。对于函数来说,同样的事情,如果我有类型'A | 'B -> 'C
的函数,它可以被视为类型为'A -> 'C | 'D
的函数的子类型,因为如果我的函数可以处理'A
和{{1它只能处理'B
,如果我只返回'A
,我可以肯定期待'C
或'C
(但我会只得到'D
。)
但对于阵列?如果我有'C
我不能将它视为'A array
,因为如果我修改数组中放置('A | 'B) array
的元素,那么我的数组类型是错误的,因为它确实是一个'B
而不是('A | 'B) array
。但'A array
与('A | 'B) array
相比如何呢?是的,这很奇怪,因为我的数组可以包含'A array
但奇怪的是我认为它与函数相同。也许,最后,我并不理解所有事情,但我想在这里提出我的想法,因为我花了很长时间才理解它。
TL; DR :
持久性:
'B
功能:
+'a
mutable:invariant(
-'a
)?为什么我不能强迫它'a
?
答案 0 :(得分:16)
我认为最简单的解释是,一个可变值有两个内部操作:getter和setter,它们使用字段访问和字段集语法表示:
type 'a t = {mutable data : 'a}
let x = {data = 42}
(* getter *)
x.data
(* setter *)
x.data <- 56
Getter的类型为'a t -> 'a
,其中'a
类型变量出现在右侧(因此它强加了协方差约束),并且setter的类型为'a t -> 'a -> unit
,其中类型变量出现在箭头的左侧,这会产生逆变约束。所以,我们有一个协变和逆变的类型,这意味着类型变量'a
是不变的。
答案 1 :(得分:6)
您的类型t
基本上允许两种操作:获取和设置。非正式地,获取类型'a t -> 'a list
并且设置类型为'a t -> 'a list -> unit
。合并后,'a
同时出现在正面和负面位置。
[编辑:以下是我首先写的(希望)更清晰的版本。我认为它更优越,所以我删除了以前的版本。]
我会尽量让它更明确。假设sub
是super
的正确子类型,witness
是super
类型的某个值,它不是类型sub
的值。现在让f : sub -> unit
成为值witness
失败的函数。类型安全是为了确保witness
永远不会传递给f
。我将通过示例显示,如果允许将sub t
视为super t
的子类型,或者相反,则类型安全会失败。
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. *)
因此不能允许将super t
视为sub t
的子类型。这显示了您已经知道的协方差。现在是逆转。
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. *)
因此,也不允许将sub t
视为super t
的子类型,显示出相反的变化。 'a t
一起是不变的。