Types 如何在OCaml中使协变可观测

Types 如何在OCaml中使协变可观测,types,ocaml,Types,Ocaml,我正在尝试为值制作一个包装器,允许调用方注册自己以获得关于它的通知。以下是一些(工作)代码: 然而,这似乎效率低下。每个排队的观察者都是一个新的闭包,它有自己对事物的引用。只对用户的回调进行排队,并在notify中添加x,这将更有意义,例如 ... = struct type 'a t = { obj : 'a; watchers : ('a -> unit) Queue.t } let make x = { obj = x

我正在尝试为值制作一个包装器,允许调用方注册自己以获得关于它的通知。以下是一些(工作)代码:

然而,这似乎效率低下。每个排队的观察者都是一个新的闭包,它有自己对事物的引用。只对用户的回调进行排队,并在
notify
中添加
x
,这将更有意义,例如

  ... = struct
    type 'a t = {
      obj : 'a;
      watchers : ('a -> unit) Queue.t
    }

    let make x = {
      obj = x;
      watchers = Queue.create ()
    }

    let watch fn x =
      x.watchers |> Queue.add fn

    let notify x =
      x.watchers |> Queue.iter (fun fn -> fn x.obj)
  end

但是将
'a
作为队列类型的一部分意味着
'a t
不再是协变的。我理解为什么会发生这种情况,但有人有解决办法吗?i、 e.在这种情况下,我如何向OCaml证明它是安全的?

您可以改变捕获位置:

module Thing :
  sig
    type +'a t
    val make : 'a -> 'a t
    val watch : ('a -> unit) -> 'a t -> unit
    val notify : 'a t -> unit
  end = struct
    type 'a t = {
      obj : 'a;
      watch : ('a -> unit) -> unit;
      notify : unit -> unit;
    }

    let make x =
      let queue = Queue.create () in
      let obj = x in
      let watch f = Queue.add f queue in
      let notify () = Queue.iter (fun f -> f x) queue in
      { obj; watch; notify; }

    let watch fn x = x.watch fn
    let notify x = x.notify ()
  end
如果你想感觉真正经济:

    let make x =
      let queue = Queue.create () in
      let obj = x in
      let rec watch f = Queue.add f queue
      and notify () = Queue.iter (fun f -> f x) queue in
      { obj; watch; notify; }

我肯定错过了一些明显的关于为什么它会安全的东西——如果
'b
'a
的一个子类型,如果您将
'b t
强制转换为
'a t
,那么是否有人可以将
'a
通知期望a
'b
的观察者?模块的公共API不允许您向观察者发送任意
'a
,只允许发送原始值(实际上是a
'b
)。从另一个角度来看,低效版本是安全的,而高效版本具有相同的行为。“let rec”版本的优点是什么?它们将共享关闭环境。
    let make x =
      let queue = Queue.create () in
      let obj = x in
      let rec watch f = Queue.add f queue
      and notify () = Queue.iter (fun f -> f x) queue in
      { obj; watch; notify; }