среда, 31 октября 2007 г.

Простота и сложность: завершение

Коллеги, я бы хотел постепенно завершить дискуссию «Простота и сложность». Во-первых, все самое главное, кажется, уже сказано, и повторять соображения еще раз не стоило бы – как мне, так и моим комментаторам. Во-вторых, хочется постепенно перейти к темам, обозначенным в самом первом моем посте (и вообще, приблизиться к компиляторной тематике :-)), и потому нужен некий перерыв, чтобы собраться с мыслями.

Конечно, отвечать на комментарии было бы логично в соответствующем разделе. Но развернутый ответ там не напишешь: получается слишком длинно и «некрасиво», потому приходится делать отдельный пост.

Прежде всего, должен извиниться за безграмотность.
Вот что мне написали в комментариях:

> Си: линейная последовательность функций. Си-функция не может хранить собственный контекст между своими вызовами (это «stateless»-компонент), поэтому для обеспечения сколько-нибудь нетривиального взаимодействия приходится активно использовать глобальные переменные.

То есть как так? А как же static-переменные внутри функций? Они же инициализируются один раз при первом вызове функции, а затем сохраняют своё значение между вызовами функций:

Виноват, совсем забыл про локальные статики. Каюсь. Насчет того, что функции – это stateless-компоненты, дурака свалял. Вообще, весь процитированный абзац выглядит не слишком логичным: взаимодействие функций никак не может быть связано с их собственными контекстами; каждый такой контекст служит как раз для сохранения значений между вызовам самой этой функции и недоступен другим.

Кроме того, и к самой теме поста – «какая модульность мне нужна» - этот пассаж не имеет особого отношения. Функции в Си если и могут быть названы модулями, то разве только совсем уж «кончеными» фанатами Си. :-)

Править основной текст после появления справедливых комментариев – не слишком корректно, потому ограничусь этим ответом.

Комментарии AVP и мои комментарии на комментарии :-)

Да, в Обероне есть модули. Очень хорошее средство; где-то я уже писал, что считаю модульность (не путать с возможностью раздельной компиляции компонент!) исключительно важным свойством языка, которое не менее (а иногда и более) существенно, нежели, скажем, поддержка ООП.

С тем, что модульность иногда важнее ООП, согласен. Но не понимаю, чем Вам раздельная компиляция "не угодила"? :)

Да почему же не угодила? Нормальное средство для Си и подобных языков, естественно вытекающее из линейной структуры программ и практических потребностей разработки. Другое дело, что это вполне, так сказать, «безыдейное» средство: грубо говоря, разрезать программу на части можно почти произвольным образом (в языке нет явных правил оформления этих частей) и положить их в разные места, сказав, что эти части будут компилироваться раздельно. Кроме того, этого явно недостаточно; об этом, собственно, и был предыдущий пост.

Возможно, здесь просто терминологическое расхождение (иногда раздельной (separate) компиляцией называют то, что - в терминологии Вирта - называется независимой (independent) компиляцией).

Ну, примерно так. Мне бы хотелось, чтобы в языке были развитые средства структурирования программ (та самая модульность), а уж техническая возможность компилировать структурные компоненты программы по отдельности подразумевалась бы как естественное следствие этой модульности. Собственно, примерно как в Аде. Модуль (пакет, подпрограмма, задача) – это языковое понятие; раздельная компиляция – больше требование к среде программирования: - к компилятору, линкеру и т.д.

Попытки назвать модулями произвольные наборы функций и глобалов в Си, по какой-то причине сложенные в один текстовый файл и компилируемые отдельным вызовом компилятора, выглядят с одной стороны, явно архаично и, с другой стороны,- до смешного претенциозно.

Но вот достаточно ли простого набора одинаковых сущностей (и опять, как и в Си, линейного), чтобы адекватно отражать многообразные отношения между частями создаваемой системы?

Почему это набор модулей линейный, как список функций в Си? Между модулями существуют отношения экспорта/импорта, благодаря которым (1) они образуют иерархическую расширяемую структуру (даг), (2) корректный порядок инициализации модулей гарантирован.

