使用ref cell进行动态绑定

Nic*_*kiy 3 ocaml

我知道你不能这样做,但想要明白为什么.

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
Run Code Online (Sandbox Code Playgroud)

结果是:

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
Run Code Online (Sandbox Code Playgroud)

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

我的直觉告诉我它与早期绑定和后期绑定有关,但我正在寻找类型系统在这里做什么的确切描述.

Jef*_*eld 5

一种看待它的方法是你的字段state不能具有你赋予它的多态值,因为可变值不能是多态的.引用最多是单形的(由'_a类型变量的符号表示).

如果您只是尝试在顶层中声明类似的引用,您将看到相同的效果:

# let lfr: ('a list -> 'a option) ref = ref (fun x -> None);;
val lfr : ('_a list -> '_a option) ref = {contents = <fun>}
Run Code Online (Sandbox Code Playgroud)

类型变量'_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>
Run Code Online (Sandbox Code Playgroud)

以下代码使用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
Run Code Online (Sandbox Code Playgroud)