我想建立一個模塊I
與memoization。 I.t
類型包含一個真實的複雜內容c
,以及一些可變屬性(例如,mutable is_cool
)。該模塊提供外與函數來計算和獲取屬性(例如,is_cool
),這可能是昂貴的,這就是爲什麼可變屬性用於:是否可以保證記憶的一致性?
(*in i.ml *)
module Content = struct
type t = { i: int; mutable j: int }
let is_cool (x: t) : bool = ...
end
module I : sig
type t
val get_c: t -> Content.t
val is_cool: t -> bool
...
end = struct
type t = {
c : Content.t;
mutable is_cool : bool option;
mutable property_a : int option }
let get_c (x: t) -> Content.t = x.c
let is_cool (x: t) : bool =
match x.is_cool with
| Some r -> r
| None -> (* not yet calculated *)
let r = Content.is_cool x.c in
x.is_cool <- Some r;
r
end
...
擔心我有,是如何將模塊代碼I
和外部代碼,使得對於整個程序執行過程中任何類型爲I.t
的值,其可變屬性總是與其內容c
一致。一致意味着「可變屬性應該是None
或者是Some v
,其中v
代表內容的當前屬性」。
例如,下面的代碼很有誘惑力的直接修改屬性深受模塊I.t
的簽名禁止:
(* in main.ml *)
open I
let test (x: I.t) =
x.is_cool <- Some false
但是,它似乎是不容易的禁止這個代碼,從而改變內容:改善這種
(* in main.ml *)
let test (x: I.t) (i_new: int) =
let c = I.get_c x in
let c_new = { c with Content.i = i_new } in
let y = { x with c = c_new } in
(* now the content and the properties in y are likely to be inconsistent *)
一種方法是添加set
模塊I
中,始終在{ x with c = c_new }
位置使用set x c_new
:
(*in i.ml *)
let set (x: t) (c: Content.t) : t =
{ c = c; is_cool = None; property_a = None }
然而,仍然存在問題,例如,
1)它仍然是不可能從寫入{ x with c = c_new }
2)在Content.t
易變部件的變形例禁止人們(例如, mutable j: int
)也可以讓I.t
不一致:
(* in main.ml *)
let test (x: I.t) (j_new: int) =
let c = I.get_c x in
c.Content.j <- j_new;
(* now the content and the properties in x are likely to be inconsistent *)
有誰知道任何現有reflexi或解決方案面對這種由記憶造成的不一致?如果我們在「record with」的地方始終使用set
,並且不允許Content.t
中的可變組件,那麼我們能否保證100%的代碼場景的一致性?