Конечно, линейный. Отношения импорта/экспорта сами по себе не дают нового качества. Функции в Си характеризуются такими же по сути отношениями импорта/экспорта: каждая функция экспортирует свое имя и импортирует имена всех доступных функций и глобалов. Оберон-модули как таковые не образуют расширяемую иерархическую структуру: такую структуру образуют расширяемые типы внутри модулей. Структура Оберон-программы, скорее, маскирует, скрывает расширяемую иерархию типов. Иными словами, сейчас Оберон-модули – это однородный набор контейнеров, по которым распределено дерево типов.

Если вывести типы из-под «власти» модулей и сделать сами эти типы модулями (записи, собственно, и так являются концептуально вполне самостоятельными сущностями, потому это выглядит, на мой взгляд, очень естественно и логично), то общая структура программы станет более ясной и адекватной задаче, реашемой этой программой. Примерно так и сделано в Зонноне.

Так, да не совсем: расширяемые записи (аналог классов в более распространенных языках) не являются средством структурирования программы в целом.

Слишком категорично сказано, вызывает сомнение.

Да вроде не слишком и категорично? Оберон-программа – это же набор одинаковых модулей-синглтонов, ведь так? То обстоятельство, что внутри этих модулей, так сказать, «кипит жизнь» в виде расширяемых записей и связанных с ними («примкнувших к ним» :-)) процедур, не меняет этой общей картины? Снаружи – линейный набор контейнеров, внутри конетейнеров – расширяемые типы, смесь «обычных» и связанных процедур и прочего...

Особенно, если вовремя вспомнить об обероновской программной шине или (на тему того, что модули как-то ограничивают записи) инсталлируемых каталогах объектов (например, в BlackBox).

Я, честно говоря, не очень осведомлен насчет решений, воплощенных в Блэкбоксе, но подозреваю, что каталоги объектов – это, судя по названию, средство, помогающее программисту увидеть, какие же типы содержатся в модулях программы и каковы отношения между этими типами. Иными словами, если я прав, то Блэкбокс как раз пытается преодолеть тот недостаток Оберона, о котором я и говорю.

А мне думается, что обероновское решение, дублированное в Аде, вводит принципиально другую (новую, если сравнивать с Си/Си++) архитектуру ПО: "тэгированную" (типизированную) память.

Наверное, можно сказать и так, но я не вижу здесь особенной разницы. Почему плюсовая архитектура не вводит эти типизированную память? По-моему, вводит. Кроме того, в моем посте речь шла, скорее, о средствах и возможностях структурирования программ (о модульности в широком смысле), а не об ООП-подходах.

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

А что значит программировать объектно? Возьмем в качестве примера (абстрактную) современную графическую ОС, основанную на обмене сообщениями. Ее объектная ориентированность вроде бы не подлежит сомнению. И на каком языке ее лучше программировать: на Си++ с его first-class классами :) или Обероне с его расширяемыми записями и проверкой динамического типа всего за одно сравнение? Можно даже сказать, что обероновская программная шина представляет собой простейшую реализацию схемы двойной диспетчеризации.

Честно говоря, я бы уклонился от общей дискуссии об ООП; наверное, я не чувствую себя для этого достаточно подготовленным (это все-таки не совсем компиляторная тема :-)). Пишу «объектно» вполне уверенно, а вот рассуждать и дискутировать... Извините.

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

Надеюсь, из сказанного примерно понятно, чего мне недостает в Обероне.

Если я правильно понял, в Обероне Вам в основном недостает (1) более разнообразной (специалированной) модульности и (2) классов.

Примерно так. Только я не стал бы разделять эти два пункта. Мне представляется, что «класс» в том виде, как он есть в том же Си++, должен иметь статус полноправного (специализированного) модуля, наряду с другими.

