пятница, 5 сентября 2008 г.

Страуструп о будущих средах программирования

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


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

9.4.4 За пределами файлов и синтаксиса

Как я вижу среду для разработки программ на С++? Прежде всего - инкрементная компиляция. Если вносится небольшое изменение, то система "понимает", что оно небольшое, и генерирует новую версию программы мгновенно. Моментальные ответы хотелось бы получать также на простые вопросы и указания типа: "Показать объявление f", "Какие еще f есть в области действия", "Как разрешен этот вызов оператора +?", "Какие классы произведены от Shape?" и "Какие деструкторы вызываются в конце этого блока?"

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

В программе на С++ есть много информации, которая в типичной среде доступна только компилятору. Уверен, что она должна быть предоставлена программисту.

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

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

                           Прекрасно! Это в чистом виде семантический взгляд на программу!
                           Если появится система, которая в полной мере воплотит такой
                           подход к работе с С++, это будет весьма существенным прогрессом,
                           если не сказать прорывом... Продолжая тему "семантического"
                           подхода к С++, эксплуатируемому Интерстроном, могу сказать, что
                           у нас есть и реальное средство, работающее с семантическим
                           представлением: это визуализатор, который показывает исходную
                           программу в виде дерева семантических элементов - именно как 
                           "набор типов, функций, предложений..." и/или в нотации UML.

То, что в основе реализаций С++ лежат символьно-ориентированные инструменты, всегда было главным препятствием на пути развития языка. Если нужно препроцессировать и перекомпилировать каждый заголовочный файл, прямо или косвенно включенный в файл, где находится слегка измененная функция, то для этого требовалось определенное, пусть и небольшое время. Существует несколько методов, позволяющих избежать ненужных перекомпиляций, но, по-моему, наиболее перспективный и интересный подход - отказаться от традиционного исходного текста и положить в основу инструментов абстрактное внутреннее представление. Ранний вариант такого представления можно найти в работах [Murray, 1992], [Koenig, 1992].  Естественно, текст все равно необходим - его вводят и читают пользователи - но он легко преобразуется системой во внутреннюю форму

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

и реконструируется по запросу.

                           А вот реконструкция текста - это действительно несложно.

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

Нетекстовое представление могло бы быть создано языками более высокого уровня, генераторами программ, инструментами визуального программирования и т.д.

                           Ну, и обычными компиляторами переднего плана, разумеется.

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

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

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

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

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

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

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

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

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

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

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

22 комментария:

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

Забавно. Почитал статью и нынче же увидел визуальный просмотр классов в новых свойствах C++ Builder. Там как раз можно смотреть классы вверх по наследованию и вниз.

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

To alex:
Показывать граф наследования умеет, кажется, не только Builder. Visual Studio тоже что-то "семантическое" показывает. Это, конечно, хорошо, но надо понимать, что это всего лишь визуализация - самое простое из того, что можно было бы сделать, будь в нашем распоряжении промежуточное представление. Да и визуализация эта - принципиально ограничена. Показывается только та информациия, которой компилятор счел нужным поделиться со средой программирования. :-)
Помимо неполноты информации, отсутствует возможность задать системе вопрос о том или ином аспекте организации программы...

zverok-kha комментирует...

