вторник, 6 ноября 2007 г.

"Древовидный" C++

Сергей Прохоренко в комментарии написал:

Вот несколько моих постов в форумах, которые могут быть полезны для Вашей разработки «древовидного» Си++...

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

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

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

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

Именно этому – своему проекту, которым я занимаюсь несколько последних месяцев – я хотел бы посвятить ближайшую серию постов (почти уже готовых). Речь там пойдет только и исключительно о том, как половчее представить программу на ЯП в виде дерева (а на самом деле, конечно, графа) и какие преимущества можно из такого представления получить.

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

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

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

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

(Между прочим, редактор Visual Studio включает элементы «древовидного» подхода – фрагменты текста программы, соответствующие отдельным синтаксическим конструкциям, можно избирательно сворачивать и разворачивать. К сожалению, набор языковых конструкций, управляемых редактором, ограничивается пространствами имен, классами, функциями и комментариями и не распространяется на «внутренности» функций. Скорее всего, нечто подобное и в Эклипсе есть; может, даже и получше, чем в VS?)

Недавняя статья Страуструпа

Evolving a language in and for the real world: C++ 1991 - 2006
Ссылка: http://www.research.att.com/~bs/hopl-almost-final.pdf
Статья большая, почти 60 страниц.
Написана для очередной конференции по истории языков программирования.

Первые главки статьи повторяют некоторые ранние статьи и его книгу "Дизайн и эволюция С++", но интерес также представляет информация, относящаяся к процессу принятия нового Стандарта C++.

В целом, статья, безусловно, стоит того, чтобы ее внимательно прочитать.

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

C++'s success in real-world use also led to influences that are less beneficial. The frequent use of ugly and irregular C++ style of syntax in modern languages is not something I'm proud of. It is, however, an excellent indicator of C++ influence - nobody would come up with such syntax from first principles.

Удивительно видеть такую трезвую и недвусмысленную оценку «большого стиля» C++ от самого создателя этого языка! И одновременно, с чувством глубокого удовлетворения наблюдать такой классный пинок тем, кто копирует «ugly and irregular style of syntax» из примитивно понятых «маркетинговых» соображений... Все ведь понимают, в чей огород этот камень.

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

понедельник, 5 ноября 2007 г.

XML как замена всему :-)

А вообще, конечно же, синтаксис надо давить.
Без синтаксиса лучше.
По этой причине Лисп и стоит выше всех прочих, вместе взятых
(Из комментариев)


- Авек плезир! - отозвался Фагот, - но почему же
с вами одним? Все примут горячее участие!
(«Мастер и Маргарита»)



А почему же только Лисп? См. второй эпиграф.
А почему только синтаксис? Почему не задавить заодно и семантику? :-)

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



Program ::= List
List ::= '(' { Element } ')'
Element ::= List | Atom


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

Насколько это упрощает/усложняет программирование и чтение Лисп-программ – давайте не будем касаться этого вопроса. Сейчас мне хочется просто отметить интересное сходство Лиспа и... XML.

В самом деле, XML ведь тоже (опять-таки, если не брать во внимание второстепенные технические детали), предельно прост:



XMLDocument ::= Element
Element ::= StartTag { Content } EndTag
Content ::= Element | NonstructuredText


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

У XML, в отличие от Лиспа, нет собственной семантики – это всего лишь язык разметки. Смысл XML-документу придают агенты, его воспринимающие – либо люди, либо программы, с ним работающие. Но отсутствие семантики парадоксальным образом оборачивается невиданным расширением сфер использования: с помощью XML можно описать практически все на свете; главное – договориться, как именно описывать и как понимать/интерпретировать такое XML-описание. Для первого служат DTD, Schema или различные неформальные соглашения, для второго – шаблоны XSLT плюс смежные технологии, а также стандартизованные программные интерфейсы – DOM, SAX и подобные.

