Язык программирования Ада родился в середине 1970-х, когда министерство обороны США и министерство обороны Британии решили заменить сотни специализированных языков программирования для встроенных вычислительных систем, всё чаще использовавшихся в военных проектах. Язык Ада разрабатывали так, чтобы это был единственный язык, способный работать на всех этих встроенных системах, и при этом обеспечивавший надёжность и быстродействие уровнем не хуже специализированных.

После обновления от 1995 года язык приспособили для систем общего назначения, добавив объектно-ориентированное программирование, не теряя из вида ключевые ценности – надёжность, простоту поддержки и эффективность. Сегодня написанное на Ада ПО формирует основу не только военного оборудования, но и коммерческих проектов в сфере авионики и систем управления воздушным трафиком. Код на Ада управляет такими ракетами, как Ариан-4 и 5, многими спутниками, и бесчисленным количеством других систем, в которых небольшие сбои могут иметь серьёзные последствия.

Возможно, Ада подойдёт и для использования в вашем следующем встроенном проекте.

Планирование военного качества


Чтобы выбрать новый язык программирования, минобороны собрала «рабочую группу языков высшего порядка» [High Order Language Working Group (HOLWG)], состоявшую из военных и учёных экспертов, в задачи которой входило составление списка запросов и выбор языков-кандидатов. В итоге были составлены т.н. "запросы Стилмана":

Главными пунктами запросов были:

  • Гибкая схема работы общего назначения, адаптирующаяся к нуждам встроенных вычислительных приложений.
  • Надёжность. Язык должен способствовать проектированию и разработке надёжных программ.
  • Лёгкость поддержки. Код должен быть читаемым, а программные решения — ясными.
  • Лёгкость производства эффективного кода. Должна быть возможность легко определять неэффективные конструкции.
  • Отсутствие ненужной сложности. Семантическая структура должна быть последовательной, и минимизировать количество концепций.
  • Независимость от машины. Язык не должен быть привязан к каким-то деталям ОС или оборудования.
  • Полное определение. Все части языка должны быть полностью и недвусмысленно определены.

Завершался отчёт мнением, что первая линия обороны от проблем с ПО заключается в том, чтобы не давать программистам совершать ошибок. Устраняя возможности сделать малозаметную ошибку, к примеру, через неявные преобразования типов или другие опасные конструкции, мы автоматически делаем код безопаснее и облегчаем его поддержку.

Группа заключила, что хотя среди существовавших на тот момент языков ни один не подходил для нужд минобороны, было вполне реально создать новый язык, подходящий под все указанные вопросы. Четырём проектировщикам получили это сделать. Промежуточный процесс выбора подобрал два наиболее подходящих метода работы, и в итоге лишь один язык победил в конкурсе и получил название "Ада".



Встроенная по умолчанию защита


Система типов в Аде не просто строгая – её иногда называют сверхстрогой, поскольку она не позволяет никакого неявного приведения типов. Возьмём, к примеру, этот отрывок кода на С:

typedef uint32_t myInt;
myInt foo = 42;
uint32_t bar = foo;

Это допустимый код; он откомпилируется, запустится и выдаст очевидный результат, обозначающий ответ на главный вопрос жизни, вселенной и всего такого. В Аде так не получится:

type MyInt is Integer;
foo: MyInt;
bar: Integer;
foo := 42;
bar := foo;

Компилятор выдаст ошибку, поскольку Integer и MyInt – это не одно и то же. Главное преимущество такого подхода в том, что если программист потом изменит определение типа, тысячи неявных приведений типа по всей базе кода не взорвут программу. Вместо этого нужно явно приводить типы – это пропагандирует хороший код, предотвращая смешение типов, которые «достаточно схожи».

Любой программист, вязнувший в болоте из смеси стандартных определений типов C, Linux и Win32, может оценить по достоинству отсутствие необходимости рыться в бесчисленных страницах документации и плохо отформатированного кода, чтобы понять в каком из typedef или макросе содержится реальное определение чего-то, что только что помешало компиляции или вылезло при отладке.