Честно говоря, с первым согласиться легче. :)
Второе, по видимости, движение в том же направлении (по Мейеру, класс = тип + модуль), но вызывает большее сомнение: как уживутся "две женщины на одной кухне" (класс и модуль :) ).

Что значит «уживутся»? Они друг другу никак не мешают, скорее необорот, помогают и дополняют, и вместо Вашего образа двух женщин на одной кухне я бы предложил, скажем, двух рабочих, несущих что-то тяжелое. Любой из них в одиночку груз даже не поднимет, а вместе они его не просто подняли, но даже и перенесли и поставили куда надо...

Ведь тот же Си++, где классы являются объектами первого рода, - язык определенно не модульный.

Конечно, не модульный, но не потому, что там классы, а потому, что он прямой потомок Си и сохранил всю его архаику. Классы в этом смысле – только добавка. Важная, принципиальная, фундаментальная – но только в том, что касается ООП. В плане же модульности плюсы, к сожалению, ничего к Си не добавили. В конце концов, ведь известный слоган «Си с классами» в применении к плюсам именно об этом.

Не могли бы Вы подкинуть немного "инсайдерской" информации и ответить на давно мучающий меня вопрос? :)

Почему в Обероне нет шаблонов - понятно. Но почему в нем не прижились даже "облегченные" дженерики, предлагавщиеся в середине 90-х Шиперским?

Я задавал когда-то подобный вопрос Гуткнехту, но, к сожалению, не понял ответа (вероятно, помешал языковой барьер).

Вряд ли я могу что-то сказать определенное. Основная цель Вирта, как я понимаю,- оставить в языке только фундаментальные, сущностные свойства. Между прочим, в этом смысле, я скорее бы понял, если бы он ввел в язык как раз полноценные шаблоны – все-таки generic programming вполне можно считать одной из фундаментальных программных парадигм. (Правда, при создании Оберона это было, мягко говоря, не очевидно.) А что говорить об облегченных дженериках, если он перечислимые типы и цикл for удалил?..

Я несколько раз говорил с Виртом насчет generic programming (пытался рассказать ему, о чем, собственно, читаю лекции в ETH :-)), но особого интереса не почувствовал. Он когда-то общался с Александром Степановым, и мне показалось, что и тот не смог объяснить ему, чем важны и хороши дженерики... Кроме того, у Вирта, если я правильно ощутил, есть какое-то неприятие Степанова лично. Впрочем, не уверен, могу ошибаться. Возможно, он просто не считает GP столь уж важным подходом и потому относится не слишком серьезно и к его создателю.

суббота, 20 октября 2007 г.

Простота и сложность (2)

Позавчера пришел такой комментарий:

> Для их создания приходится использовать адекватные инструменты, которые не могут не соответствовать сложности и ответственности задач и потому объективно не могут быть простыми. (это был мой текст)

Ну а если говорить в плоскости Java и C++? Первый язык значительно проще плюсов, и, при этом, на нем решаются действительно сложные задачи. В принципе, на том же Обероне Вирт реализовал реально работающую ОС -- проект отнюдь не маленький. Тот же Страуструп часто говорит, что язык, в конечном счете, вторичен, это всего лишь инструмент.

Кстати, с большим интересом прочитал бы ваше мнения о Java и C# как о языках (в некотором отрыве от виртуальной машины и фреймворка).

Во-первых, я не хотел бы «сваливаться» в неформальное сравнение языков (обещание себе дал :-). Если уж сравнивать, то глубоко, предельно конкретно (по существу), тщательно и непредвзято. Для этого нужен традиционный формат журнальной статьи, причем в журнал уровня Software: Practice & Experience или по крайней мере C++ User’s Journal, но уж конечно, не заметка в блог. (Кстати, что-то не припомню подобного сравнительного анализа!- по большей части, встречал только что-то вроде «Critique of C++» или известные едкие пассажи про «Жабу»...)

