使用 ref 单元格进行动态绑定



我知道你不能这样做,但想确切地了解为什么。

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

为什么这里的抽象类型不兼容?

我的直觉告诉我,它与早期绑定与晚期绑定有关,但我正在寻找类型系统在这里所做的事情的确切描述。

一种看待它的方法是,你的字段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),放宽价值限制(当我忘记事情是如何运作时,我会定期提到)。

更新

我认为你想要的是"秩 2 多态性"。 即,你想要一个类型为多态的字段。实际上,只要您声明类型,就可以在 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

最新更新