Организующей программной единицей является раздел. В ряде языков их называют модулями
[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?
Чтобы понять почему так, нужно ответить на вопрос:
«Если в разделе несовместимо изменилось экспортированное объявление, должен ли он потерять совместимость с разделами, использующими другие его неизменившиеся объявления?»
Если ответ - «не должен», значит, истинными модулями являются отдельные объявления раздела, а не сам раздел. Объявления пронизаны неявным импортом внутренних объявлений, необходимого для избежания чрезмерного перечисления используемых связей, что было бы нужно в случае раздельного оформления отдельных сущностей раздела.
[1]
Оберон умер, да здравствует Оберон! Часть 2. Модули
[2]
Михаэль Франц Динамическая кодогенерация: ключ к разработке переносимого
программного обеспечения
[3]
Webassembly: Design Rationale