Тем не менее, кое-что сказать можно. Конечно, Java проще и стройней (и чисто по-человечески – мне, по крайней мере - писать на ней, как и на C#, не в пример удобнее и приятнее, чем на плюсах). Что же касается решения сложных задач, то их ведь решают на всех языках – и на чистом Си, и на Обероне, в том числе. Я лично знаю человека, который один написал больше полумиллиона строк на ассемблере БЭСМ-6 - и его программа была вполне рабочей, даже популярной - несколько десятков инсталляций по бывшему Советскому Союзу.

Вопрос, в конечном счете, заключается в том, какова цена решения, основанного на том или ином языке: какова вероятность завершения работы в заданный срок, насколько такое решение надежно, масштабируемо, развиваемо, сопровождаемо – прежде всего, в долговременном плане. Если брать в рассмотрение эти критерии, то мне все-таки кажется, что «простые» языки... ну, скажем мягко, не выглядят полностью адекватными им (этим критериям).

Насчет вторичности языка. Язык, конечно, вторичен, однако встык к Вашей цитате из Страуструпа я бы поставил цитату из Дейкстры (под рукой нет первоисточника, передаю смысл): «язык – это инструмент, но это такой инструмент, который оказывает глубокое (и тонкое!) влияние на мышление того, кто им пользуется». Рискну проиллюстрировать тезис классика вполне тривиальным примером.

Пусть вам нужно запрограммировать группу логически связанных действий, которые работают над некими общими для них данными. В простом Си у вас нет другого способа реализовать эти действия, кроме как определить данные в виде глобальных переменных (или структуры, неважно), а действия представить в виде простого набора функций. Если нужно как-то обособить этот код от других частей программы (уж не говорю – оформить его как независимый модуль!), то имеется, по существу, единственное решение -вырезать этот код (ножницами :-)) из программы, положить его в отдельный файл, а для «склейки» программы использовать текстовые инклюды. Написаны десятки статей, объясняющих недостатки и опасности такого решения, не буду повторять их,- но ведь в Си ничего другого нет! Ну, в крайнем случае можно попытаться откомпилировать этот фрагмент отдельно от основной программы, а потом прилинковать его; однако не всегда это возможно, а очень многие проблемы при этом остаются.

Так вот, значит ли это, что Си нельзя использовать при разработке сложных программ? Конечно, не значит!- используют же. Но в этом случае на объективную сложность решаемой задачи будут накладываться сложности, порожденные бедным набором выразительных средств и возможностей инструмента. То есть, язык не только не помогает«бороться со сложностью задачи», но еще и привносит в решение собственные проблемы... Получается, как и сказано у Дейкстры: инструмент оказывает (в данном случае негативное) влияние на мышление программиста по поводу его прграммы.

Давайте пойдем чуть дальше; заодно я отвечу на один недавний комментарий. Вот он:

AVC>Интересно, почему модульности очевидно недостаточно для борьбы со сложностью?

ЕЗ>Э-э-э... А что, достаточно? То есть, в языке достаточно иметь модули, и они одни способны решить все проблемы создания больших программ? Я что-то не понял реплики, простите.

Вопрос был не о всех проблемах создания больших систем, а конкретно о борьбе со сложностью. (Мне просто хотелось услышать обоснование. А то о Си++ Вы пишете много, а об Обероне только одно слово, что его очевидно недостаточно. :) )
Что касается борьбы со сложностью.
Возможно, я не в курсе, но мне известен только один способ борьбы со сложностью: делить сложное целое на части так, чтобы части не вмешивались в дела друг друга. Для этого нужны прежде всего границы, а границы и суть модули.
Интересно, что еще (кроме модулей) имеет отношение непосредственно к борьбе со сложностью?


Да, в Обероне есть модули. Очень хорошее средство; где-то я уже писал, что считаю модульность (не путать с возможностью раздельной компиляции компонент!) исключительно важным свойством языка, которое не менее (а иногда и более) существенно, нежели, скажем, поддержка ООП.

Так вот, давайте тупо (с некоторым огрублением) сравним типичную структуру Си-программы и структуру Оберон-программы.

