Знакомство с языком программирования Ада можно осуществлять несколькими различными путями. Чаще всего изложение языка осуществляют "снизу вверх", начиная с серий языковых конструкций с постепенно возрастающей структурой и (или) семантической сложностью. С этой точки зрения, например, рассказ о языке Ада должен был бы начаться с рассказа об алфавите языка, затем о его идентификаторах, операторах, программных единицах и, наконец, о его программах. Это типичный подход, применяемый авторами справочных руководств по языкам программирования. И действительно, справочное руководство по языку Ада [2] является весьма полезным пособием при чтении этой главы.
Мы воспользуемся подходом "сверху вниз". На самом верхнем уровне будем анализировать законченные программы на языке Ада, рассматривая их в этом общем случае как сети взаимосвязанных компонент, называемых модулями или программными единицами. Каждый модуль состоит из двух принципиально различных частей: интерфейса модуля с оставшейся частью сети (его спецификации) и внутренней структуры модуля с его выполняемыми кодами (тела модуля). Внутренняя структура модуля может выступать в качестве подструктуры сети, однако этого пока мы касаться не будем.
Такой подход "сверху вниз" соответствует точке зрения, рассматривающей программирование как сугубо интеллектуальный процесс системного проектирования. С точки зрения инженера под системой обычно подразумевается набор взаимосвязанных компонент (возможно, функционирующих параллельно), с каждой из которых может быть связана некоторая информация о состоянии.Эти взаимодействующие компоненты образуют некоторый набор информационных структур, чьи времена жизни охватывают жизнь связанного с ними модуля, причем последняя, как это часто бывает, может охватывать жизнь самой системы.
Язык Ада в большей степени, чем остальные широко распространенные языки программирования, и в такой же степени, как некоторые экспериментальные языки [3, 9, 39, 42, 45, 67, 68], позволяет программисту разрабатывать и создавать программы в виде ансамблей взаимодействующих и поддерживающих свое состояние программных единиц.
Любая программа на языке Ада может быть составлена из трех видов программных единиц, которые могут нести (иметь связанные с собой) развивающиеся информационные структуры:
Программа на языке Ада начинает свое выполнение с единственной нити управления, связанной с главной подпрограммой (стартовой задачей). При вызове подпрограммы внутри пакета нить управления "перемещается" по кодам, составляющим этот пакет. В итоге нить управления возвращается к стартовой задаче подобно тому, как нить управления перемещается от главной части программы на Фортране (или самого верхнего блока программы на Паскале) к различным вызываемым подпрограммам или от них.
В некоторых точках программы (например, во время обработки спецификации задачи) могут быть порождены новые нити управления. Процесс порождения нитей управления может последовательно развиваться, образуя дерево задачи. Окончание такой задачи может произойти только после окончания работы всех порожденных ею задач в порядке, противоположном тому, в котором они были порождены.
Любая задача может вызвать подпрограмму из другого пакета, однако в результате такой операции новые нити управления не создаются. Под "вызовом пакета" мы подразумеваем обращение к подпрограмме, т. е. операцию, принадлежащую к общедоступной части пакета. В случае вызова пакета нить управления может быть представлена как линия связи от кода вызывающей программы к коду вызываемого пакета и в обратном направлении при возврате из вызываемого пакета.
В противоположность этому задача "вызывает другую задачу", т. е. осуществляет обращение ко входу, посылая ей сообщение. (Сообщение входит в синтаксис оператора вызова входа и соответствует описанию входа entry в вызываемой задаче.) В результате вызова одной задачи из другой нить управления вызывающей задачи задерживается до тех пор, пока вызываемая задача не отреагирует на посланное ей сообщение (завершит прием этого сообщения). Если все завершается благополучно, то вызывающую задачу информируют о том, что сообщение было получено. После этого вызывающая программа возобновляет свою работу. Это означает, что нить управления вызывающей задачи возобновляет свою работу, а вызываемая задача продолжает выполнять свой независимый участок программы.
Такой протокол, включающий в себя временную остановку вызывающей задачи, называется рандеву. Рандеву является единственным средством языка для внешних связей между задачами. Использование рандеву гарантирует поддержание "структурности" в терминах "структурированного программирования".
[Архитектура i432 позволяет программисту реализовать менее структурированный (однако более параллельный) механизм коммуникации между задачами. Например, в нем имеются средства, позволяющие посылать сообщения одной или более задачам, не дожидаясь ответа ни от одной из них. Для использования этих возможностей архитектуры i432 программист вызывает специальный пакет (описываемый в гл. 5). Использование средств, поддерживающих коммуникации на уровне, сообщений, делает программу трудно переносимой на другие вычислительные системы. Однако необходимо отметить, что операционная система i432 позволяет полностью использовать имеющиеся на нижнем уровне средства коммуникационной поддержки для вспомогательных пакетов, используемых компилятором языка Ада для реализации возможностей задачи.]
При завершении рандеву вызывающая задача может снова начать выполнение, возможно, параллельно с вызванной ею задачей. В системах, подобных i432, принцип параллелизма реализуется закреплением за каждой из задач отдельного (доступного) процессора. В общем случае дерево задач, состоящее из m порожденных и активных в текущий момент задач, может обрабатываться с уровнем параллельности, равным m, если в системе имеется m доступных процессоров.
Если рассматривать систему как ансамбль (возможно, работающих параллельно) компонент, каждая из которых включает в себя информацию о своем состоянии, то возможность языка Ада реализовывать разнообразные богатые структуры пакетов и задач является крупным достижением в разработке языков программирования. Рассмотрим противоположную ситуацию, имеющуюся в языках Фортран или Паскаль. В программах на языках Фортран или Паскаль большинство информации о состоянии (например, о скалярных переменных, массивах, записях, и т.д.) связано со всей программой, а не с отдельными ее подпрограммами. Таким образом, за исключением данных, объявленных во внешнем блоке программы на языке Паскаль или в COMMON блоках главной программы на Фортране, время жизни всей объявленной информации ограничено временем нахождения в активном состоянии блока или подпрограммы. (В языке Фортран 77 это ограничение было несколько ослаблено с появлением оператора SAVE [41].)
Программист, работающий на языках Фортран или Паскаль, может испытывать затруднения при моделировании реальной системы или при объяснении принципов работы созданной им программы другим лицам, знакомым с реальной моделью. Это объясняется в первую очередь тем, что отсутствие разнообразных пространств состояний в Фортране и Паскале запрещает сохранение соответствия между системой и моделирующей ее программой. Более того, наличие в моделируемой системе параллельно функционирующих компонент еще более уменьшает соответствие между программой на Фортране или Паскале и моделируемой ею системой.
Чем в меньшей степени поведение системы соответствует поведению моделирующей ее программы, тем труднее осуществлять проверку этого соответствия. Такие программы также труднее поддерживать (модифицировать) по мере того, как вносятся изменения в моделируемую систему (из-за изменений в постановке проблемы или в требованиях к результатам). Поскольку расходы на модификацию особенно при больших размерах программ могут быть значительными, то в этом случае одним из существенных преимуществ, склоняющих к программированию на языке Ада, оказывается возможность поддержания ясного структурного соответствия между программой и моделируемой системой.
[При использовании языков программирования, подобных Фортрану или Паскалю, большая часть усилий концентрируется на сохранении логического и функционального соответствия между программами и моделируемыми ими системами. Это соответствие, особенно для небольших программ, обычно оказывается достаточным. В прошлом, однако, сильно недооценивались сложности, связанные с достижением требуемого соответствия при увеличении программ до больших объемов. В качестве замены языков Фортран или Паскаль рассматривались также и чисто функциональные языки [4, 11], однако они до сих пор не проверены на реальных задачах, использующих крупные базы данных или представляющих собой модели систем со сложными взаимодействующими компонентами. Более того, до сих пор не определены наиболее пригодные для таких языков архитектуры.]