Ада добавляет дополнительные слои защиты в проверках на этапах компиляции и запуска. В Аде программист должен явно указывать закрывающие операторы для блоков и границы, в которые должно укладываться значение переменной. Ада не определяет стандартные типы вроде int или float, а требует, чтобы программист с самого начала создал типы с определённым диапазоном. Это верно и для строк – за исключением неограниченных строк, у всех строк длина фиксирована.

На этапе работы можно проверить ошибки типа неверного доступа к памяти, переполнения буфера, выхода за установленные пределы, ошибки ±1, доступа к массиву. Затем их можно безопасно обработать, вместо того, чтобы ронять всё приложение.

Ада реализует модель ссылочных типов вместо низкоуровневых указателей. Каждый ссылочный тип обрабатывается пулом памяти, либо заданным по умолчанию, либо определённым программистом при необходимости работы с более экзотическими реализациями памяти типа NUMA. Программисту никогда не приходится обращаться к памяти напрямую, он должен использовать обработчик пула памяти.

Наконец, компилятор или программа во время исполнения решает, как передавать данные в функцию или из неё. И хотя направление передачи каждого параметра указывать нужно (‘in‘, ‘out‘, или ‘in out‘), но итоговое решение о том, передаются ли данные через регистры, кучу или по ссылке, принимает компилятор или программа во время выполнения, но не программист. Это предотвращает проблемы с переполнением стека.

Ravenscar profile и диалект SPARK являются подмножествами Ады, причём последний концентрируется на контрактах. Со временем особенности этих подмножеств перенесли в спецификацию основного языка.

Программирование на языке Ада сегодня


ANSI установила спецификацию Ada 83 в 1983. Тогда только-только вышел Intel 80286, а процессору Motorola 68000 было всего четыре года. Это была заря домашних компьютеров, а также неуклюжий переход из 1970-х в 80-е, когда популярность микроконтроллеров начала расти. Представьте себе микроконтроллер Intel 8051 и его потрясающие 4 кБ EPROM и 128 Б оперативной памяти.



Популярные сегодня микроконтроллеры во много раз более мощные по сравнению с теми, что были в 1983. Можно взять любой ARM, AVR, RISC-V, и т.п. (или Lego Mindstorms NXT kit) и начать под него разработку при помощи одинаковых инструментальных средств на базе С. Неудивительно, что популярный компилятор GNAT Ada основан на GCC. Также в разработке в рамках проекта DragonEgg находятся инструментальные средства на базе LLVM.

Существуют две версии инструментальных средств Ады на основе GCC. Вариант AdaCore поддерживается коммерчески, однако имеет свои особенности. Вариант от Free Software Foundation, естественно, свободен, и по функциональности сравним с AdaCore.

Для лёгкого старта используйте либо GNAT Programming Studio IDE (GPS), идущее в комплекте с AdaCore (копия на Github), или пишите код в текстовом редакторе и компилируйте его вручную, или при помощи Makefiles. Инструментарий тут немного посложнее, чем у С или С++, однако разработку облегчает утилита gnatmake, включающая в себя все инструменты, и работающая примерно как GCC.



Пример небольшого, но нетривиального проекта на Аде, написанного вашей покорной слугой в виде парсера аргументов командной строки. Там вы найдёте Makefile, находящийся в папке проекта ada/, где определяются папки, в которых можно найти файлы спецификации пакетов (.ads) и сами пакеты (.adb).

Эти файлы примерно соответствуют файлам с заголовками и кодом от С и С++, однако имеют и важные отличия. В отличие от С, у Ады нет препроцессора, и она не объединяет код и заголовки для создания компилируемых файлов. Вместо этого идёт ссылка на название пакета, указанное в спецификации. Название файла .ads тоже не обязано совпадать с названием пакета. Это даёт большую гибкость и предотвращает распространённые в С проблемы с циклической зависимостью или необходимостью линковки заголовков в определённом порядке.