Си: линейная последовательность функций. Си-функция не может хранить собственный контекст между своими вызовами (это «stateless»-компонент), поэтому для обеспечения сколько-нибудь нетривиального взаимодействия приходится активно использовать глобальные переменные.

Чем сложнее Си-программа, тем из большего количества функций она состоит. А скажите, где вы видели действительно сложную систему, архитектура которой адекватно представлялась бы линейным набором функций? Вот и получается, что у Си-программиста имеется крайне ограниченный набор выразительных средств, и ему приходится редуцировать, «сводить» системную архитектуру – как правило, очень сложную и нетривиальную – к той, которую способен реализовать его инструмент. Помогает ли ему инструмент «бороться со сложностью»? Привносит ли он в реализацию собственные проблемы? Вопросы риторические, ответы очевидные.

Оберон: одноранговый набор модулей-«синглтонов» (то есть, каждый модуль присутствует в работающей программе в единственном числе). Каждый модуль может хранить собственное состояние (контекст). "Активные" компоненты модуля - процедуры. Гораздо лучше, чем в Си, никто не спорит. Но вот достаточно ли простого набора одинаковых сущностей (и опять, как и в Си, линейного), чтобы адекватно отражать многообразные отношения между частями создаваемой системы? Тем более, что, собственно, какой-либо ассортимент этих сущностей отсутствует: есть только модули.

Возразят: в Обероне есть поддержка ООП, которая вводит новое качество: можно организовывать более сложные и многобразные схемы взаимодействия компонент, более удобно развивать систему, не подвергая опасным модификациям написанное ранее.

Так, да не совсем: расширяемые записи (аналог классов в более распространенных языках) не являются средством структурирования программы в целом. Они существуют, по сути для того же, для чего предназначены были обычные записи: для представления данных. Добавились просто более продвинутые средства организации работы с этими данными и средства наращивания этой функциональности. Для целей организации программы служат те же модули, и только они. Записи остаются сущностями "второго сорта" - их можно экспортировать в другие модули и там расширять, но это не меняет общей ситуации: "объектные" записи всегда заключены внутри модулей и, тем самым, никак не помогают строить программу. В точности та же картина, кстати, и в Аде (хотя в ней других средств организации программы изначально было гораздо больше).

Можно сказать, что и Вирт, и авторы второй (1995 года) редакции Ады приняли одно и то же проектное решение: вводя в язык новые понятия (а в том, что касается создания сложных систем, ООП - принципиально новое понятие, пусть это было осознано не сразу), делали это, стараясь как можно меньше изменить концептуальный базис существующего языка. Они рассуждали примерно так (Вирт, по крайней мере, именно так): зачем вводить "классы", если уже есть записи?- давайте введем "расширяемые" записи (в Аде - "тегированные"). Смысл тот же, а число базовых понятий не увеличилось. Зачем "функции-члены"?- пусть останутся обычные подпрограммы, только сделаем их "связанными с типом". В Аде, кажется, вообще даже нового термина не ввели, обошлись словесными объяснениями, как подпрограммы должны работать с параметрами тегированных типов.

(Авторы Ады ужасно гордились, что они сделали свой язык объектно-ориентированным, добавив в него всего лишь шесть, кажется, новых служебных слов. Нашли чем гордиться: Вирт не добавил вообще ни одного...)

Такой подход кажется концептуально очень "чистым" и грамотным; он правилен и в практическом ключе: обеспечивается совместимость со старыми версиями языков. Однако, это решение оказалось очень ограниченным: "объектность" в обоих языках осталась "гражданином второго сорта", будучи всегда заключенной в рамки "более равных" программных единиц. Поэтому, между прочим, на обоих языках программировать объектно оказалось, попросту говоря, крайне неудобно. Как только потребности решаемой задачи выходят за рамки красивых и понятных примеров из руководств, начинается сущее мучение. В Аде, якобы, хотят как-то улучшить "юзабилити", предлагая нотацию вида "object.method", но для этого надо ждать нового стандарта...

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

