В.Ш. Кауфман - Языки программирования - концепции и принципы (1990) (1160787), страница 43
Текст из файла (страница 43)
принципом сундука (в чемоданчик кладут только абсолютно необходимое).
Продемонстрируем этот важнейший метаязыковый принцип на примере
конкретных решений, принятых при создании ЯП Модула-2 и сопоставим их с
решениями, воплощенными в Аде. Но сначала придется в общих чертах
познакомиться с Модулой-2.
12.4. Обзор языка Модула-2
Язык Модула-2, созданный в конце семидесятых годов, прямой наследник ЯП
Паскаль и Модула, созданных Н.Виртом в конце шестидесятых и в середине
семидесятых годов соответственно. Это ЯП общего назначения, ориентированный
на относительно скромные ресурсы как инструментальной, так и целевой машины.
Автор предназначал его для небольших, в том числе персональных компьютеров
("для небольшой однопроцессорной ЭВМ").
В Модуле-2 автору удалось соединить простоту и естественность основных
конструктов Паскаля с мощными и изящными средствами модуляризации. Коротко
говоря, Модула-2 - это модульный Паскаль.
Специалисты обратили внимание на очередное достижение Вирта с первых
публикаций. В настоящее время интерес к Модуле-2 становится всеобщим, так
как появились высококачественные его реализации. Замечательная особенность
ЯП, обеспечившая его достоинства - следование принципу чемоданчика.
12.4.1. Характеристика Модулы-2 в координатах фон-неймановского
языкового пространства (технологическая позиция)
По сравнению с моделью А отсутствуют производные типы; концепция типа
ориентирована скорее на структуру, чем на имена; существенно меньше
предопределенных типов; резко ограничен аппарат управления асинхронными
процессами; сильно упрощено управление видимостью и аппарат раздельной
компиляции (в частности, отсутствуют вторичные модули); отсутствует аппарат
управления точностью расчетов; упрощена предопределенная модель обмена;
отсутствуют родовые модули; резко ограничено управление представлением;
отсутствует управление исключениями.
С другой стороны, с точки зрения языкового пространства Модула-2 ничего
не добавляет к модели А. Иначе говоря, верна неформальная теорема : для
всякого конструкта Модулы-2 в Аде найдется конструкт с аналогичными
возможностями, т.е. язык Модула-2 можно назвать технологическим
подмножеством Ады.
12.4.2. Характеристика Модулы-2 в терминах концептуальной схемы
Для краткости свойства Модулы-2 назовем М-свойствами, а свойства Ады
(А-модели) - А-свойствами.
Базис. Скалярная М-сигнатура - точное подмножество А-сигнатуры.
Отсутствуют фиксированные вещественные типы и вообще управление точностью.
Но имеются целые, вещественные, символьные, логические типы (с более или
менее устоявшимся набором операций).
Структурная М-сигнатура содержит регулярные, комбинированные и
ссылочные типы, аналогично А-сигнатуре. Но в ней имеются также процедурные и
множественные типы. Правда, весьма ограниченные.
Именно, значением процедурного типа не может быть стандартная процедура
или процедура, вложенная в другую процедуру. А "множественный" тип (SET OF
T), класс значений которого состоит из всех возможных множеств значений
исходного типа T, можно образовывать только для исходных типов малой
мощности (чтобы множество можно было представить одним машинным словом -
двоичной шкалой; это пример влияния на язык реализаторской позиции).
Имеются обычные управляющие структуры (последовательности, развилки,
циклы, процедуры) и ограниченные средства управления асинхронными
процессами. При этом настоящий параллелизм подразумевается только для так
называемых периферийных процессов (аппаратных задач, соответствующих внешним
устройствам).
Развитие. Можно создавать операционные абстракции (процедуры и функции)
и абстракции данных (именованные типы с возможностью ограничивать набор
применимых операций - аналог приватных А-типов). Основное средство развития
- модуль. Это аналог А-пакета.
Защита. Обязательные объявления с соответствующим контролем поведения.
Подразумевается статический контроль так называемой совместимости типов и
динамический контроль за допустимостью значений переменных. Роль подтипа по
существу играет тип, объявленный как отрезок другого типа. Отрезки одного
исходного типа совместимы между собой и с исходным типом. Формально понятие
подтипа отсутствует. Производные типы отсутствуют (и формально, и по
существу).
[Понятие типа и совместимости типов в авторском описании определено
нечетко. Неясно, какие типы равны. Можно подозревать, что равными считаются
типы, названные одинаковыми именами. Так как нет производных типов, то
операции явно с типом не связаны (нет проблемы неявного определения операций
для производных типов).]
Аппарат исключений не предусмотрен.
Мощное средство прогнозирования и контроля фактически предоставляет
аппарат управления видимостью - списки экспортируемых и импортируемых имен.
С их помощью обеспечивается инкапсуляция в рамках модулей (аналогов А-
пакетов).
Исполнитель. Характеризуется набором предопределенных и определяемых
реализацией модулей, в совокупности обеспечивающих рациональное для
небольших компьютеров сочетание общеязыковых (резидентных, постоянно
присутствующих в компиляторе и (или) целевой машине) и специализированных
средств. Среди последних - аппарат файлового обмена, управления
параллельными процессами посредством сигналов, управление динамическим
распределением памяти.
Архитектура. Характеризуется принципом чемоданчика. Именно поэтому мы
особенно интересуемся Модулой-2.
12.5. Пример М-программы
Рассмотрим решение уже известной нам задачи об управлении сетями. Цель
- создание у читателя "зрительного образа" М-программ, а также подробное
знакомство с теми свойствами ЯП, которые помогут продемонстрировать принцип
чемоданчика. Требования к реализации комплекса услуг по управлению сетями те
же, что и в А-случае (надежность, целостность, модифицируемость).
Однако в самом начале следует сказать о ключевом понятии Модулы-2.
Название этого понятия отражено в названии ЯП. Конечно, это понятие -
модуль.
Как и в Аде, спецификация в Модуле-2 отделена от реализации.
Представлены они соответственно определяющими (DEFINITION) и реализующими
(IMPLEMENTATIN) модулями, аналогами спецификации и тела пакета в Аде.
Продуманная интерпретация фундаментальной концепции модуля - основа
элегантности и конкурентоспособности Модулы-2. Именно на этой интерпретации
мы и сосредоточим свой анализ.
12.5.1. Управление сетями на Модуле-2
Ниже следует определяющий модуль ПараметрыСети (аналог спецификации
соответствующего А-пакета).
1. DEFINITION MODULE ПараметрыСети;
2. EXPORT QUALIFIED МаксУзлов, МаксСвязей;
3. CONST МаксУзлов = 100;
4. МаксСвязей = 8;
5. END ПараметрыСети;
Как видите, очень похоже на Аду. Отличаются ключевые слова; в
идентификаторах недопустимы разделители-подчеркивания (поэтому применяются
большие буквы для отделения слов); допустимы серии объявлений типов,
констант и переменных, выделяемых соответствующим ключевым словом; вместо is
применяется знак "=". Короче говоря, Модула-2 в перечисленных отношениях
ближе к своему старшему родственнику - Паскалю, чем Ада.
Главное отличие - во второй строке. Она представляет собой так
называемый список экспорта. В нем явно перечисляются те и только те имена,
определенные в модуле, которые считаются доступными (видимыми) в объемлющем
контексте.
[Точнее говоря, ключевое слово QUALIFIED указывает на косвенный
экспорт, когда доступны лишь полные имена (с указанием имени экспортирующего
модуля): ПараметрыСети.МаксУзлов и ПараметрыСети.МаксСвязей. При отсутствии
этого ключевого слова имеется в виду прямой экспорт - непосредственно
доступны "короткие" имена МаксУзлов и МаксСвязей.]
В использующих модулях можно управлять доступом с помощью так
называемых списков импорта.
12.5.2. Определяющий модуль
1. DEFINITION MODULE УправлениеСетями;
2. FROM ПараметрыСети IMPORT МаксУзлов, МаксСвязей;
(* это список импорта *)
3. EXPORT QUALIFIED Создать, Вставить, Удалить, Связать,
Узел, Связи, Присвоить, УзелЕсть,
ВсеСвязи, Сети;
(* это список экспорта *)
4. TYPE Узел = [1..МаксУэлов];
5. ЧислоСвязей = [0..МаксСвязей];
6. ИндексУзла = [1..МаксСвязей];
(* производных типов нет. Все три типа совместимы. *)
7. ПереченьСвязей = ARRAY ИндексУзла OF Узел;
(* регулярный тип (тип массива). Все неформальные массивы - с
постоянными границами. Точнее говоря, границы -
константные выражения, вычислимые в период компиляции *)
8. Связи = RECORD
9. Число : ЧислоСвязей; (* инициализации нет *)
10. Узлы : ПереченьСвязей;
11. END;
(* комбинированный тип (тип записи). Допустимы и вариантные. *)
12. Сети;
(* указано только имя типа. Это так называемое непрозрачное
объявление типа. Аналог объявления приватного типа. *)
13. PROCEDURE Создать (VAR Сеть : Сети);
(* В Аде этого не было. Непрозрачные типы могут быть только ссылочными или
отрезками предопределенных типов. Содержательные сети у нас - массивы.
Поэтому тип "Сети" будет ссылочным (а никак не отрезком). Реализовать
процедуру создания соответствующего массива можно только в модуле,
экспортирующем тип "Сети", т.е. в реализующем модуле УправлениеСетями. Дело
в том, что в отличие от Ады приватной части в определяющем модуле нет.
Поэтому нет во внешнем контексте и информации об устройстве непрозрачных
типов (ее нет даже для транслятора). Поэтому приходится определять
специальную процедуру создания содержательных сетей. *)
(* Параметр "Сеть" специфицирован ключевым словом "VAR" - это так называемый
параметр-переменная - аналог А-переменной, вызываемой в режиме in out. В
результате исполнения процедуры будет создан указатель на массив-сеть и
присвоен формальному параметру-переменной. *)
14. PROCEDURE Вставить (X : Узел; ВСеть : Сети);
(* Оба параметра - параметры-значения (аналог режима in). Хотя второй
параметр указывает на содержательно изменяющуюся сеть, сам указатель при
этом остается неизменным. *)
15. PROCEDURE Удалить (X : Узел; ИзСети : Сети);
16. PROCEDURE Связать (АУзел, ВУзел : Узел; ВСети : Сети);
17. PROCEDURE Присвоить (Сеть1, Сеть2 : Сети);
(* В Аде этого не было. Во внешнем контексте содержательное присваивание
сетей описать невозможно из-за отсутствия информации об их строении даже у
транслятора - приватной части нет! Поэтому и приходится определять
специальную процедуру для присваивания содержательных сетей. *)
18. PROCEDURE УзелЕсть (X : Узел; ВСети : Сети) :BOOLEAN ;
(* Так объявляют в Модуле-2 логическую функцию. *)
19. PROCEDURE ВсеСвязи (X : Узел; ВСети : Сети) : Связи;
20. END УправлениеСетями;
Строка 1 - заголовок определяющего модуля. Строка 2 - список импорта.
Перечисленные в нем имена (экспортированные модулем ПараметрыСети) доступны
(прямо, по коротким именам) в модуле УправлениеСетями. Важно, что никакие
другие внешние имена (кроме стандартных) недоступны. Часть FROM списка
импорта указывает имя модуля-экспортера и тем самым позволяет применять
короткие имена. Если бы список начинался сразу словом IMPORT, то в нем
должны были бы фигурировать косвенные имена (с точкой) для случая косвенного
экспорта (и прямые имена для случая прямого экспорта).
Строка 3 - список косвенного экспорта (требующего во внешнем контексте
в общем случае косвенных имен). Косвенный экспорт называют иногда
"квалифицированным", а действие фрагмента FROM "снятием квалификации". Такие
обороты выглядят чужеродными в русском тексте.
В строке 12 - непрозрачное объявление типа. По назначению оно
соответствует объявлению приватного типа в Аде. Во внешнем контексте
становится известно имя объявленного непрозрачного типа, но применять к его
объектам можно лишь фиксированный набор операций, объявленных в этом же
(определяющем модуле), так как о природе объектов непрозрачного типа во
внешнем контексте ничего неизвестно.
[Конечно, имя непрозрачного типа и имена соответствующих операций
должны быть в списке экспорта. Иначе тип незачем объявлять непрозрачным.]
12.5.3. Использующий модуль
MODULE ПостроениеСетей;
(* это главный модуль, ничего не экспортирующий *)
(* определяющий модуль для главного не пишется *)
FROM УправлениеСетями IMPORT Создать, Вставить, Связать,
Присвоить, Сети;
VAR Сеть1, Сеть2 : Сети; (* объявление переменных типа Сети *)
BEGIN
Создать (Сеть1); (* содержательную сеть - в отличие от объекта *)
(* типа Сети - можно создать только *)