Неоднократно сталкивался с мнением, что именно синтаксис С++ мешает построению красивых семантических инструментов (а-ля Idea/Java, Reflector/C#, Smalltalk-овских IDE и т.п.)

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

А что скажете о возможностях Eclipse CDT?

Анонимный комментирует...

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

Ну действительно, Intellisense есть и в Visual Studio, и в Java-средах программированя, и в Dephi - да где только нет... Откуда берется эта информация? По моим понятиям, из некоторой формы внутреннего представления.

Внутреннее семантическое представление есть во всех компиляторах. Многие системы используют единое внутреннее представление для нескольких языков (на ум приходят Visual Works и gcc, есть и в VS наверняка). Только оно у коммерческих фирм либо закрыто вовсе, либо продается за отдельные деньги. Кстати, Интерстрон тоже небесплатно его предлагает. Понятно, что из семантического представления можно "выуживать" практически все факты о программе программным же образом, а не только те, которые предоставляются средой. То есть, непонятно, что нового - в использовании какого-то особого внутреннего представления C++.

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

По поводу линковки по семантическому представлению. Идея для C++ - правильная и хорошая. Только - его основные проблемы - отсутствие толковой метаинформации в откомпилированных модулях, да и отсутствие приличной модульности вообще. Даже в устаревающем Delphi уже давно есть откомпилированные модули dcu с кучей метаинформации в заголовке. И линковщик только заглядывает в нее и сразу разрешает внешние ссылки. А в C++ линковщик проделывает гигантскую работу - известно же про скорость линковки проектов на C++... Я уж не говорю про .NET и Java, где этап линковки ну почти отсутствует - в байт-коде уже хранится нужная информация. Кстати, интересен вопрос, насколько лучше делать линковку по семантическому представлению, чем по байт-коду - мне кажется, что преимущества в большинстве сомнительны.

Мне кажется, что сейчас нужно ставить более прогрессивные вопросы, чем ставил Страуструп 15 лет назад: создание единого семантического представления для группы языков программирования и преобразования "во все стороны". Можно также говорить о стандарте внутреннего представления только для C++ и о стандарте хранения его на диске - иначе каждая фирма будет разрабатывать свое и тщательно скрывать от конкурентов.

Бесспорно, интересен вопрос об увязывании семантического представления с модульностью и об изменении лишь "куска" семантического представления при изменении фрагмента программы (для C++ - часть компиляции будет делаться "на лету" - правда, думаю, это не очень актуально из-за долгого процесса линковки).

Ну и - что касается C++ - хорошо бы хранить в каком-то стандартном бинарном виде заголовочные файлы - а то они каждый раз перекомпилируются - пещерный век!

Yury Akopov комментирует...

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

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

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

Для abreslav:
С Эклписом я не работал, только однажды слушал выступление Эриха Гаммы, в котором он показывал какие-то красивые возможности среды. Наслышавшись о замечательной расширяемости этой системы, купил его книжку о том, как делать расширения для Эклипса, и... расстроился: насколько красиво выглядит сама система - настолько же плоха оказалась эта книга. Я, наверное, не самый глупый человек на свете, но прорваться сквозь обилие деталей и терминов, которые обрушились на меня с первых же страниц, я не смог...

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

Для yury akopov:

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

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

Yury Akopov комментирует...

Да, про компиляторы я ошибся, невнимательно прочитал статью.

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

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

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

Для abreslav:
Действительно, XML представляется мне очень удобной нотацией для представления того, что можно извлечь из исходной программы. (Конечно, я прекрасно понимаю, что этот формат - больше для последующих автоматических манипуляций, но никак не для чтения человеком.) И я, кстати, не считаю, что этот формат низкоуровневый.
А мысль использовать XSLT для задания различных манипуляций над представлением семантики - это просто желание проверить гипотезу о применимости (и о границах применимости) такого подхода. Все-таки, XSLT - достаточно мощный механизм, и на первый взгляд кажется, что с его помощью можно сделать довольно много. Например, я попросил одного студента написать на XSLT интерпретатор выражений, представленных в XML... Я понимаю, это может показаться вывертом праздного ума, но почему нет? :-) К сожалению, это была всего лишь курсовая, и у студента не хватило сил и терпения...

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

Я спросил именно потому, что XSLT/XPath предназначен для манипуляций с деревьями XML. А ведь любое разумное представление семантики - это граф, не всегда связный, и почти всегда - с циклами. Для работы с такими данными есть более высокоуровневый стандартизированный формат MOF/XMI (физическое представление, конечно, основано на XML) и специализированные языки для манипуляций c данными в этом формате - QVT (стандартизирован OMG) и ему подобные, позволяющие писать преобразования на том уровне абстракции, на котором описана модель, а не на уровне элементов XML-документа.

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

Для ABreslav:
Большое спасибо за информацию - к своему стыду, я не знал об этом специализированном формате. Обязательно посмотрю.
Конечно, сем.представление - граф, а не дерево.
С другой стороны, мне представлялось, что важнее определить двоичный, программный интерфейс к сем.представлению, а XML-представление - это вторичная форма, которая, возможно, будет удобнее для каких-то специальных манипуляций. Поэтому я эту идею особо глубоко не прорабатывал.
Еще раз спасибо. То, что упомянутые Вами представление и язык манипуляций стандартизованы OMG, повышает их привлекательность.

