Организующей программной единицей является раздел. В ряде языков их называют модулями [0].
section name { /* Это именованное место для упорядоченных объявлений: типов, констант, переменных, функций. */ }.
Oberon
MODULE name; /* Это именованное место для упорядоченных объявлений: типов, констант, переменных, процедур. */ END name.
Раздел не привязан к понятию файла как излишне платформоспецифичному. Конечно, для обычной разработки удобно провести соответствие, но возможны и другие варианты, когда, например, код встраивается в интерактивный документ и запускается прямо оттуда, что может пригодиться в литературе и документации.
Для обеспечения доступности объявлений раздела другим разделам, они должны быть экспортированы. Упорядоченная совокупность экспортируемых объявлений составляет интерфейс раздела. Для работы с экспортированными объявлениями требуется произвести импорт разделов, в которых они объявлены.
section m1 { const (+a = 0;/* экспорт обозначен «+» перед именем */ b = a + 1) ... }. section m2 { import (m1) /* доступ к элементам раздела через двоеточие */ const (a = m1:a;/* a = 0 */ b = m1:b)/* ошибка - константа "b" не экспортирована в "m1" */ ... }.
Прямой или косвенный циклический импорт разделов запрещён. Это требует большего размышления над архитектурой, но способствует её улучшению и уменьшает сцепление кода.
section m1 { import (m2) }. section m2 { import (m1)/* ошибка - m1 уже ссылается на m2 */ }.
Назначение разделов
Смысл использования разделов может существенно отличаться, что желательно отобразить в языке для ясности и возможности дополнительной защиты со стороны транслятора.
- Раздел может использоваться исключительно как вспомогательный, не влияя на интерфейс импортирующего раздела. Использование такого раздела может быть заменёно другим кодом, выполняющим те же задачи.
- Раздел, являющийся частью интерфейса импортирующего его раздела. Его объявления могут становиться видимой частью экспортируемых объявлений импортирующего.
- Раздел, чьё определение отчасти задаётся импортированными разделами-параметрами, и при создании которого учитывалось это.
- Опциональные разделы, наличие которых можно проверить в коде. Могут использоваться либо для создания более ограниченной версии раздела, либо для задействования более эффективных средств, не приводя к жёсткой зависимости от них.
- Декоративные разделы, каким-либо образом перестраивающие взаимодействие с основным функционалом, практически не меняя его, например, объединяя интерфейсы разных разделов. Служат для удобства использования.
Поскольку разделов, особенно с учётом сторонних библиотек, может быть много, то возникает необходимость в создании иерархий:
section a.m { const (+c1 = 1) }. section a.m2 { const (+c2 = 2) }. section b.m { const (+c3 = 3) }. section m { import ( d.m; /* в качестве идентификатора раздела после импорта */ d.m2; /* используется только вторая часть имени после точки */ bm = b.m/* переименование, чтобы избежать ошибки совпадения имён */ ) const (c4 = m.c1 + m2.c2 + bm.c3) }.
Разграничение
Для удобства разграничения возможностей, предоставляемых импортирующим разделам, раздел может быть разбит на части, ограниченно владеющие функционалом. Необходимо лучше продумать синтаксис разделения, но как пример оно может выглядеть так:
section path+content+full { type (+t) section content+full { type ( +t = { +name string; +next *t } ) section full { proc +new(name string, subpath *t) (path *t) { ... } } full; } content+full; } path+content+full. section editor { import ( /* path+full; */ /* после импорта должен быть доступен по имени path, но если у editor нет доступа к полному разделу, то здесь была бы ошибка трансляции */ path /* эта часть позволяет получать доступ к ресурсам, пути к которым явно переданы параметрами, но не позволяет самостоятельно указывать, с какими ресурсами можно работать, как и не даёт доступа к самому имени */ ) proc +do(file path:t) { ... } }.
По сравнению с некоторыми другими способами обособления такое разделение позволяет легче доказывать отсутствие в меньших интерфейсах предоставления лишних возможностей. Нет смешивания, в которм было бы легко потерять включение чего-то ненужного. Чего нет в подразделе, то точно недоступно. Вынесение разделения на пользовательский уровень вместе с другими мерами позволит получить гарантии отсутствия нежелательного доступа у произвольного исполнимого кода уже на этапе верификации кода, что даст гораздо лучшую предсказумость и понятность системы в сравнении с устаревшим подходом.
Трансляция
При трансляции корректного раздела может создаваться два файла: один с исполнимым кодом, другой - интерфейсный для возможности независимой сборки импортирующих его разделов. Лучше, если исполнимый код будет не специфичным для машины, хотя и с возможностью внедрения машинного кода, выступающего как оптимизированная альтернатива основному коду. Для обоих файлов должны быть предусмотрены контроль целостности и подпись.
Чтобы избежать случайного изменения интерфейса раздела, транслятор по умолчанию должен выдавать ошибку, если вновь созданный интерфейсный файл не совпадает с предыдущим. В инструментарии для больших команд следует предусмотреть возможность распределения ролей, в которых некоторым разработчикам запрещается менять интерфейс целевых, не вспомогательных разделов, над которыми они работают.
В операционных системах, где были бы воплощены идеи хотя бы 80-х, а не мусолились идеи 60-70-х, раздел может служить главной и единственной единицей загрузки вместо россыпи таких понятий как программа, статическая и динамическая библиотеки. В морально устаревших же операционных системах нужно будет выполнить наподобие этого:
$ sc build util.do
Что приведёт к созданию исполнимого файла util, полученного из одноименного раздела с точкой входа do - экспортированной процедуры без параметров.
Чтобы понять почему так, нужно ответить на вопрос: Если ответ - «не должен», значит, истинными модулями являются отдельные объявления раздела, а не сам раздел. Объявления пронизаны неявным импортом внутренних объявлений, необходимого для избежания чрезмерного перечисления используемых связей, что было бы нужно в случае раздельного оформления отдельных сущностей раздела.
Дополнительные сведения:
[0]Почему раздел - это не модуль или unit?
«Если в разделе несовместимо изменилось экспортированное объявление, должен ли он потерять совместимость с разделами, использующими другие его неизменившиеся объявления?»
[2]
Михаэль Франц Динамическая кодогенерация: ключ к разработке переносимого
программного обеспечения
[3]
Webassembly: Design Rationale
Комментариев нет:
Отправить комментарий