Куда двигаться далее


Скачав инструментарий GNAT, запустив GPS или Vim/Emacs, и некоторое время посмотрев на мигающий курсор на пустой странице, вы можете задуматься над тем, с чего начать. К счастью, мы недавно освещали проект на основе Ады с использованием ядра PicoRV32 RISC-V. Он использует распространённый ICE40LP8K CPLD, который поддерживают инструментарии FPGA с открытым кодом, например, Yosys.



В плане документации есть вводные статьи для начинающих, рассчитанные на разработчиков Java и С++, справочник по AdaCore, справочник на WikiBooks, и, конечно же, документация Programming in Ada 2012. Это, возможно, наиболее полные справочники, за исключением документации Ada 2012 Language Reference Manual (LRM) на 945 страниц.

Язык Ада, пусть и довольно редкий для любителей программирования, является полностью открытым языком с надёжными средствами разработки с коммерческой поддержкой, и используется для создания ПО для всего, от межконтинентальных баллистических ракет и F-15 до прошивок медицинских устройств. Хотя это довольно сложный язык, если выходить за базовые пределы, он должен определённо входить в список языков, которые вы когда-либо использовали в своих проектах – пусть даже и для того, чтобы ваше резюме выглядело покруче.

Комментарии (19)


  1. Tsvetik
    02.10.2019 14:24
    -2

    Давно смотрю в сторону этого языка, но никак не начну писать. C/C++ уже видеть не могу.


  1. dukei
    02.10.2019 14:38
    -1

    Звучит-то как! Почему вам стоит использовать язык Ада… Адские старые исходники, действительно, естественно писать на языке Ада.


  1. oam2oam
    02.10.2019 14:48
    -1

    А я так и писал — Адское программирование голого железа, Ада сейчас очень даже годится и для микроконтроллеров!


  1. a-tk
    02.10.2019 21:32

    Вот только адская многословность адски напрягает…


  1. miga
    02.10.2019 22:28
    -2

    if ... then
    ...
    elsif
    ...
    end if


    Серьезная заявка в конкурсе на самый идиотский синтаксис условного оператора — почему elsif слитно-то?


    1. gorodnev
      02.10.2019 23:10

      по той же причине, что и в Python есть elif? Но там что-то никто не жалуется :)


    1. Sungmaster
      03.10.2019 20:01

      Технически else if и elsif / elif / elseif не являются одним и тем же. else if не просто пишется отдельно, это два отдельных оператора: else относиться к предыдущему if, следующий if создает новое выражение. Следующие два выражения эквивалентны:

      if (x == 0) puts("first");
      else {if (x == 1) puts("second");}
      
      if (x == 0) puts("first");
      else if (x == 1) puts("second");
      


  1. Alyoshka1976
    02.10.2019 23:08

    Компилятор Ada вполне успешно работает даже на 8080 под CP/M. Можно скомпилировать и поиграть в текстовый StarTrek.


  1. paluke
    03.10.2019 06:06

    Самая строгая система типов не спасает от ошибок — можно всё сломать явным приведением типа.


  1. Keynessian
    03.10.2019 06:18

    Всем известный PL/SQL является диалектом Ады.


    1. valis
      03.10.2019 11:56

      Там тоже с военщины же начинали


  1. White_Scorpion
    03.10.2019 09:58
    +3

    Компилятор выдаст ошибку, поскольку Integer и MyInt – это не одно и то же. Главное преимущество такого подхода в том, что если программист потом изменит определение типа, тысячи неявных приведений типа по всей базе кода не взорвут программу. Вместо этого нужно явно приводить типы – это пропагандирует хороший код, предотвращая смешение типов, которые «достаточно схожи».

    Только мне кажется этот пример надуманным? Что мешает программисту явно привести тип в определённых местах, после чего замена своего типа всё так же ломает поведение программы?


    ИМХО: Использование Ады это городить костыли на ровном месте, чтобы сделать элементарные вещи. Т.е. тратить полезное время на всё новое и новое изобретение велосипедов.
    В каких то случаях — это может быть оправдано, но в большинстве случаев причина использования Ады есть клинический консерватизм.


    1. xrEon
      04.10.2019 11:40

      Использование производных типов полезно, оно предохраняет от ошибок, например чтобы случайно не сложить количество бит с количеством байт. Компилятор обнаружит это сразу, если вы используете разные типы, иначе — долгие поиски в отладчике.


      1. White_Scorpion
        04.10.2019 12:02

        Любой современный компилятор находит несоответствия типов чуть ли не на шаге синтаксического анализа. К примеру любимая мною и миллионами разработчиков Visual Studio — помещает информацию о подобных подозрениях о несовместимых типах как Warnings, которые постоянно висят как напоминания после каждого билда и, которые, можно засупрессить — но надо полагать, что делая это разработчик знает что он делает.
        И для того, чтобы избежать нессоответствия типов теперь не надо оправдываться использованием древнего языка и городить кучу других костылей только для того, чтобы избежать столь примитивной ошибки.


        ИМХО!
        В прошлом использование "надёжной Ады" было оправдано тем, что компиляторы были недостаточно надёжны. Ненадёжность компиляторов не устраивала разработчиков, которые, для решения задач, в которых нужна была дополнительная степень защиты — придумывали дополнительные утилиты и ту же Аду.
        Сейчас же, при наличии современных компиляторов, хождение по граблям с Адой не так необходимо, как многие консервативные разработчики хотят нам показать. Основная фишка Ады — дополнительная степень защиты — переложена на плечи улучшенных компиляторов и даже, местами, на операционную систему. А без своей фишки Ада всего лишь просто устаревший язык. Один из многих языков, чьи задачи больше не соответствуют духу времени.


        1. oam2oam
          04.10.2019 19:52

          Я думаю, что время Ады не просто не прошло, а еще не совсем пришло… Сложно называть язык устаревшим, если он имеет в своем составе конструкции, которые еще только-только обсуждаются в самых передовых и современных языках ( задачи и механизм рандеву, дистрибутивное выполнение, контракты и верификация, исключительные ситуации, управления пулами памяти и многое, многое другое).
          И еще есть область микроконтроллеров — там Ада очень даже в тему, не сравнить ни с чем.
          Конечно, если программа должна работать один раз и ошибка не только не смертельна, но даже и не критично, то яваскрипт или C# или еще что-либо — вполне допустимы. Но если нужна надежность, скорость разработки, уверенность в результате — Ада пока вне конкуренции…
          Тот, кому доводилось хоть раз формально верифицировать свою программу, всегда будет в восторге от SPARK — и поверьте, альтернатива хоть и есть (тот же coq с автоматическим синтезом программ из доказательства) — но это ужас-ужас…


          1. White_Scorpion
            05.10.2019 03:28

            Сложно называть язык устаревшим

            Почему? Как раз таки очень просто, если учесть, что стандарт не менялся аж с какого там? 1983 года? :D


            1. oam2oam
              05.10.2019 05:53

              У вас очень-очень устаревшие сведения — последний стандарт Ada-2012, как видно, был принят в 2012 году, готовится 2020…


    1. Yuuri
      05.10.2019 01:03

      Ну всё-таки подавляющее большинство ошибок делаются нечаянно. И если какие-то языковые средства позволяют существенно уменьшить вероятность их возникновения – стоит их использовать.


      Только мне кажется этот пример надуманным?

      Вам кажется, ибо юзкейс очень распространённый. Почитайте, например, [эту статью].(https://habr.com/en/post/198568/)


  1. savelievser
    04.10.2019 09:13

    Помню в универе был курс с ним. Только там был акцент на параллельные вычисления.