我知道你不能这样做,但想要明白为什么。
module M : sig
type 'a t
val call : 'a t -> 'a option
end = struct
type 'a t
let state : ('a t -> 'a option) ref = ref (fun _ -> None)
let call : ('a t -> 'a option) = fun x -> !state x
end
结果:
Error: Signature mismatch:
Modules do not match:
sig
type 'a t
val state : ('_a t -> '_a option) ref
val call : '_a t -> '_a option
end
is not included in
sig
type 'a t
val call : 'a t -> 'a option
end
Values do not match:
val call : '_a t -> '_a option
is not included in
val call : 'a t -> 'a option
为什么抽象类型在这里不兼容?
我的直觉告诉我它与早期绑定和后期绑定有关,但我正在寻找类型系统在这里做什么的确切描述。
答案 0 :(得分:5)
查看它的一种方法是,您的字段state
不能具有您赋予它的多态值,因为可变值不能是多态的。引用最多是单形的(如类型变量的'_a
表示法所示)。
如果您只是尝试在顶层声明类似的引用,您会看到相同的效果:
# let lfr: ('a list -> 'a option) ref = ref (fun x -> None);;
val lfr : ('_a list -> '_a option) ref = {contents = <fun>}
类型变量'_a
表示某些尚未确定的单一类型。
引用不能是多态的原因是它不健全。如果你允许引用被推广(多态),那么很容易产生非常错误的程序。 (实际上,这通常意味着崩溃和核心转储。)
本文开头讨论了稳健性问题:Jacques Garrigue, Relaxing the Value Restriction(当我忘记事情是如何工作的时候,我会定期提到这一问题。)
<强>更新强>
我认为你想要的是&#34;排名2多态&#34;。即,您需要一个类型为多态的字段。只要您声明类型,您就可以在OCaml中实现此功能。通常的方法是使用记录类型:
# type lfrec = { mutable f: 'a. 'a list -> 'a option };;
type lfrec = { mutable f : 'a. 'a list -> 'a option; }
# let x = { f = fun x -> None };;
val x : lfrec = {f = <fun>}
# x.f ;;
- : 'a list -> 'a option = <fun>
以下代码使用lfrec
代替参考编译:
module M : sig
type 'a t
val call : 'a t -> 'a option
end = struct
type 'a t
type lfrec = { mutable f: 'a. 'a t -> 'a option }
let state: lfrec = { f = fun _ -> None }
let call : ('a t -> 'a option) = fun x -> state.f x
end