Статически «расширять» тип данных записи без проблем с косвенностью - PullRequest
13 голосов
/ 21 февраля 2012

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

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, например ...)

Ответы [ 3 ]

10 голосов
/ 21 февраля 2012

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

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

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

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

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

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

EDIT:

Как отметил @Thomas в своем комментарии, в OCaml доступна полная реализация линз здесь . В частности, gapiLens.mli представляет наш интерес.

5 голосов
/ 21 февраля 2012

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

3 голосов
/ 22 февраля 2012

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