Циклические зависимости и модульный дизайн - PullRequest
1 голос
/ 17 апреля 2019

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

Ниже приведено очень упрощенное изображение файлов в проекте, которые представляют интерес:

ast.ml (на самом деле нет интерфейса, я не слишком заинтересован в копировании всего типа)

type loc = string * (int * int) * (int * int)
and id = string * loc
and decl = 
  | Decl_Func of decl_func
and decl_func = {
  df_Name: id;
  mutable df_SymTab: sym_tab option;
}
(* goes on for about 100 more types *)

symtab.mli

type t
type symbol =
  | Sym_Func of Ast.decl_func

val lookup_by_id: Ast.id -> symbol

(в будущем будут добавлены другие файлы)

В C я просто сделал бы таблицу символов указателем и объявил бы ее вперед. Задача решена. К сожалению, это невозможно в OCaml.

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

Как бы я решил эту проблему, сохранив несколько модульный дизайн?

1 Ответ

1 голос
/ 17 апреля 2019

Вы не первый, кто сталкивается с этой проблемой, и существует множество различных решений в зависимости от рабочего процесса, вкуса и потребностей.

Вот хороший способ подумать об этом.

1. Изолировать листья вашего АСТ

Под листьями я подразумеваю типы типа loc или id, которые не зависят от других типов. Они не должны быть в вашем рекурсивном определении типа и, следовательно, не должны быть.

Более того, у вас, вероятно, будут специальные функции для обработки местоположений и идентификаторов, и использование этих функций близко к определению типа является хорошей практикой. Таким образом, вы можете создать файл ast_loc.ml и ast_id.ml с соответствующими определениями и основными функциями.

Это может показаться небольшим, но на самом деле это поможет сделать ваш код более понятным с дополнительным бонусом освещения ast.ml .

2. При необходимости настройте параметры для типов

Теперь я не рекомендую использовать это широко, так как это делает код труднее для чтения, так как имеет больше косвенных ссылок. Проверьте это:

type 't v = Thing of 't

(* potentially in a different later file *)
type t = Stuff of t v

Используя параметр типа, вы можете отложить использование рекурсивности в определении типа. Обратите внимание, что я не рекомендую использовать его для всей вашей AST, так как это будет поддерживать боль, но если у вас есть некоторые средние узлы, которые ведут себя совершенно независимо от остальных, это может помочь.

Например, их можно часто использовать:

type 'a named = { id : id; v : 'a; }
type 'a located = { loc : loc; v: 'a; }

Этот метод особенно полезен, если он помогает факторизовать ваше определение типа. Но, как я уже говорил, не злоупотребляйте этим! Это легко сделать, но трудно поддерживать.

3. В какой-то момент вам нужно большое жирное рекурсивное определение

На сегодняшний день файл Parsetree компилятора OCaml содержит 958 строк. Это то, что должно быть. Это сложная древовидная структура, и она должна быть видимой.

Обратите внимание, что файл является просто определением типа. Последующие файлы содержат код для управления этим определением (и обычно не вводят новые типы, которые необходимы вне их модуля).

В некотором смысле, я немного противоречу высказанному мной замечанию о loc и id, утверждая, что вы должны разделить определение типа и код, но это другой случай: loc и id просты типы, которыми можно манипулировать независимо. symbol имеет смысл только в пределах вашего определения AST. Кроме того, ничто не мешает вам создать файл symbol.ml , который управляет этой частью AST без указания типа (комментарии - ваши друзья, Merlin - необходимость).

Кроме того, рекурсивные функторы - это не то, что я бы посоветовал, если они вам действительно не нужны.

...