我正在尝试为值包装,使调用者可以注册自己的通知。这是一些(有效的)代码:
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证明它是安全的? 最佳答案
您可以转移捕获位置:
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; }
关于types - 如何在OCaml中使协变量可观察,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/21377727/