Учебник Ada для начинающих
Ada — это язык, который проектировали не для того, чтобы программу было быстро написать, а для того, чтобы ей можно было доверить человеческие жизни. На нём летают самолёты Boeing и Airbus, работает европейская ракета-носитель Ariane, управляются поезда метро без машиниста, бьются сердца кардиостимуляторов и наводятся системы вооружения. Везде, где цена ошибки измеряется не в потерянной выручке, а в катастрофе, инженеры выбирают язык, который ловит ошибки как можно раньше: на этапе компиляции, а если не вышло — то на этапе исполнения через явную проверяемую ошибку, а не тихое повреждение памяти.
Этот курс — серьёзное погружение в Ada для тех, кто хочет понять не только синтаксис, но и инженерную философию надёжного программирования. Мы пройдём путь от истории создания языка по заказу Министерства обороны США до тонкостей системы типов, которая считается одной из самых выразительных и безопасных в индустрии. Вы научитесь думать так, как думает разработчик критически важных систем: описывать ограничения данных прямо в типах, заставлять компилятор работать на вас и проектировать программы, которые невозможно использовать неправильно. Курс рассчитан на читателя с базовым опытом программирования; примеры даны на современном Ada 2012/2022.
Курс «Ada: язык надёжных и безопасных систем» состоит из 10 разделов и 40 уроков: Что такое Ada и философия надёжности, Основы языка, Система типов — сердце Ada, Управление и подпрограммы, Составные типы, Пакеты и инкапсуляция, Надёжность: исключения и контракты, Обобщённое программирование (generics), Параллелизм, встроенный в язык и ООП и системное программирование. Уроки идут по порядку — от основ к более сложным темам, в каждом есть объяснение с примерами, а в конце — вопросы для самопроверки. К урокам привязаны задачи с автоматической проверкой: прочитали тему — сразу закрепили её кодом.
Программа курса
1 Что такое Ada и философия надёжности
- Зачем понадобился ещё один язык: история и заказ DoD
Как Министерство обороны США в 1970-х пришло к идее единого языка, требования Steelman и конкурс, породивший Ada в честь Ады Лавлейс.
- Эволюция языка: от Ada 83 до Ada 2022
Четыре крупных ревизии стандарта Ada: ООП в 95, контракты в 2012, параллелизм в 2022 — что и зачем добавляли, сохраняя совместимость.
- Где живёт Ada: авионика, космос, железные дороги, медицина
Реальные системы на Ada: Boeing 777, Ariane, метро, кардиостимуляторы. Почему критичные отрасли с высокой ценой отказа выбирают этот язык.
- Экосистема: компилятор GNAT и анонс SPARK
Свободный компилятор GNAT на базе GCC, инструменты AdaCore и SPARK — подмножество Ada для математического доказательства корректности.
- Зачем понадобился ещё один язык: история и заказ DoD
2 Основы языка
- Структура программы: главная процедура, with и use
Анатомия программы на Ada: где точка входа, почему главная единица — процедура, и как with/use подключают пакеты стандартной библиотеки.
- Переменные, константы и присваивание :=
Объявление переменных с обязательным типом, инициализация, неизменяемые константы и почему присваивание обозначается := а не =.
- Базовый ввод-вывод: семейство Text_IO
Печать строк через Put и Put_Line, вывод чисел специализированными пакетами, преобразование Image и базовое чтение Get_Line.
- Стандартная библиотека и первая полноценная программа
Обзор иерархии Ada: Numerics, Calendar, Containers, Strings — и сборка работающей программы с вводом, расчётом и форматированным выводом.
- Структура программы: главная процедура, with и use
3 Система типов — сердце Ada
- Сильная типизация: почему 1 и 1.0 несовместимы
Скалярные типы Ada и принцип строгой типизации: значения разных типов нельзя смешивать без явного преобразования, и это ловит ошибки рано.
- Поддиапазоны и производные типы
Subtype сужает диапазон, оставаясь совместимым; type ... is new создаёт несовместимый тип, чтобы развести метры и секунды на уровне компилятора.
- Перечисления и модульные типы
Перечисления заменяют магические числа именами, Boolean и Character — тоже перечисления; модульные типы (mod) дают циклическую арифметику для битов и счётчиков.
- Атрибуты типов: 'First, 'Last, 'Range, 'Image
Атрибуты — встроенные запросы свойств типа через апостроф; опора на них вместо вписанных констант делает код устойчивым к изменениям.
- Сильная типизация: почему 1 и 1.0 несовместимы
4 Управление и подпрограммы
- Ветвление: if и case с полным покрытием
Оператор if с elsif и явным end if, оператор case с обязательным покрытием всех значений, when others и null, сокращённые and then / or else.
- Циклы: for, while и loop с exit
Единая конструкция loop и формы for (по диапазону), while (по условию), loop ... exit; защищённый счётчик и именованные циклы для выхода из вложенности.
- Подпрограммы и режимы параметров in/out/in out
Процедуры и функции, режимы параметров in, out, in out как видимый и проверяемый контракт направления данных, чистые функции и выражения-функции.
- Перегрузка, именованные аргументы и условные выражения
Перегрузка имён по типам и даже по результату, именованные аргументы для читаемых вызовов, значения по умолчанию и выражения if/case, возвращающие значение.
- Ветвление: if и case с полным покрытием
5 Составные типы
- Массивы: ограниченные, неограниченные и атрибуты
Массивы Ada с индексацией любым дискретным типом, атрибуты границ и длины, неограниченные массивы (range box) и проверка границ против переполнения буфера.
- Агрегаты: составное значение одним выражением
Агрегаты задают массив или запись целиком: позиционно, по именам, с диапазонами и others; компилятор проверяет полноту покрытия компонентов.
- Записи: поля, дискриминанты и варианты
Записи объединяют разнородные поля; записи с дискриминантом параметризуют структуру, а вариантные записи меняют набор полей типобезопасно, в отличие от union в C.
- Строки: String и Unbounded_String
String как массив символов фиксированной длины со срезами и атрибутами, Unbounded_String для текста переменной длины и осознанный выбор между ними.
- Массивы: ограниченные, неограниченные и атрибуты
6 Пакеты и инкапсуляция
- Пакеты: спецификация и тело
Спецификация и тело пакета в Ada: как разделить видимый контракт и скрытую реализацию, раздельная компиляция, директива use и инкапсуляция состояния.
- Приватные типы: абстрактные типы данных
Приватные типы Ada (private): абстрактные типы данных с скрытым представлением, видимая и приватная части спецификации, операции по умолчанию, много экземпляров.
- Ограниченные приватные типы (limited private)
Ограниченные приватные типы Ada (limited private): запрет присваивания и сравнения, защита уникальных ресурсов, build-in-place и расширенный return, аналогия с move-семантикой.
- Дочерние пакеты и инициализация
Дочерние пакеты Ada (Parent.Child): расширение родителя с доступом к его приватной части, иерархии библиотек, приватные потомки и порядок инициализации (elaboration).
- Пакеты: спецификация и тело
7 Надёжность: исключения и контракты
- Исключения: обработка нештатных ситуаций
Исключения в Ada: raise, раздел exception и when, своё исключение с сообщением, Ada.Exceptions, сворачивание стека, zero-cost модель и идиомы безопасной обработки ошибок.
- Проверки времени выполнения и Constraint_Error
Предопределённые проверки Ada: границы массивов, диапазоны подтипов, переполнение, разыменование null. Constraint_Error, узкие подтипы, модульные типы и атрибуты 'Range/'Valid.
- Контракты Ada 2012: Pre, Post, инварианты
Контракты Ada 2012: предусловия Pre, постусловия Post с 'Result/'Old/for all, Type_Invariant и предикаты подтипов, Design by Contract и связь с формальной верификацией SPARK.
- Обзор SPARK: формальная верификация
SPARK — верифицируемое подмножество Ada: GNATprove доказывает отсутствие ошибок времени выполнения и соответствие контрактам для всех входов, аспекты Global/Depends, уровни Stone–Platinum.
- Исключения: обработка нештатных ситуаций
8 Обобщённое программирование (generics)
- Generic-пакеты и подпрограммы: основы
Обобщённые единицы Ada: generic-пакеты и подпрограммы как шаблоны, инстанцирование через new, независимые экземпляры, типобезопасность и двусторонняя проверка по контракту параметров.
- Формальные параметры: типы, значения, операции
Формальные параметры generic в Ada: лесенка формальных типов (private, (<>), range <>), формальные значения и формальные подпрограммы (with function), умолчания is <> и проверка по контракту.
- Инстанцирование и переиспользуемые компоненты
Инстанцирование generic в Ada как композиция: экземпляр-библиотека, формальные пакеты (with package), вложение обобщённых единиц и стандартные контейнеры Ada.Containers.Vectors.
- Generics против ООП: два полиморфизма
Generics как основа переиспользуемых компонентов Ada: статический против динамического полиморфизма, паттерны (Итератор, Стратегия) через обобщения, границы подхода и сочетание с ООП.
- Generic-пакеты и подпрограммы: основы
9 Параллелизм, встроенный в язык
- Задачи (task): параллелизм как часть языка
Задачи Ada (task): встроенный в язык параллелизм, спецификация и тело задачи, автоматический запуск, структурированная зависимость, delay и отображение на потоки ОС или голое железо.
- Рандеву: entry, accept и select
Рандеву в Ada: точки входа entry, оператор accept, синхронная встреча задач, select со сторожевыми условиями и terminate, таймированные и условные вызовы, модель в духе CSP.
- Защищённые объекты, очереди и барьеры
Защищённые объекты Ada (protected): встроенный монитор, function/procedure/entry, автоматическое взаимное исключение, барьеры входов (when) для условной синхронизации, сравнение с рандеву.
- Ravenscar-профиль для реального времени
Ravenscar-профиль Ada: детерминированное подмножество задач для жёсткого реального времени, pragma Profile, delay until для периодичности, протокол потолка приоритета и анализ планируемости.
- Задачи (task): параллелизм как часть языка
10 ООП и системное программирование
- Тэгированные типы: основа ООП
Тэгированные типы Ada (tagged): объекты с рантайм-тегом, примитивные операции как методы, точечная нотация, атрибут 'Tag и Ada.Tags, таблица диспетчеризации под капотом.
- Наследование, диспетчеризация и интерфейсы
Наследование и полиморфизм в Ada: расширение типа (new ... with record), overriding, class-wide типы 'Class и динамическая диспетчеризация, абстрактные типы и интерфейсы без ромбовидной проблемы.
- Access-типы, безопасность и представление данных
Системное программирование в Ada: access-типы как безопасные указатели (accessibility, storage pools, без адресной арифметики), Unchecked_Deallocation и владение, rep clauses для точной раскладки битов.
- Интероп с C и итог по верификации
Интероп Ada с C (Import/Export, Convention => C, Interfaces.C) и итог курса: эшелонированная надёжность от типов и контрактов до формальной верификации SPARK/GNATprove для критичных систем.
- Тэгированные типы: основа ООП