Надеюсь, из сказанного примерно понятно, чего мне недостает в Обероне. Возвращаясь к процитированному выше комментарию: действительно, есть "только один способ борьбы со сложностью: делить сложное целое на части так, чтобы части не вмешивались в дела друг друга". Проблема в том, что делить сложное нужно очень многими различными способами. И "в дела друг друга" приходится вмешиваться, и тоже по-разному. Модули хороши, но простых модулей недостаточно.

Чтобы не упрекали в голом критиканстве, вот очень приблизительный список того, что хотелось бы иметь. Может, чего-то упустил, а что-то из перечисленного на самом деле и ни к чему - не судите строго, это все-таки не статья в журнал. :-)

Обобщая: различные виды модулей и развитые средства задания отношений между модулями. Скажем, в Аде можно делать вложенные модули (самый простой вид отношений), в последней Аде появились дочерние модули. В ней же есть задачи – модульные средства задания параллельного выполнения – и соответствующие средства синхронизации. В дополнение к задачам имеются "защищенные" модули (программируемый аналог монитора Хоара). В ООП-языках вводятся отношения наследования (пусть далеко не везде классы являются одновременно модулями, но сейчас важна идея). Вообще же классы должны быть first-class (полноправными) entities. Так, в Зонноне есть традиционные модули, интерфейсы, их реализации («обычные» классы), и еще есть средства задания протоколов взаимодействия модулей (в том числе, и асинхронных взаимодействий). В Аде модулем может выступать как «пакет» или «задача», так и одиночная подпрограмма. В ней же есть separate-подпрограммы (пусть с довольно слабой семантикой и с ограничениями).

Еще раз повторяю, сейчас речь идет не "вообще", а именно об адекватности инструмента большим и сложным задачам. "Вообще" же Оберон мне очень нравится: помимо аккуратного дизайна, он несет в себе какую-то позитивную ауру, что ли... Программируя на нем, чувствуешь себя комфортно и уютно. Чего и близко нет в Си. Но это - личное, прошу прощения. :-)

пятница, 19 октября 2007 г.

Annotated C# Standard

Вот, сегодня пришло извещение от Amazon:




C# Annotated Standard (Morgan Kaufmann) Jon Jagger
EUR 47,03

Судя по заголовку, практически то же, что я задумал со стандартом C++. Жалко, что не выложили какую-нибудь главу (только предисловие и индекс); интересно было бы взглянуть, как они текст/комментарии организовали. Придется, наверное, саму книгу заказывать, чтобы было с чего пример брать. :-)

Вот, кстати, синопсис. Английскими словами - практически то же обоснование, что и я выдвигал по поводу Аннотированного Стандарта C++:

Synopsis
Standards, while being definitive, do not usually serve as the best reference to the use of a programming language. Books on languages usually are able to explain usage better, but lack the definitive precision of a standard. This book combines the two; it is the standard with added explanatory material. Written by members of the standards committee, it annotates the standard with practical implementation advice. It is the definitive reference to the C# International Standard.

пятница, 5 октября 2007 г.

C++ и Ada: вынужденный пост

Собирался уже написать очередной пост, как пришел такой комментарий:

OCTAGRAM комментирует...

Для их создания приходится использовать адекватные инструменты, которые не могут не соответствовать сложности и ответственности задач и потому объективно не могут быть простыми.
(Это был мой текст)

Ну так сравните сложность C++ и сложность Ады.

Э-э-э... А зачем? Я разве что-то говорил про то, что какой-то язык сложнее, а какой-то проще? Не дождетесь. :-) Во всяком случае, ничего дурного про Аду у меня не было,- скорее уж, над C++ и над его автором иронизировал...

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

На C++ я немного уже потоптался, вот вам про Аду.

В C++ мы должны не забывать ставить проверки.
В Аде мы проверки опционально снимаем.

А кто мешает assert'ов понаставить куда надо? Они, вроде, тоже по предопределенному макросу включаются/отключаются?
Тяжелее, согласен, и программу загромождает. Зато все явно выписано, и не надо помнить, как в Аде, какие именно проверки делаются, и где, а где не делаются...

