Search code examples
functional-programmingnestedocamlsmlrecords

Statically "extend" a record-ish data type without indirection hassle


I am currently working with a three-level process for which I need some information to flow being accessed and updated. The information is also three-leveled, in such a way that a process at one level may need to access/update information at its level and at higher levels.

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

fun0 will do some stuff with an info_0, then pass it to fun1 along with an info_1, then get back the resulting info_0 and proceed, calling another fun1 with another info_1. The same happens at the lower level.


My current representation has

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

In fun2, updating info_0 get pretty messy:

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

Something simpler would be:

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)

Does the last solution look good?

Is there an even better solution to this kind of problem? (for instance, one where I could write a function that can handle updating a field0, no matter whether it's dealing with a info_0, info_1 or info_2)

Would OCaml modules help? (I could include a Sig0 inside Sig1 for instance...)


Solution

  • What you need is an idiomatic way of updating nested immutable data structures. I don't know any relevant work in OCaml, but there are a few techniques available in Scala/Haskell including Zippers, Tree rewriting, and Functional lenses:

    Cleaner way to update nested structures

    Is there a Haskell idiom for updating a nested data structure?

    For F#, a descendant of OCaml, functional lenses gives a nice solution. Therefore, lenses is the most relevant approach here. You can get the idea of using it from this thread:

    Updating nested immutable data structures

    since F# record syntax is almost the same as that of OCaml.

    EDIT:

    As @Thomas mentioned in his comment, there is a complete implementation of lenses in OCaml available here. And particularly, gapiLens.mli is of our interest.