是否可以保证记忆化的一致性?
Is it possible to garantee the consistency of memoization?
我想构建一个带有记忆功能的模块 I
。类型 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
。保持一致意味着 "the mutable properties should either be None
or be Some v
where v
represents the current property of the content".
例如,下面的代码试图直接修改 属性 是被模块 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 *)
一种改进方法是在模块 I
中添加 set
,并始终在 { 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 *)
有谁知道任何现有的反思或解决方案面对由记忆引起的这种不一致?如果我们总是在[=56=的地方使用set
,并且在Content.t
中不允许可变组件,我们是否可以保证任何代码场景的100%一致性?
嗯,你可以使用 private
您的代码如下所示:
module type C = sig
type t = private { i: int; mutable j: int }
val new_c : int -> int -> t
val is_cool : t -> bool
end
module Content : C = struct
type t = { i: int; mutable j: int }
let new_c = ...
let is_cool (x: t) : bool = ...
end
module I : sig
type t
val new_i : int -> int -> 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 new_i = ...
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
...
如果你尝试写:
let c = {i = 3; j = 3};;
它会拒绝你回答:
Error: Cannot create values of the private type Content.t
我想构建一个带有记忆功能的模块 I
。类型 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
。保持一致意味着 "the mutable properties should either be None
or be Some v
where v
represents the current property of the content".
例如,下面的代码试图直接修改 属性 是被模块 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 *)
一种改进方法是在模块 I
中添加 set
,并始终在 { 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 *)
有谁知道任何现有的反思或解决方案面对由记忆引起的这种不一致?如果我们总是在[=56=的地方使用set
,并且在Content.t
中不允许可变组件,我们是否可以保证任何代码场景的100%一致性?
嗯,你可以使用 private
您的代码如下所示:
module type C = sig
type t = private { i: int; mutable j: int }
val new_c : int -> int -> t
val is_cool : t -> bool
end
module Content : C = struct
type t = { i: int; mutable j: int }
let new_c = ...
let is_cool (x: t) : bool = ...
end
module I : sig
type t
val new_i : int -> int -> 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 new_i = ...
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
...
如果你尝试写:
let c = {i = 3; j = 3};;
它会拒绝你回答:
Error: Cannot create values of the private type Content.t