В C++, чтобы сделать объект некопируемым, мы переопределяем operator= и конструктор копирования в приватной части.
В Аде мы пишем limited.

Замечательно! Красивое служебное слово, все наглядно.
Только вот в плюсах действует общий механизм задания собственных версий для всех операций, включая присваивание, и общие для всех правила управления доступом. А в Аде присваивание и операцией-то не считается, и, стало быть, пришлось для него отдельный limited-механизм вводить. Что лучше: набор различных средств для, в общем-то, одного и того же или единые (и вполне, кстати, ясные) правила для всех операций?..

В C++, чтобы определить метод как виртуальный, мы пишем =0 после прототипа.
В Аде мы пишем is abstract.

Согласен. Красиво и ясно. Страуструп служебные слова экономил, на мелочи их не тратил...

В C++ тела настраиваемых методов должны быть в ашниках, а ненастраиваемых — в цппшниках.
В Аде все (видимые снаружи, я имею в виду) определения в .ads, все тела в .adb.

Да никто и не спорит, что в Аде модульность лучше. (Только вот .ads и .adb - это не в Аде, а в GNATе. В стандарте на этот счет ничего нет.) В плюсах этот архаичный механизм заголовочных файлов целиком остался от предшественника, ничего не поделаешь, если надо было обеспечить совместимость.

Только вот делать на основании этого вывод, что C++ "сложнее" Ады - как-то не слишком убедительно. В ответ я мог бы в красках рассказать, как нелепо в Аде сделана поддержка ООП (кто-то заметил, что программировать в объектном стиле на Аде - занятие не для слабонервных, и это истинная правда).... И, кстати, генезис этой нелепости тот же самый: надо было встроить (а на самом деле пристроить) поддержку ООП в уже существующий язык (Ада83) с тем, чтобы непременно обеспечить обратную совместимость - прямо как с C и C++...

В C++ нельзя подключить пространство имён в ашнике. (точнее, можно, но по головке за такое не погладят)

Э-э-э... А кто мне запретит, если мне это необходимо сделать? Прибегут хмурые дядьки и по лицу настучат? Конечно, то что Вы упоминаете - не самое лучшее, что может сделать программист, но, вроде, запретов-то и нету?

В Аде ... ну, ясен пень, такая тупость только в C++ может быть, в Аде всё в порядке.

Ваш тон, простите, много о Вас рассказывает. Можно и не отвечать дальше...

Почитайте внимательно правила использования with/use в Аде, правила, связанные с областями действия имен и видимости идентификаторов. Уверяю Вас, они ничуть не проще и не яснее плюсовых. Опытные Ада-программисты с трудом разбираются...

В C++ простые аргументы нужно передавать по значению, а большие — по ссылке, иначе они полностью копируются.

Ну, так и передавайте по ссылке, трудно, что ли? Можете слово const добавить, если модифицировать не собираетесь. Все нагляднее, чем про in по умолчанию помнить.

В Аде входные параметры (с модификатором in по умолчанию) доступны только для чтения, поэтому копировать их не нужно. А уж как их передать на машинном уровне — это дело компилятора.

Так и в плюсах стандарт явно отдает механизм передачи параметров по ссылке на откуп компилятору. В чем преимущество-то, не пойму. Функционально эквивалентные механизмы.

В C++, чтобы настроить шаблон дробным числом, необходимо настроить его ссылкой на это дробное число.

Ну и настройте ссылкой!
(Уж молчу о том, кому и зачем может такое понадобиться...)

В Аде ... ну, ясен пень, такая тупость только в C++ может быть, в Аде всё в порядке.

Опять... Как же это часто встречается - эта великолепная уверенность в собственном праве припечатывать ярлыки куда и кому угодно! Невзирая, так сказать, на лица. "Тупость"... Не знаю, видели ли Вы один старый фильм, в нем герой говорит примерно следующее:

