静态"extend"记录数据类型,无需间接寻址麻烦



我目前正在使用一个三级流程,我需要一些信息来访问和更新流程。信息也是三级的,这样,一个级别的进程可能需要在其级别和更高级别访问/更新信息。

type info_0 = { ... fields ... }
type info_1 = { ... fields ... }
type info_2 = { ... fields ... }

fun0会用info_0做一些事情,然后将其与info_1一起传递给fun1,然后取回结果info_0并继续,用另一个info_1调用另一个fun1。同样的情况也发生在较低级别。


我目前的代表有

type info_0 = { ... fields ... }
type info_1 = { i0: info_0; ... fields ... }
type info_2 = { i1: info_1; ... fields ... }

fun2,更新info_0变得非常混乱:

let fun2 (i2: info_2): info_2 =
  {
    i2 with
      i1 = {
        i2.i1 with
          i0 = update_field0 i2.i1.i0
      }
  }

更简单的是:

type info_0 = { ... fields ... }
type info_1 = { ... fields ... }
type info_2 = { ... fields ... }
type info_01 = info_0 * info_1
type info_012 = info_0 * info_1 * info_2
let fun2 (i0, i1, i2): info_012 =
  (update_field0 i0, i1, i2)

最后一个解决方案看起来不错吗?

没有更好的解决方案来解决这类问题?(例如,我可以编写一个可以处理更新field0的函数,无论是处理info_0info_1还是info_2(

OCaml 模块会有所帮助吗?(例如,我可以在Sig1中包含Sig0...

您需要的是一种更新嵌套不可变数据结构的惯用方法。我不知道OCaml中的任何相关工作,但是Scala/Haskell中有一些可用的技术,包括Zippers,TreerewriteFunctional Lenses

更新嵌套结构的更简洁方法

是否有用于更新嵌套数据结构的 Haskell 惯用语?

对于 OCaml 的后代 F#,功能性镜头提供了一个不错的解决方案。因此,镜片是这里最相关的方法。您可以从此线程中获得使用它的想法:

更新嵌套的不可变数据结构

因为 F# 记录语法与 OCaml 几乎相同。

编辑:

正如@Thomas在他的评论中提到的,这里有一个完整的OCaml镜头实现。特别是,gapiLens.mli是我们感兴趣的。

您似乎希望能够将更复杂的值视为更简单的值。 这(或多或少(是OO模型的本质。 除非我真的需要它,否则我通常会尽量避免使用 OCaml 的 OO 子集,但它似乎确实满足了您在这里的需求。 您将有一个对应于 info_0 的基类。 类info_1将是info_0的子类,而info_2将是info_1的子类。 无论如何,这值得考虑。

正如 Jeffrey Scofield 所建议的那样,您可以使用类来节省使用和更新时间的麻烦:使 info_1 成为派生类和子类型,info_0 ,等等。

class info_1 = object
  inherit info_0
  val a_1_1 : int
  …
  method update_a_1_1 v = {<a_1_1 = v>}
end
…
let x : info_1 = new info_1 … in
let y = x#update_a_1_1 42 in
…

这种直接对象方法的缺点是,如果更新任何字段,则会复制对象中的所有数据;您无法在xy之间共享info_0片段。

您可以使用对象并保留设计中的共享行为和记录,但定义时的样板和恒定的运行时开销会变得更大。

class info_1 = object
  val zero : info_0
  method get_a_0_1 = zero#get_a_0_1
  method update_a_0_1 = {<zero = zero#update_a_0_1>}
  val a_1_1 : int
  method get_a_1_1 = a_1_1
  method update_a_1_1 v = {<a_1_1 = v>}
end
let x : info_1 = new info_1 … in
let y = x#update_a_1_1 42 in
…

最新更新