如何在OCaml中使协变量可观察 [英] How to make a covariant observable in OCaml

查看:80
本文介绍了如何在OCaml中使协变量可观察的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为值包装,以允许调用者注册自己以获取有关其的通知.这是一些(有效的)代码:

I'm trying to make a wrapper for values that allows callers to register themselves for notifications about it. Here's some (working) code:

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

但是,这似乎效率很低.每个排队的观察者都是一个新的闭包,它对事物有自己的引用.仅将用户的回调排队,并在notify中添加x,例如

However, this seems inefficient. Each queued watcher is a new closure with its own reference to the thing. It would make more sense to queue just the user's callback and add the x in notify, e.g.

  ... = 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证明它是安全的?

But having the 'a as part of the queue type means that 'a t is no longer covariant. I understand why this happens, but does anyone have a solution? i.e. how can I show OCaml that it's safe in this case?

推荐答案

您可以移动捕获位置:

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

这篇关于如何在OCaml中使协变量可观察的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