- То и дело слышишь: Тургенев не разобрался, Толстой недопонял... Как будто в истории орудовала банда двоечников!

В C++ namespace может быть одним, имя цппшника/ашника другим, а объектный файл, который нужно не забыть подключить — третьим.
В Аде между всеми тремя несложное соответствие. Да и то, реально знать нужно только namespace.

Это, уважаемый, вы что-то не то сказали, воля Ваша. С стандарте нет ни слова об именах файлов-носителей Ада-программ и, тем более, о каких-то "соответствиях" между ними и именами в программе. Это все заморочки GNAT'а, и явно не самые удачные. По-моему опыту, требование называть файл с Ада-пакетом именем самого пакета - ужасная нелепость и неудобство. Семантически, файл как контейнер, и единица компиляции как его содержимое - совершенно различные сущности, и любые правила их "совместного" именования,- ничем не оправданы. Да, компилятору и линкеру можно не быть слишком умными (программист должен сам позаботиться, чтобы назвать файлы так, как им, а не ему, удобно). Но еще хуже, что одно неудачное проектное решение тянет за собой другие, вынужденные. Скажем, наличие ограничения "8+3" на имена файлов в некоторых ОС автоматически приводит к необходимости некоего соглашения: как надо сократить-искорежить имя Ада-единицы, чтобы полученным словом-уродцем поименовать файл, ее содержащий... А тут еще и дочерние единицы появились, с составными именами! Их-то как корежить, по новым каким правилам?

Это лишь несколько примеров к слову об адекватности сложности C++,

Так вот в чем дело, оказывается! Вы читаете между строк!
Вам показалось, что смысл моего поста был в том, чтобы заявить об адекватности C++ уровню сложности современных задач? И где именно Вы это увидели?..

Писал же (уж яснее, казалось бы, некуда) о C++ и Обероне как о двух крайних подходах к преодолению объективной сложности. Скепсис выражал и о том, и о другом. Критиканствовал даже. И на тебе - увидели апологетику C++...

Что ж... я уже не раз сталкивался с подобным эффектом: в самых невинных, самых прямых и бесхитростных моих словах ищут и находят какой-то второй смысл, неясные намеки и т.д. Так вот, во избежание подобных казусов в будущем, хочу заявить: в моих текстах никаких намеков, подтекстов, вторых планов и прочей литературщины нет. То, что написано (и только это), я и хотел сказать. Можете считать, что я слишком глуп, чтобы делать тонкие намеки, организовывать подтексты и выстраивать вторые планы...

которой здесь было посвящено столько воды.

Неужто много воды? Дык... Это все-таки блог, а не статья в журнал. Жанр другой. Свободный, так сказать, полет мысли. Извините, что отнял у Вас время. Однако, хочу честно предупредить, что и впредь намерен продолжать в том же духе. :-) Так что придется Вам теперь что-то другое читать, где воды поменьше... Найдете, поделитесь ссылочкой, please.

Ну, и в заключение:

А про Оберон (про первый) ещё Жан Ичбиа, создатель Ады говорил :
"There are times when Wirth believes in small solutions for big problems. I don't believe in that sort of miracle. Big problems need big solutions!"

Ну так и весь мой предыдущий пост про то же! - почти теми же словами...
Что Вам не понравилось-то, не пойму?
Что я Аду не похвалил? :-)

P.S. Написал все это и подумал: какие-то все замечания мелкие. Ерунда, в общем. Придирки... Почему было не взять что-то очевидное, чтобы никто был не в силах возразить? Cравнить, скажем, модульность (аккуратно проработанную в Аде и попросту отсутствующую в плюсах)? упомянуть механизм подтипов - простой, но удивительно полезный в умелых руках, аналогов которому нет, кажется, больше нигде? Да и многозадачность - непростую в освоении, но надежную и полностью встроенную в язык?..

P.P.S. Не хотел, честное слово... Обещал же себе не участвовать в религиозных войнах. Последний пост такой. Больше не будет.