воскресенье, 7 сентября 2008 г.

Семантическое представление: ответ Анониму

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

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

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

Ну действительно, Intellisense есть и в Visual Studio, и в Java-средах программированя, и в Dephi - да где только нет... 

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

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

Откуда берется эта информация? По моим понятиям, из некоторой формы внутреннего представления.

А тут и гадать не надо: код, генерируемый для .NET, по определению несет в себе метаданные - некоторую информацию об исходной программе, которой и пользуется Intellisense. Более того, доступ к этой информаици можно производить кому угодно (программным путем) - из самой программы, из другой программы...

Внутреннее семантическое представление есть во всех компиляторах. 

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

Многие системы используют единое внутреннее представление для нескольких языков (на ум приходят Visual Works и gcc, есть и в VS наверняка).

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

У VS нет единого внутреннего представления. Все языки, представленные в VS, создавались в различных условиях, с разными задачами, в разное время и различными командами. Вообще, VS скрывает в себе немало неожиданностей. Например, там, как говорят инсайдеры, на самом деле два компилятора C# - один обычный, подобный тому, который из командной строки (он запускается по команде Build), а второй - "умный", который находит синтаксические ошибки на лету (в процессе наборра программы в редакторе) и обеспечивает Intellisense. Компилятор VB - один на все виды работ. Что касается плюсового компилятора, то с ним вообще непонятно. Он совсем старый, подкрученный на "живую нитку". Говорят, они собираются его капитально переделывать (якобы, уже начали...)

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

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

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

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

То есть, непонятно, что нового - в использовании какого-то особого внутреннего представления C++.

Новое заключается в том, чтобы придумать целостное, полное, независимое от конкретного компилятора представление семантики языка Си++, а также программный интерфейс к такому представлению, и, конечно, реализовать этот интерфейс. Этого до сих пор никто не сделал, хотя пара проектов с подобными целями известна (но они либо умерли лет пять назад, либо несколько "сменили ориентацию" в пользу более простых задач). Правда, можно двигаться несколько другим путем: начать с того, чтобы специфицировать интерфейс доступа к семантическому представлению, а потом заниматься реализацией этого интерфейса. Именно так сделано в Ада-мире: был принят стандарт на интерфейс доступа к Ада-программам - ASIS (Ada Semantic Interface Specification), и он был реализован для нескольких Ада-компиляторов.

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

Уверяю Вас, реальных и насущных задач подобного рода очень много - даже в России, не говоря об остальном мире. Есть насущная потребность в разного рода анализе исходных кодов громадных программ. Да и актуальность даже тех задач, которые вы перечислили (добавьте еще оптимизацию), будучи помноженной на гигантские размеры иных программ, дает весьма высокую потребность в программах анализа. Тривиальный проимер. Несколько лет назад Боинг был, якобы, крайне заинтересован (и готов был платить большие деньги) в том, чтобы кто-то проанализировал сотни миллионов строк их авиационного софта на предмет (всего лишь!) "мертвого" кода.

По поводу линковки по семантическому представлению. Идея для C++ - правильная и хорошая. Только - его основные проблемы - отсутствие толковой метаинформации в откомпилированных модулях, да и отсутствие приличной модульности вообще.

Ну и я о том же самом писал: нет семантической информации. Где спор-то? :-)

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

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

 Я уж не говорю про .NET и Java, где этап линковки ну почти отсутствует - в байт-коде уже хранится нужная информация.

В .NET линковки нет по другой причине - сама архитектура спроектирована таким образом, что исключает статическую линковку. Динамическое связывание - да, возможно, и, кстати, на основе тех же метаданных.

 Кстати, интересен вопрос, насколько лучше делать линковку по семантическому представлению, чем по байт-коду - мне кажется, что преимущества в большинстве сомнительны. 

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

Мне кажется, что сейчас нужно ставить более прогрессивные вопросы, чем ставил Страуструп 15 лет назад: создание единого семантического представления для группы языков программирования

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

 и преобразования "во все стороны".

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

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

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

А насчет "скрывать" - мне, честно говоря, не очень интересно, как та или иная фирма будет поступать со своими разработками. Все равно эффект от семантического представления будет виден в продуктах фирмы...

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

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

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

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

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


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Комментированный стандарт: current state

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


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

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