Я хочу, чтобы построить модуль I
с memoization. Тип I.t
содержит реальное сложное содержимое c
и некоторые изменчивые свойства (например, mutable is_cool
). Модуль обеспечивает снаружи с функциями, чтобы вычислить и получить свойства (например, is_cool
), которые могут быть дорогостоящими, поэтому используются изменяемые свойства:Можно ли гарантировать согласованность memoization?
(*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
, и всегда использовать set x c_new
на месте { x with c = 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 на или решение сталкиваются с этой несогласованностью, вызванной воспоминаниями? Если мы всегда используем set
на месте «записи с» и не разрешаем изменчивые компоненты в Content.t
, можем ли мы гарантировать 100% консистенцию для любого сценария кода?