Nick Shaforostoff комментирует...

откройте для себя kdevelop4

Анонимный комментирует...

О XANDF все забыли. Конечно не совсем семантическое представление (или совсем не семантическое ;)), но уж внутреннее и стандартизованное (X/Open) точно. И, что показательно, XANDF особой популярностью не отличается, ровно настолько, что поддержка этого стандарта прекращена. Может быть просто не кому не нужно стандартизованное внутреннее представление? Лучше так: почему это никто не использует? Глупые люди ведь не разрабатывают компиляторы.

Валентин комментирует...

Закройте для себя KDevelop4....

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

Excuse me for English - no Russian keyboard on the computer I am writing this on.

What you are trying to do many people tried before you, with mixed success. Some of them were distracted by more glorious purpose to enhance programming at large, e.g. Charles Simonyi with Intentional Programming - very similar to smart AST converter from any type of programming language, no public implementation for more than 10 years. He has his own company Intentsoft since 2000 somthing. http://en.wikipedia.org/wiki/Intentional_programming

Some are more practical, e.g. Microsoft's ASTLOG (http://research.microsoft.com/research/pubs/view.aspx?id=43&type=Publication) which was used for analysis of large bodies of code with the purpose of their formal verification.

Conversion between representations can be considered as a case of synchronization, and handled as such in Jekyll. http://jekyllc.sourceforge.net/

Linking with optimization, which is close to what Stroustrup talking about was implemented circa 1994 by Mary Fernandez. I did not find her dissertation, but found techreport, which seems to describe this well enough. http://www.cs.princeton.edu/research/techreps/TR-490-95

People from SourceInsight (as well as many others afterwords) made an excellent reverse engineering tool which parses and semantically cross references an arbitrarily large piece of C++ code. It's commercial, they have a fully functioning demo.

I do not mention LISP here which in a sense have no syntax, its external and internal forms match perfectly (AST tree), that is why LISP actually is so powerful and so inconvenient for a programmer with traditional training.

But there was no hero to make a reasonable implementation of all of this for a mainstream language like C++. Especially in graphical manner. So good luck, some ideas really lie unimplemented for a long long time.

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

Всё, о чём мечтал бедный Страуструп, давно уже свершилось, но не в его языке. :) Надо иметь мужество признаться, что его Цэпипи - набор костылей вокруг угловатой, реактивной мотоколяски. Современный C# имеет на порядок более простую идеологию (сохраняя мощь ООП), идеальную структуру для всяких runtime манипуляций и уж куда более простой анализатор кода! Осталось дело за мелкософтом - реализовать всё то, о чём мечтал старый академик. :) (да у них и так 70% сделано!)
Можно спорить до посинения с реальностью, но все эти "идеологические титаны" типа Смоллтока, Лиспа, Пролога, Хацкеля не выдерживают испытание практикой - они НЕ НУЖНЫ для большинства задач и ТРУДНЫ для понимания средним прогером. А рынок - его не интересует, насколько элегантно ты реализовал сортировку - ему нужен продукт, причём быстро, красочно и надёжно. Отсюда понятна популярность связки VS+C# - для 90% применений, лучшего не найти. Делайте выводы...

Анонимный комментирует...

возможно, стоит посмотреть в сторону Reflection/RTTI & C++ interpreter
http://root.cern.ch/twiki/bin/view/ROOT/CINT
который позволяет иметь многое из того, о чем пишет автор.
Например:
http://root.cern.ch/root/html/HIST_HIST_Index.html

Анонимный комментирует...

Я пока не знаю точно, каково может быть практическое применение этого семантического синтаксиса.


Очевиден следующий шаг -- resyntaxed C++. При этом еще стоит ввести гораздо более мощный язык метапрограммирования.

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

Ресинтаксис уже не нужен, опять опоздали :) Есть такое чудо - Nemerle. Там есть всё, что душа пожелает - хоть begin-end, хоть {}, хоть русские ключевые слова. Одна беда - у него кривовато продуман синтаксис(!!!!) и никакая поддержка - всё живёт на плечах студентов. :[

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

Ресинтаксис был бы полезен на самом деле. Ибо область применения C++ сильно отличается от области применения .Net (да и .Net в реальности есть только под Windows)