Заключение

Хочется надеяться, что эта брошюра была Вам интересна. Мы коснулись различных аспектов написания надежного ПО и надеемся, что смогли продемонстрировать особое положение языка Ада в этом вопросе. Напоследок мы коснемся некоторых моментов относительно разработки языков программирования.

Баланс между программным обеспечением и аппаратной частью сам по себе вызывает интерес. Аппаратные средства за последние пол-столетия развивались ошеломляющим темпом. Они сегодня не имеют ничего общего с тем, что было в 1960-х гг. По сравнению с аппаратными средствами, ПО тоже развивалось, но совсем чуть-чуть. Большинство языков программирования сейчас имеют лишь незначительные отличия от тех языков, что были в 1960-х. Я подозреваю, что основная причина этого заключается в том, что мы очень мало знаем о ПО, хотя считаем, что знаем о нем очень много. Человечество, вложив колоссальные средства в плохо написанное ПО, вдруг обнаружило, что никуда не может двинуться с ним дальше. В то же время новая аппаратура создается с такой скоростью, что это неизбежно приводит к ее замене. Несомненно, что любой может легко научиться, как написать небольшую программку, но чтобы создать хоть какую-нибудь аппаратуру требуется овладеть огромным объемом знаний.

Существуют две основные ветви развития языков. Одна берет начало от Algol 60 и CPL. Производные от этих языков часто упоминаются в этом буклете. Другая ветвь, охватывающая Fortran, COBOL и PL/I, также еще жива, хотя и весьма изолирована.

Algol 60 можно считать самым значительным шагом в развитии. (Когда-то существовал также его менее знаменитый предшественник Algol 58, от которого произошел язык Jovial, использовавшийся в военных сферах США.) Algol дал ощущение того, что написание ПО - это больше чем просто кодинг.

При создании Algol было сделано два больших шага вперед. Во первых, появилось понимание того, что присваивание не есть равенство. Это привело к появлению обозначения := для операции присваивания. Для обозначения различных управляющих структур стали использовать английские слова, в результате отпала необходимость в многочисленных инструкциях goto и метках, которые так затрудняли чтение программы на Fortran и autocode. На втором моменте стоит остановиться более подробно.

Сначала рассмотрим следующие две инструкции в Algol 60:

if X > 0 then
   Action(...);
Otherstuff(...);

Суть в том, что если X больше нуля, то вызывается процедура Action. Независимо от этого, мы далее вызываем Otherstuff. Т.е. действие условия здесь распространяется только на первую инструкцию после then. Если нам необходимо несколько инструкций, например, вызвать две процедуры This и That, то мы должны объединить их в составную инструкцию следующим образом:

if X > 0 then
begin
   This(...);
   That(...);
end;
Otherstuff(...);

Здесь возникает опасность сделать две ошибки. Во первых, мы можем забыть добавить begin и end. Результат выйдет без ошибок, но процедура That будет вызываться в любом случае. Будет еще хуже, если мы нечаянно добавим лишнюю точку с запятой. Наверное, Algol 60 был первым языком, где использовалась точка с запятой как разделитель инструкций. Итак, мы получим:

if X > 0 then ;
begin
   This(...);
   That(...);
end;
Otherstuff(...);

К несчастью, в Algol 60 эта запись означает неявную пустую инструкцию после then. В результате, условие не будет влиять на вызовы подпрограмм This и That. Аналогичные проблемы в Algol 60 есть и для циклов.

Разработчики Algol 68 осознали эту проблему и ввели скобочную запись условной инструкции, т. е.:

if X > 0 then
   This(...);
   That(...);
fi;
Otherstuff(...);

Аналогичная запись появилась для циклов, где слову do соответствует od, а для case есть esac. Это полностью решает проблему. Теперь совершенно очевидно, что условная инструкция захватит оба вызова подпрограмм. Появление лишней точки с запятой после then приведет к синтаксической ошибке, которая легко будет обнаружена компилятором. Конечно, появление такой записи, как fi, od и esac выглядит причудливо, что может помешать серьезно отнестись к решаемой проблеме.

По какой-то причине разработчики языка Pascal проигнорировали этот здравый подход и оставили уязвимую запись инструкций из Algol 60. Они исправили свою ошибку гораздо позже, уже при проектировании Modula 2. К тому моменту Ада уже давно существовала.

Вероятно, в языке Ада впервые появилась удачная форма структурных инструкций, не использовавшая причудливую запись. На языке Ада мы бы написали:

if X > 0 then
   This(...);
   That(...);
end if;
Otherstuff(...);

Позже многие языки переняли такую безопасную форму записи. К таковым относится даже макро-язык для Microsoft Word for DOS и Visual Basic в составе Word for Windows.

Еще одним значимым языком можно считать CPL. Он был разработан в 1962г. и использовался в двух новых вычислительных машинах в университетах Кембриджа и Лондона.

CPL, (как и Algol 60) использовал := для обозначения присваивания и = для сравнения. Вот небольшой фрагмент кода на CPL:

§ let t, s, n = 1, 0, 1
  let x be real
  Read[x]
   t, s, n := tx/n, s + t, n + 1
   repeat until t<<1
  Write[s] §

Интересная особенность CPL заключается в использовании = (вместо :=) при задании начальных значений, ввиду того, что при этом не происходит изменения значений. В CPL был ряд интересных решений, например, параллельное присваивание и обработка списков. Но CPL остался лишь академической игрушкой и не был реализован.

Для группировки инструкций язык CPL использовал запись, аналогичную принятой в Algol 60. Например

if X > 0 then do
   § This(...)
     That(...) §⃒
Otherstuff(...);

Для этого использовались странные символы параграфа и параграфа перечеркнутого вертикальной чертой.

Хотя сам язык CPL никогда не был реализован, в Кембридже изобрели его упрощенный вариант BCPL (Basic CPL). В отличии от имеющего строгую типизацию CPL, BCPL вообще не имел типов, а массивы были реализованы с помощью адресной арифметики. Так BCPL положил начало проблеме переполнения буфера, от которой мы страдаем по сей день.

BCPL преобразился в B, а затем в C, C++ и т. д. В BCPL использовалось обозначение := для операции присваивания, но по пути кто-то забыл, в чем смысл, и C остался с обозначением =. Поскольку знак = оказался занят, для операции сравнения пришлось ввести обозначение ==, а вместе с этим получить букет проблем, о котором мы писали в главе «Безопасный синтаксис».

Язык C унаследовал принцип группировки инструкций от CPL, но заменил его странные символы на фигурные скобки. То есть в C мы напишем

if (x > 0)
{
   this(...);
   that(...);
};
otherstuff(...);

Что же, в C от CPL практически ничего не осталось, ну разве что скобочки для группировки инструкций.

В заключение отметим, что использование знака равенства для обозначения присваивания однозначно осуждал Кристофер Страчи, один из авторов CPL. Много лет назад, на лекциях NATO, он сказал: «То, как люди учатся программировать, отвратительно. Они снова и снова учатся каламбурить. Они используют операции сдвига вместо умножения, запутывают код, используя битовые маски и числовые литералы, и вообще говорят одно, когда имеют в виду что-то совсем другое. Я думаю у нас не будет инженерного подхода к разработке ПО до тех пор, пока у нас не закрепятся профессиональные стандарты о том, как писать программы. А добиться этого можно лишь, начиная обучение программированию с того, как писать программы должным образом. Я убежден, что, в первую очередь, необходимо начать говорить именно то, что вы хотите сказать, а не что-то другое.»

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