Вообще-то писать на эту тему можно очень много, но сейчас я хочу только лишь указать на проект Superx++ (http://xplusplus.sourceforge.net/), интересный как раз попыткой предложить XML как замену синтаксиса всеми нами любимого языка C++.

Собственно, смысл проекта достаточно прост: предлагается нотация, полностью заменяющая синтаксис C++ эквивалентными XML-конструкциями. Правда, более внимательное чтение примеров не подтверждает полной замены: автор не решился «xml-изировать» язык целиком и синтаксис выражений оставил в исходном виде. Более того, в какой-то момент он, похоже, испугался собственного радикализма и предложил некий «промежуточный» вариант под названием shortx, в котором язык переведен в XML-нотацию только частично...

Вот, полюбуйтесь: объявление класса в XML-нотации. Исходное, «плюсовое» объявление легко восстановить.





<class name="XTree">
  <scope type="public">
    <func name="GetSize" type="int">
      <return>
        <eval member="Size" object="this">
      </return>
    </func>
    <var name="Size" type="int">200</var>
  </scope>
</class>




Отметим, что (само)уверенности автору не занимать: одна из его первых статей на эту тему называется так: x++: The World's First Full XML-Based Programming Language Released!

(Сначала язык назывался X++, но это имя оказалось занято более «авторитетными» компаниями, и автору пришлось сменить его на superx++.)

Что касается собственно проекта Superx++, то его реализация не вышла из беты, а с 2004 года он, кажется, не развивается; по правде говоря, трудно представить себе какое-то реальное практическое применение такого парадоксального подхода... В общем, любопытна только идея как таковая, а также некоторые технические подробности; в частности, такая.

Для superx++ автор сделал интерпретатор, причем написал его на плюсах. А почему не попытаться применить для этой же цели XSLT? Это выглядело бы логично: ведь он как бы хочет по максимуму использовать преимущества XML-технологий? Понятно, что автор хотел сделать эффективный инструмент, так как надеялся найти практическое применение своему проекту (даже фирму специально для этого сделал), но мне кажется, исследователькая ценность проекта от такого выбора только бы возросла, при этом совершенно неважно, в какой мере это ему бы в итоге удалось. Даже отрицательный результат здесь был бы очень интересен; по крайней мере, мы бы лучше себе представляли границы применимости шаблонов XSLT. Я, например, давно хочу эти границы применимости почувствовать...
UPDATE: совершенно случайно вдруг увидел старую статью В.Турчина:
Рефал как язык для обработки xml-документов
Валентин Турчин
Опубликовано в журнале "Компьютерра" №25 от 02 июля 2001 года
Ссылка: http://www.computerra.ru/2001/402/10900/

Там, помимо вопросов обработки XML-документов средствами Рефала, обсуждается и вариант XML-нотации для этого языка...

среда, 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. Не хотел, честное слово... Обещал же себе не участвовать в религиозных войнах. Последний пост такой. Больше не будет.


среда, 26 сентября 2007 г.

Разработка российской ОС - так с нуля или где?

17 сентября в Компьюленте (http://soft.compulenta.ru/332541) появилось
такое сообщение (цитирую только часть; все выделения цветом, жирностью и курсивом - мои):

На разработку российской ОС будут выделены 70 миллионов рублей
17 сентября 2007 года, 15:42
Текст: Владимир Парамонов

Исполняющий обязанности министра информационных технологий и связи России Леонид Рейман во время заседания Совета при Президенте РФ по реализации приоритетных нацпроектов заявил, что из бюджета страны планируется выделить 70 миллионов рублей на разработку операционной системы и набора прикладных программ.

Как сообщает "РБК daily" со ссылкой на заявления Реймана, в 2008 году российские программные продукты пройдут тестирование в Томской области, Пермском крае и Республике Татарстан. К в 2009 году отечественное программное обеспечение планируется установить на все школьные компьютеры.

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

Возникает ряд естественных недоумений и вопросов:

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

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

НО: в тексте явно же сказано, что создаваться ОС будет с ноля! То есть, речь идет о совершенно новом продукте! Причем, это, похоже, не оговорка и не домысел журналиста: последняя фраза в приведенном выше тексте явно указывает на наличие альтернативы в виде опенсорсной платформы. То есть, все-таки, своя ОС и "с ноля"?..

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

- За этот же срок должны быть созданы: "пакет офисных приложений, антивирус, графический редактор, решение по автоматизации управления школой и некоторые другие продукты". Опять же, для случая Линукса все это вполне реально, если за основу берется OpenOffice, Gimp и что там есть еще готового.

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

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

Постскриптум

После этого самого первого сообщения повалили повторные, уже не хочется их цитировать. Кроме того, сообщалось, что Рейман/Медведев провели встречу с представителями российских софтверных фирм, где обсуждалось это решение. Бог с ними со всеми.

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

Так что же: есть решение о создании российской ОС с нуля, или нет? Будет ли проведен конкурс "до конца текущего месяца"? Действует ли еще срок "к 2009 году"?

четверг, 20 сентября 2007 г.

Синтаксис и семантика, простота и сложность

Этот пост - нечто вроде ответа на комментарии (см. "О сайте compilerjobs.com") насчет синтаксической сложности C++ и трудностях его освоения.

Очевидно, при разработке C++ перед Страусом стояли два неотменимых предусловия. Во-первых, обеспечить совместимость с Си и, в частности, добиться стилистической преемственности с языком-предшественником (впрочем, я вполне допускаю, что эта самая стилистическая преемственность подразумевалась по умолчанию - складывается впечатление, что эта братия из AT&T просто не могла представить, что можно придумать что-то получше, чем их птичий язык...) И во-вторых, отразить в языке как можно больше потребностей программистов и - что даже важнее - постараться предупредить все возможные ситуации, когда то, что они придумали, окажется в противоречии с обстоятельствами реального программирования. В этом смысле очень интересно сравнить первоначальные, абсолютно умозрительные наброски "шаблонных" конструкций из "Зеленой книги" (Язык Си++ с комментариями, "Мир", 1991), когда никто, включая Страуса, даже не пробовал их использовать, и окончательный вид главы 14 Стандарта... После того, как было решено включить в Стандарт STL Степанова, в шаблоны было сделано громадное количество усовершенствований и поправок - от принципиальных до самых что ни на есть мелких, которые вот именно пытались учесть и предвосхитить все возможные будущие проблемы. Еще и не все внесли, можно было кое-что добавить...

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

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

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

Я, кстати, вовсе не считаю, что кто-то из них безусловно прав, и не принадлежу ни к какому "лагерю". Для меня вполне очевидно, что C++ спроектирован в целом неудачно, он несбалансирован, избыточно сложен и стилистически безвкусен. При этом я считал и продолжаю считать, что эта самая сложность есть не какой-то умственный заскок неких злонамеренных извращенцев, а адекватное отражение объективной сложности программирования. Прошу прощения за самоцитату, но "Задачи, решаемые современными программными системами, очень и очень сложны. Для их создания приходится использовать адекватные инструменты, которые не могут не соответствовать сложности и ответственности задач и потому объективно не могут быть простыми".

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

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

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

И благодарите Бога, что мы говорим сейчас о "нормальных" языках: попытайтесь хотя бы отделить синтаксис от семантики, скажем, в языке Haskell...

Вдогонку

К вопросу об "элегантности" C++ и о стиле программирования, им навязываемом.
Вспомнил, как несколько лет назад (в 2002 году, кажется) в ETH случился "симпозиум по языкам программирования". Под этим громким названием скрывались всего три доклада, однако они и их авторы стоили иной многодневной конференции: выступали Страуструп, Дал (автор Симулы) и Вирт.

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

(Уж не говоря о том, что примеры эти, как и в его книге, были набраны пропорциональным шрифтом и вдобавок курсивом. Как хотите, но в этом одном мне видится явное извращение...)

Кто-то из озадаченно молчащего зала растерянно заметил что-то насчет крайней неудобочитаемости (в подтексте звучало: нелепости) примеров. Страус отреагировал: ну, это не проблема, программисты к такому стилю быстро привыкают.

Ну что тут скажешь...

суббота, 11 августа 2007 г.

Перевод стандарта C++: несколько аргументов

Пришел такой комментарий:

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


ps. А почему 14882:1998(E), а не 14882:2003(E)?

pps. Будет ли актуален этот перевод когда выйдет 14883:2009? Очень сомневаюсь...

Что ж, вполне разумные сомнения. Попробую высказать свои соображения.

Никто никогда не научится программировать просто прочитав Стандарт. Да что там программировать... даже просто языку никто не научится.

Разумеется. Я Вам даже больше скажу: никто никогда не научится программировать, прочитав любую книгу по программированию. И самому языку тоже нельзя научиться, прочитав какой бы то ни было текст. Чтобы чему-то научиться, надо реально программировать - другого пути нет. Но это все ведь вполне банально, и никто не собирается делать перевод стандарта, чтобы "научить" кого-то программировать. В том исходном посте ведь было ясно написано, что Стандарт - не учебник (ни по программированию, ни по языку), у него другие цели, его главная задача - представить язык в максимально строгом, недвусмысленном и, главное, полном виде. Этого не делает ни один, даже самый качественный учебник, в том числе книги, упомянутые Вами.

Именно отсюда следует основная задача задумываемой публикации: дать в распоряжение программистов (то есть, тех людей, которые уже умеют программировать!) первичный источник информации по языку. Это принципиально. Тот, кто программировать умеет, но хочет научиться делать это (еще) лучше, возьмет того же Майерса или Александреску. Но куда обращаться тому, кто хочет узнать или вспомнить точное правило вызова деструкторов для разрушения элементов не полностью построенного массива? Кроме стандарта (и именно комментированного, и снабженного адекватными примерами), этого, скорее всего, не написано нигде. Даже если в какой-то хорошей книге это правило и упоминается, то поди еще найди - и такую книгу, и место в ней... А в стандарте это все есть с гарантией. Если хотите, это своего рода энциклопедия по языку, и она может прекрасно соседствовать с учебниками по C++, ничуть их не заменяя, но давая большое количество уникальной информации.

Ну и те люди, которые не захотят останавливаться на достигнутом, могут начать читать Стандарт в оригинале.

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

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

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

Проблемы с терминологией?- они существуют и сейчас, без всякого перевода стандарта, и каждая новая книга по C++ хоть какой-нибудь перл к коллекции нелепиц, да добавит. Хотел бы думать, что моя (наша) версия перевода терминологии будет гораздо более выверенной - и с профессиональной, и со стилистической, и с исторической точек зрения. Я хотел бы даже добавить специальный раздел, посвященной именно вопросам терминологии и ее адекватного перевода, с необходимыми обоснованиями и анализом отвергнутых вариантов.

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

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

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

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

ps. А почему 14882:1998(E), а не 14882:2003(E)?
pps. Будет ли актуален этот перевод когда выйдет 14883:2009? Очень сомневаюсь...


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

Проект С11 и компилятор C++ Интерстрона

Пришел такой вопрос:

Анонимный комментирует...
В 1999 году Интерстрон вроде разрабатывал компилятор С11, самой любопытной особенностью которого была поддержка русского синтаксиса (причём достаточно хорошо проработанного).Вы не в курсе, какова судьба этого проекта?? Где его можно найти чтобы посмотреть??


Отвечаю.
С11 - довольно давний проект, который начался в конце девяностых (тогда нынешний Интерстрон еще назывался МедиаЛингва:Компиляторы). Действительно, одной из идей была поддержка русских эквивалентов служебных слов. Надо отметить, что кириллические идентификаторы по определению языка должны поддерживаться любой реализацией стандарта ISO, так что речь шла именно о служебных словах. Нам казалось, что "кириллические" программы было бы легче сопровождать - конечно, если программу не надо было переносить в другие среды. Кроме того, русские служебные слова помогли бы при начальном изучении языка - особенно для тех, кто не слишком хорошо владеет английским.

Русские эквиваленты служебных слов были и вправду достаточно тщательно продуманы; есть документ, который подробно обсуждает, почему были выбраны именно те, а не иные эквиваленты. Возможно, этот текст до сих пор лежит на сайте Интерстрона. Те, кто интересуется историей вопроса, могут найти мою статью в "Мире ПК", номер 4 за 1999 год: http://www.osp.ru/pcworld/1999/04/160292/. Там все описано (правда, довольно кратко - статью при публикации сильно обрезали).

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

Вообще-то, подобная "локализация" языка программирования - вовсе не какая-то экзотика. Первые российские компиляторы Фортрана и Алгола-60 были русскоязычными. В свое время был разработан компилятор PL/I для "Эльбруса" с двумя наборами служебных слов. В Алголе-68 была предусмотрена стандартная возможность перенастраивать язык на различные "культуры", как сейчас говорят. Есть и другие примеры. Так что мы не ощущали себя маргиналами; наоборот, нам казалось, что мы делаем как раз то новое, что на самом деле хорошо забытое старое...

Проект С11 не был доведен до коробочного продукта по причинам, не связанным с его "русскоязычностью". Тем не менее, собственно компилятор C++ полностью разработан и тщательно оттестирован. Он и сейчас вполне себе жив, развивается и распространяется на рынке софта для спецпроцессоров, в чем можно убедиться, зайдя на сайт www.interstron.ru.

вторник, 10 июля 2007 г.

Перекрыть Енисей или перекрыть функцию (2)

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

Уважаемый Евгений!

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


О перекрытых функциях. Мне не нравится этот термин. И я был бы против него, если бы мог предложить что-нибудь значительно лучшее. На самом деле в том, что "перекрытые" функции нам не нравятся, виноват русский язык. С его многозначностью. Вы думаете о Енисее. Я представляю другую картину. Возьмите несколько фигур и расположите их над плоскостью одну над другой, но не строго. Посмотрите на их тени. Эти тени (проекции) перекрываются, пересекаются друг с другом. Теперь представьте себе функции вместо фигур. Мне кажется, смысл слова "перекрытие" здесь очень точно (достаточно точно)
отражает смысл явления.


Ваше возражение против слова "инструкция" сводится к тому, что оно плохое потому, что используется при описании ассемблера (Instruction Set). На самом деле во многих переводах книг по ассемблеру Вы встретите "оператор ассемблера"! В них (некоторых из них) нет слова "инструкция". Но главное другое. Допустим слово "инструкция" используется при переводе книг по ассемблеру. Ну и что? Чем оно от этого становится хуже? Неоднозначность? Но книга про С++, а не про ассемблер. И разве нам привыкать к перегруженным терминам (возьмите, например, слово "поток")? Ваш вариант с "предложением" тоже вполне приемлем. <...>


Насколько я понял, Вы предлагаете переводить statement как "оператор", а operator как "операция". Такой вариант можно было бы рассматривать, если бы удалось всех студентов (и вообще всех программистов) полностью изолировать от оригинала и от справочных материалов на английском языке. Потому что иначе в людских головах была бы сплошная путаница: запомнить, что оператор - это не оператор, а операция, а statement - это оператор, довольно сложно. Не говоря же о том, чтобы понять, почему так. На самом деле, мне представляется все довольно просто. Оператор - это оператор. Английский язык - не беднее русского (а если точнее - богаче). В английском есть слова operator и operation. И если бы они хотели сказать "операция", они бы сказали. Они (и, в частности, Страуструп), сказали "оператор". Все. И я не прилагаю особых усилий, чтобы сказать "оператор сложения". Я считаю это выражение вполне естественным. И в математическом контексте (например "плюс (+) - оператор сложения - записывается между двумя слагаемыми..." - Вы не можете здесь сказать "операция сложения") и в программном. Операция, это что-то абстрактное, не материальное, не представимое. Это то, что подразумевается (если я пишу +, это приведет к тому, что вызовется функция, являющаяся по смыслу _операцией_ сложения). И наоборот оператор - это то, что можно увидеть и написать (знак +).

> Запись a = b; не может быть "инструкцией присваивания", даже если иметь в виду, что
> под "инструкцией" понимается statement. По Стандарту эта конструкция называется
> expression-statement

Я не помню, где в оригинале написано "expression-statement". И в каком контексте.

<...>

> почему "перегрузка операторов", но "переопределение функций"?
> В оригинальных английских текстах используется одно и то же слово overloading:


Не могли бы Вы привести пример?

> решительно все термины, ... уже имеют выбранные профессионалами,
> вполне определенные, обоснованные и исторически прижившиеся русские эквиваленты.


Это не так. Если бы они были вполне обоснованы и полностью прижившимися, не было бы этой переписки и разноголосицы в переводах. Я понимаю, что Вам неприятно встречать в книге термины, которые Вы считаете неправильными. Мне тоже очень неприятно видеть в книгах термины, с которыми я не согласен. Как правило я сначала страшно ругаюсь на переводчиков и редакторов книг. Потом я пытаюсь понять - чем их термин хуже моего. И хуже ли? Может быть они имели ввиду значение слова, которое сразу не очевидно. И т.д.
<...>

В заключение, скажу, что меня очень печалит ситуация, с переводом компьютерных книг в России. Мне кажется, можно было бы вполне согласовать хотя бы некоторые вещи. Непонятно, правда, кто этим будет заниматься. Единственное, что утешает, так это то, что подобную разноголосицу замечают только переводчики, редакторы, преподователи - то есть люди, обязанные думать над тем, что говорят. Большинство читателей эту неразбериху либо не замечает, либо воспринимает как должное. И использует либо слэнг, либо транслитерацию английских слов (не всегда правильную). Что, может быть, не так уж и страшно.

С уважением, <...>


Уважаемый <...>

>Я не согласен с Вашим главным постулатом. О том, что нужно ориентироваться на
>предшественников и, в частности, на книги, выпущенные до перестройки. Вы
>сами говорите о наличии школы в России. А это означает, что ее "ученики"
>должны быть во многом не согласны с традициями другой школы - западной.


Да почему они должны были быть "во многом не согласны"? Откуда это следует? Как раз наоборот, все развивались, в общем, в одном русле. Те российские школы, которые я упомянул, вовсе не были антагонистами. И, скажем, Андрей Петрович Ершов (слышали про такого?), автор оптимизирующего компилятора Algol-60 и член IFIP,- если и был в чем-то не согласен с зарубежными авторитетами, но, конечно же, не потому, что работал в Новосибирске, а не в Беркли.

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

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


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

Уверяю Вас, никто не тщился выдумать свою терминологию, которая непременно должна была быть отличной от западной. Именно что переводы в основном как раз и следовали "западной" традиции.


На самом деле речь идет о достаточно простых вещах. Можно эту традицию знать и следовать ей, можно знать и по каким-либо причинам не следовать, и, наконец, можно не знать и выдумывать свое. Что Вам больше подходит?

>Не было и нет единой общепризнанной традиции. Возьмите первое издание
>Страуструпа и перевод книги Эллис, Страуструп. Там используются разные термины.


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

Так что есть не "разные термины", а переводы, выполненные профессионально, и переводы, сделанные непрофессионально, случайными людьми.

>> решительно все термины, ... уже имеют выбранные профессионалами,
>> вполне определенные, обоснованные и исторически прижившиеся русские эквиваленты.
>Это не так. Если бы они были вполне обоснованы и полностью прижившимися, не
>было бы этой переписки и разноголосицы в переводах.

Странная логика... Если есть разноголосица, значит не для всех терминов есть перевод? Разноголосица и происходит как раз тогда, когда переводчики не знают уже имеющихся переводов, которые реально используются профессионалами. И если computer science у нас в стране сейчас не так активно развивается, как хотелось бы, так что ж: для всего вводить собственные термины?

Я продолжаю утверждать: все базовые термины (такие, как оператор, операция) действительно давно имеют свои вполне адекватные переводы. Вы пишете: "Они (и, в частности, Страуструп), сказали "оператор". Все". Вообще-то, Страуструп сказал не "оператор", а "operator". И уверяю Вас, не Страуструп придумал операцию сложения и не он впервые ввел в программирование возможность задавать собственные реализации операций для нетривиальных типов. Все это было очень давно (Algol-68, Ada), и об этом много писалось в нашей литературе. И, поверьте, тогда никому не приходило в голову писать "оператор сложения"; любой редактор вполне справедливо такого безобразия не допустил бы.


Позвольте привести еще один пример. Первые книги по системе UNIX появились у нас довольно скоро после того, как эта система получила известность. В переводах этих книг для термина handle (file handle) был предложен вариант "дескриптор", "дескриптор файла". Не Бог весть какое красивое название, но точно отражает суть дела: это описатель файла. Так или иначе, это название активно использовалось (и при описании других ОС и машинных архитектур).


В системе MS-DOS, которая появилась спустя десяток лет, этот же термин handle использовался для обозначения семантически того же самого понятия. Но книги по MS-DOS переводили совсем другие люди! У них не было background'а, они просто не читали тех, уже классических книг, а знали только английские help'ы. Они попросту были плохо образованы. Хорошо понимая, что такое handle, они решительно не знали, как перевести это слово. И не долго думая, написали... хендл.


Неужели это правильно????? Неужели мы с Вами должны следовать за этими "переводчиками", лишенными чувства языка?


Конечно, есть термины, которых лет десять назад не существовало, например, те же function overloading (хотя сам механизм в ЯП, конечно, был известен). Но тут уж для выбора адекватного перевода требуется чувство языка (русского/английского) и, конечно же, четкое понимание сути термина. Ваша метафора с тенями на плоскости иллюстрирует только многозначность русского языка. Еще раз: "перекрытие" чего-то чем-то подразумевает, что перекрывающий объект делает невидимым, недоступным, неактуальным перекрываемый объект. Для overloading functions, равно как и для overloading operators это принципиально не так. Все одноименные функции и все функции-операции одновременно актуальны; никакая из них не "перекрывает" никакую другую. Они все, вот именно, совместно используются.


>Насколько я понял, Вы предлагаете переводить statement как "оператор", а
>operator как "операция". Такой вариант можно было бы рассматривать...


Да не я предлагаю! Так уже есть, и так уже есть не один десяток лет. Ситуация заключается просто в том, что сейчас потоком идут западные книги, переводчики и редакторы которых не знают этого.

<...>

>В английском есть слова operator и
>operation. И если бы они хотели сказать "операция", они бы сказали.


Блестяще! Вот в чем дело: в западной computer science тоже есть определенные
терминологические традиции. Вы представляете дело так, что перед Страуструпом
стояла примерно такая же задача, что перед Вами: выбрать по своему усмотрению
подходящие термины из множества равнозначных. Вовсе не так! Слово operator используется во всех языках программирования для обозначения вполне определенной языковой конструкции, и Страуструп естественно, и не думал ломать эту традицию. Operation, кстати, используется в Стандарте Си++ (в одном-двух местах) с другим оттенком: при описании семантики операций, т.е. существа действий, выполняемых операцией.


>> Запись a = b; не может быть "инструкцией присваивания", даже если иметь
>> в виду, что под
>> "инструкцией" понимается statement. По Стандарту эта конструкция
>>называется expression-statement
>Я не помню, где в оригинале написано "expression-statement". И в каком
>контексте.


Expression-statement - это термин Стандарта Си++ (оператор-выражение); в данном случае он отражает тот факт, что a=b - не оператор в традиционном понимании (как, например, в Pascal'е), а бинарная операция, которая вырабатывает значение и, как и многие другие операции, может совместно использоваться.


>> почему "перегрузка операторов", но "переопределение функций"?
>> В оригинальных английских текстах используется одно и то же слово overloading:
>Не могли бы Вы привести пример?


Да пожалуйста: вся глава 13 Стандарта посвящена именно этому. Там для языкового механизма как такового используется термин overloading. Для обычных функций и функций-членов классов - термин function overloading; сами такие функции называются overloaded functions, а функции-операции - overloaded operators.


С уважением,
- Евгений Зуев.

Перекрыть Енисей или перекрыть функцию

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

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

Обсуждая со студентами вопросы программирования на Си++, часто приходится сталкиваться с достаточно вольным обращением с терминологией. В основном, эти вольности проистекают от прямой транслитерации англоязычных терминов из западных описаний и руководств и недостаточного знакомства с квалифицированными переводами. Со временем я научился терпимо относиться к подобной "терминологии", помня, что устная речь, в том числе и профессиональная, может несколько отличаться от письменной в сторону большей свободы. Я даже научился не морщиться от "перекрытых функций"; иной раз, правда, не выдерживаю: "Функция - не Енисей, ее не перекрывают". Но, честно говоря, я никак не ожидал подобного от переводчиков Бьярна Строуструпа!

Недавно вышло в свет долгожданное третье издание Б. Строуструпа "Язык программирования C++" (издательство "БИНОМ"). Впечатление от оригинальной версии книги создалось весьма положительное, так что появление перевода следует всячески приветствовать.

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

Слово "statement" принято переводить на русский язык как "оператор". Мы привыкли к тому, что if, while, case и т. д. - это операторы. Увы, в контексте С++ такой перевод неприемлем. Дело в том, что в С++ слово "operator" (которое и переведено как "оператор") имеет совсем другое значение. Оно применяется для обозначения сложения (оператор +), разыменования (оператор *), выяснения размера (оператор sizeof ()) и в других подобных случаях…

…Что же касается термина "statement", то из предлагаемых различными словарями вариантов "утверждение", "предложение" и "инструкция" мы избрали последний, так как он, по-видимому, лучше всего соответствует сущности обозначаемых словом "statement" конструкций С++ и, кроме того, периодически встречается в книгах и документации в нужном значении…

Си++ - типичный операторный язык; в этом смысле он ничем не отличается от своих предшественников и других алгоритмических языков программирования - Алгол-60, Паскаль, Си, Ада. Тот факт, что в Си++ присваивание считается бинарной операцией, вырабатывающей значение, не меняет существа дела - в Си ситуация точно такая же. Уже по одной этой причине нет решительно никаких оснований вводить для традиционных операторов - условного, цикла и т.д.- новое название. И уж совсем диким выглядит конкретный вариант перевода для английского statement - инструкция! Конечно, любой англо-русский словарь добросовестно перечислит весь спектр возможных значений для того или иного английского слова; наверное, в каком-нибудь из них и можно найти и такой вариант. Но это же не основание для того, чтобы выбрать для фундаментальной книги по программированию на Си++ слово, традиционно используемое - даже не для языка ассемблера, а в описании системы команд какого-нибудь процессора! Instruction set - устойчивое сочетание в книгах о процессорных архитектурах.

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

На самом деле авторы перевода тут же простодушно поясняют свой выбор. Дело в том, что так они надеялись решить проблему с другим понятием языка, выражаемым словом operator. Действительно, как же иначе перевести это слово, как не побуквенно! Вот и получается, что слово "оператор" уже занято… И statement становится - инструкцией.

Между тем, в истории языков программирования, кажется, не было ни одного случая, чтобы сложение, вычитание, сравнение и другие подобные действия назывались в русском языке как-либо иначе, нежели операция. Поэтому трактовка слова operator совершенно однозначна. Если для statement, действительно, часто используются различные варианты перевода, и вариант "оператор" более предпочтителен, в основном, как уважительное следование традиции, то, чтобы выговорить "оператор сложения", необходимы специальные услилия по деформированию русского языка. Да, в некоторых областях математики английское operator переводят как "оператор", но для программирования это совершенный нонсенс, впервые появишейся в устной речи тех самых студентов, познававших Си++ по текстам оперативной помощи.

В результате получаем следующий пассаж авторов перевода:

В частности, = - это оператор присваивания, семантику которого можно изменить путем перегрузки, а вот запись a = b; является инструкцией присваивания, семантика которой неизменна, фиксирована языком и состоит в вызове (возможно перегруженного) оператора присваивания с аргументами a и b.

Запись a = b; не может быть "инструкцией присваивания", даже если иметь в виду, что под "инструкцией" понимается statement. По Стандарту эта конструкция называется expression-statement (оператор-выражение, согласно переводу предварительной версии стандарта, см. конец заметки). Иными словами, присваивание (операция присваивания!) - это частный случай выражения, а выражение в позиции, не вырабатывающей значения, считается именно оператором-выражением. Семантика же этой конструкции вовсе не обязательно заключается в вызове какой-либо функции; возможно (для базовых типов) и непосредственное копирование значения. Кстати, как можно вызвать "оператор присваивания"? Вроде бы, вызвать можно только функцию… На самом деле авторы перевода имели в виду функцию-операцию (в Стандарте - operator function).

Далее авторы перевода пишут:

С учетом того, что большинство операторов С++ допускает перегрузку, синтаксически и семантически напоминающую (пере)определение функций, можно сказать, что операторы в С++ сродни функциям.

Сходство функций и операций - давно осознанный и освоенный наукой программирования факт. Скажем, в языке Clu, появление которого в свое время было воспринято как заметное событие, традиционные операции в инфиксной форме рассматривались как излишество, "синтаксический сахар", уступка привычкам, а первичной конструкцией считался как раз вызов функции. Так что приведенная выше цитата не имеет прямого отношения к выбранной терминологии. Отметим только некоторую словесную кашу: если "операторы в Си++ сродни функциям", то почему "перегрузка операторов", но "переопределение функций"? В оригинальных английских текстах используется одно и то же слово overloading…

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

Хочется высказать замечание общего характера. В информатике и, в частности, в программировании сложилась какая-то странная ситуация. Иногда создается ощущение, что история вычислительного дела началась с IBM-овских персоналок (до них, как всем известно, компьютеров не было), язык Си++ был первым дерзновенным рывком от ассемблера к сияющим вершинам объектно-ориентированного программирования, а первой компьютерной книгой, переведенной на русский язык, был серый том Питера Нортона…

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

Так что, читая сейчас очередной "вольный перевод" какой-нибудь новинки и натыкаясь взглядом на многочисленные "хэндлы", "директории" и "перекрытые функции", я точно знаю, что переводчик и редактор не читали ни одной книги по программированию, вышедшей ранее 1990 г., историю информатики ведут от голубых окон Norton Commander и даже не подозревают о том, что решительно все термины, для которых они упоенно придумывают собственные толкования, уже имеют выбранные профессионалами, вполне определенные, обоснованные и исторически прижившиеся русские эквиваленты.

Возвращаясь к книге Строуструпа, не могу не сказать, что ее переводчики при желании (и при наличии простого уважения к своим предшественникам) могли обратиться к действительно грамотно выполненному переводу описания предварительной версии Стандарта Си++, написанному тем же автором (Эллис М. и Строуструп Б. Справочное руководство по языку программирования C++ с комментариями: пер. с англ.- М.: Мир, 1992). Этот перевод сделан специалистами из новосибирского Академгородка, которые сами являются профессиональными и высококлассными программистами. Поэтому в нем вы не найдете ни "инструкций присваивания", ни "операторов сложения", ни "перегруженных функций"…

Кстати, в этой же книге для терминов declaration и definition используются, соответственно, переводы "объявление" и "описание". Неужели и здесь было совершенно необходимо непременно сказать свое слово, переведя definition как "определение"?