Подтвердить что ты не робот

Статически "расширять" тип данных записи без косвенных сбоев

В настоящее время я работаю с трехуровневым процессом, для которого мне нужна некоторая информация для потока, к которой обращаются и обновляются. Информация также является трехуровневой, таким образом, что процессу на одном уровне может потребоваться доступ/обновление информации на ее уровне и на более высоких уровнях.

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

fun0 будет делать некоторые вещи с помощью info_0, а затем передать его в fun1 вместе с info_1, а затем вернуть результирующий info_0 и продолжить, вызывая другой fun1 с другим info_1. То же самое происходит на более низком уровне.


Мое текущее представление имеет

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_0, info_1 или info_2)

Могут ли модули OCaml помочь? (Я мог бы включить Sig0 внутри Sig1, например...)

4b9b3361

Ответ 1

Вам нужен идиоматический способ обновления вложенных неизменяемых структур данных. Я не знаю никакой соответствующей работы в OCaml, но есть несколько методов, доступных в Scala/Haskell, включая Молнии, Переписывание дерева, Функциональные линзы:

Более чистый способ обновления вложенных структур

Есть ли иконка Haskell для обновления вложенной структуры данных?

Для F #, потомок OCaml, функциональные линзы дает хорошее решение. Поэтому линзы - это наиболее подходящий подход. Вы можете получить представление об использовании этого потока:

Обновление вложенных неизменяемых структур данных

поскольку синтаксис записи F # почти такой же, как у OCaml.

EDIT:

Как отметил в своем комментарии @Thomas, в OCaml имеется полная реализация объективов в здесь. И особенно, gapiLens.mli представляет нас.

Ответ 2

Кажется, вам нужна способность рассматривать более сложную ценность, как если бы она была более простой. Это (более или менее) сущность модели OO. Обычно я стараюсь избегать подмножества OO OCaml, если я действительно не нуждаюсь в нем, но он, похоже, отвечает вашим потребностям. У вас будет базовый класс, соответствующий info_0. Класс info_1 будет подклассом info_0, а info_2 будет подклассом info_1. Во всяком случае, стоит подумать.

Ответ 3

Как предложил Джеффри Скофилд, вы можете сэкономить время на использование и время обновления с помощью классов: make 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
…

Недостатком этого прямого подхода является то, что все данные в объекте копируются, если вы обновляете любое из полей; вы не можете делиться фрагментами info_0 между x и y.

Вы можете использовать объекты и сохранять поведение совместного использования в своем дизайне с записями, но шаблоны во время определения и постоянные накладные расходы во время работы становятся больше.

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
…