Факторинг присвоений типа продукта в OCaml

Я обычно не удовлетворен с написанием кода как это:

let load_record_field cursor gets geti gett a = function
  | 0x01 -> let c, s = gets () in (a.a_record_uuid <- s; `More_record c)
  | 0x02 -> let c, s = gets () in (a.a_group <- s; `More_record c)
  | 0x03 -> let c, s = gets () in (a.a_title <- s; `More_record c)
  | 0x04 -> let c, s = gets () in (a.a_username <- s; `More_record c)
  | 0x07 -> let c, t = gett () in (a.a_creation_time <- t; `More_record c)
  .
  .
  .
  | 0xFF -> `End_of_record cursor

Я минимизировал шаблон, но я задавался вопросом, было ли волшебство OCaml, которое позволило бы мне полностью устранить его.

5
задан mbac32768 26 February 2010 в 14:34
поделиться

3 ответа

Это предельно просто: просто используйте замыкание для настройки и напишите функцию для абстрагирования шаблона

let load_record_field cursor gets geti gett a x =
  let frob get set =
     let (c,s) = get () in
     set s; `More_record c
  in
  function
  | 0x01 -> frob gets (fun s -> a.a_record_uuid <- s)
  | 0x02 -> frob gets (fun s -> a.a_group <- s)
  | 0x03 -> frob gett (fun s -> a.a_title <- s)
  ...

и так далее.

Вы можете сделать это еще лучше, если воспользуетесь пакетом макросов, таким как Fieldslib Джейн Стрит. Это генерирует первоклассные поля вместе с автоматически сгенерированными сеттерами и получателями . Это означало бы, что вам не нужно каждый раз конструировать замыкание вручную.

2
ответ дан 15 December 2019 в 06:24
поделиться

Самое короткое, что вы могли бы избежать в теории, это:

frobnicate (function 
| 0x01 -> gets , a_record_uuid 
| 0x02 -> gets , a_group 
  ...
)

Конечно, OCaml помешает вам, потому что на 1 ° нет "указателя на member "конструкции в Objective Caml, поэтому вам придется написать fun как -> a.a_record_uuid <- s вместо a_record_uuid (по крайней мере) и 2 ° систему типов не полностью поддерживает количественную оценку существования, поэтому возвращаемый тип функции не может быть ожидаемым:

существует 'a. int -> (unit -> record * 'a) * (' a -> record -> unit)

Я думаю, вы могли бы решить 1 °, указав функции для установки значений в записи, если вы это сделаете достаточно часто:

type complex = { re : int ; im : int }
let re r c = { c with re = r }
let im r c = { c with im = i }

Я думаю, это немного неортодоксально, но обычно окупается позже, потому что я склонен использовать их в большинстве функциональных ситуаций. Вы можете создать эквивалент в императивном стиле или согласиться с накладными расходами функции (она добавляет только около 20 символов).

Как или 2 °, это может быть решено путем сокрытия экзистенциального квантификатора в функции:

let t e read write = let c, x = read () in write x e ; `More_record c

Это позволит вам перейти к:

let t = t a in
match 
  | 0x01 -> t gets a_record_uuid 
  | 0x02 -> t gets a_title
  ...

Я не удивлюсь, если CamlP4 поддерживает какой-то сахар для присваивания функции. Между тем, если вы используете ссылки вместо изменяемых полей, вы можете сократить это (поскольку ссылки являются значениями первого класса, а поля - нет):

let t read reference = let c, x = read () in reference := x ; `More_record c

match 
  | 0x01 -> t gets a.a_record_uuid
  ...
1
ответ дан 15 December 2019 в 06:24
поделиться

Я обычно не удовлетворен написанием такого кода

A признак хорошего вкуса, если вы спросите меня: -)


Я не знаю никакой магии, но я думаю, что лучший способ - разделить шаблон:

  1. Одна функция-установщик шаблонов для каждого изменяемого поля. Может быть полезно в разных контекстах.

  2. Одна структура данных для отображения целочисленных кодов на «что делать с этим полем»

Вы можете реализовать свой сканер записей, используя таблицу вместо функции. Пример, который наводит на размышления, представлен ниже. Разница между gets и gett здесь очень важна. Далее

  • sf означает «строковое поле»
  • tf означает «поле времени»
  • eor означает «конец записи»

Я составил таблицу и поиск для моего примера; используйте любую эффективную структуру данных.

let sf set a c =     let c, s = gets() in (set a s; `More_record c)
let tf set a c =     let c, s = gett() in (set a t; `More_record c)
let eor    a c =     `End_of_record c

let fields = tabulate
  [ 0x01, sf a_record_uuid
  ; 0x02, sf a_group
  ; ...
  ; 0x07, tf a_creation_time
  ; ...
  ]

let load_record_field cursor gets geti gett a code = lookup fields code cursor a
0
ответ дан 15 December 2019 в 06:24
поделиться
Другие вопросы по тегам:

Похожие вопросы: