如何在OCaml中进行协变可观察

时间:2014-01-27 09:55:33

标签: types 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;
      watchers : (unit -> unit) Queue.t
    }

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

    let watch fn x =
      x.watchers |> Queue.add (fun () -> fn x.obj) 

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

let () =
  let x = Thing.make (`Int 4) in
  Thing.watch (fun (`Int d) -> Printf.printf "Observed %d\n" d) x;
  let x = (x :> [`Int of int | `None] Thing.t) in
  Thing.notify x

然而,这似乎效率低下。每个排队的观察者都是一个新的闭包,它有自己的东西参考。仅排队用户的回调并在x中添加notify更有意义,例如

  ... = 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不再是协变的。我理解为什么会这样,但有没有人有解决方案?即如何向OCaml展示在这种情况下它是安全的?

1 个答案:

答案 0 :(得分:2)

您可以改变捕获地点:

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; }