Бурное развитие информационных технологий повлекло за собой создание множества искусственных языков, ориентированных на решение проблемы общения человека с компьютером. Любой обзор по языкам программирования первым делом неизбежно затрагивает вопросы классификации этих языков. Не стремясь к соблюдению слишком строгой и исчерпывающей классификации и опираясь на ряд традиционных подходов, попробуем проанализировать современное состояние языков программирования.
Один из подходов, помогающий провести обзор языков программирования, - подход исторический. Действительно, на первый взгляд все выглядит очень просто. Сначала было программирование в машинных кодах, когда программист являлся единственным посредником между остальными смертными и Машиной - гигантским "ламповым монстром", занимающим если не целое здание, то почти целый этаж. Затем появились мнемонические представления машинного кода, ассемблер и, наконец, макроассемблер. В конце 50-х возникли языки формульного программирования, из которых наиболее замечательным был Фортран, затем (в 60-х) центр тяжести стал понемногу смещаться к нечисленным методам - появился АЛГОЛ. Еще немного, и к 70-м годам произошла структурная революция - АЛГОЛ-W и, наконец, Паскаль. Далее настала очередь "модульного" программирования - Модула и Модула-2. Приблизительно в это же время рождается знаменитый язык Си, идет новая революция логического программирования - в моде ПРОЛОГ и экспертные системы. Пентагон проводит свой знаменитый конкурс, на котором побеждает Ада, а Япония заявляет о проекте машин пятого поколения, основанных на SmallTalk. В результате происходит объектно-ориентированная революция, появляются С++, Оберон, Eiffel и Модула-3.
Общие тенденции развития языков программирования при историческом подходе проследить вполне возможно, однако изложение получается сумбурным и путаным. Удивляться тут нечему - ладно если бы произошла, например, структурная революция: программистская общественность присягнула на верность программированию без "goto" и прощай Фортран! Ан нет, и Фортран сейчас "в ходу", а если вспомнить об его преуспевших детях Basic и Visual Basic, то придется признать, что "некрологи" Фортрана более чем двадцатилетней давности выглядят в наше время как забавный исторический курьез. Хотя верно и то, что тех версий языка Фортран, что были четверть века назад, уже не осталось, да и машин, на которых с ними работали, сейчас найдешь разве что в музее. Язык Паскаль также, по сравнению со многими другими языками, сейчас уже не молод, но это не мешает ему оставаться одним из наиболее популярных языков и в наши дни. А Кобол? У него еще более преклонный возраст, а если посмотреть по конференциям на Internet - окажется, что очень много работ и сейчас проводится на Коболе.
Другой возможный классификационный критерий языков программирования - это революционные идеи программирования, воплотившиеся в соответствующих решениях: структурное программирование, модульное программирование, объектно-ориентированное программирование (ООП). Однако и тут четкой классификации не получится. К примеру, Паскаль возник как "продукт" структурной революции, удачно впитал в себя идеи революции "модульной", и сегодня существует практически на всех компьютерных платформах в объектно-ориентированных воплощениях. Другой пример: приверженцы языка С++, как правило, самым важным его достоинством называют ООП. Однако было бы неверно считать, что С++ стал популярным только благодаря объектам - как и ОО Паскаль, С++ является языком гибридным. Применение объектно-ориентированной парадигмы при работе на нем совсем не обязательно, и многие программисты в практической работе этими возможностями как в С++, так и в ОО Паскале не пользуются. Точно так же, работая с современными компиляторами языка Паскаль, например с широко известными Borland Pascal/Turbo Pascal (Borland) 7.0 для IBM PC или Think Pascal (Symantec) для компьютеров Macintosh, можно в явном виде не пользоваться модульными возможностями, оформляя исходный код программы почти в полном соответствии со стандартным Паскалем. Ошибочный подход, скажет иной поклонник прогресса. Однако не спешите с выводами: исходный код, соответствующий стандарту, будет обладать высокой переносимостью на разные платформы. Могут быть и иные резоны как для стандартных, так и для гибридных подходов. Видимо, поэтому разработчики ОО языка Модула-3 сделали принцип "гибридности" одним из основных в своем языке. С другой стороны, существует большая группа чистых ОО языков, где объектно-ориентированная парадигма является обязательной. Примером такого языка может служить Dee.
Казалось бы, еще один удобный классификационный признак - популярность языка: чем язык популярней - тем он лучше, так может, недолго думая, и разбить все языки на "плохие" и "хорошие"? Однако хорошо известно, что коммерческий успех того или иного продукта не является объективной оценкой качества. Мода - преходяща, особенно если ее приход стимулируется громкой рекламой и значительными капиталовложениями. И чем популярней язык, тем больше споров вокруг него, чем больше у него сторонников - тем больше и противников. Так, в самое ближайшее время много споров можно будет услышать об языке Java. Не вызовет ли приверженность этого языка принципам ООП оттока его сторонников - и тех, кто не стремится использовать ООП на практике, и тех, кто считает, что в ряде современных языков (например в С++ или в Eiffel) эти принципы реализованы полнее?
И, наконец, еще один критерий - уровень языка. Традиционно к языкам низкого уровня относят ассемблеры, а к языкам высокого уровня все остальные универсальные языки программирования, впрочем, для таких языков, как Форт (FORTH), Си и т. д., иногда выделяется некий "промежуточный" уровень. Помимо этого, делались неоднократные попытки выделить какой-либо язык или группу языков на "сверхвысокий" уровень, например для макросредств электронных таблиц. Не нужно вдаваться в детали, чтобы почувствовать всю условность и этой классификации. Тем более, если учесть, что большинство реализаций современных языков программирования высокого уровня имеют богатые низкоуровневые возможности. Скажем, Inline-директива, позволяющая записывать в исходном тексте машинные коды, или встроенный ассемблер, как, например, это сделано в Borland Pascal. Отметим, что, как правило, встроенный ассемблер гораздо удобнее - запись в кодах на его фоне выглядит анахронизмом. При необходимости программирования какого-либо фрагмента программы на низком уровне можно применять и обычный "не встроенный" ассемблер. В общем случае в современных языковых средах вполне возможно программировать разные модули одной и той же программы на разных языках, например и на нескольких языках высокого уровня. Отметим, однако, что на практике этого лучше не делать без особых причин.
Весьма проблематична классификация языков и по названию. Например, Н. Вирт заявляет, что недостатки концепций языка Паскаль преодолеваются в языках Модула-2 и Оберон: "Назови я эти языки Паскаль-2 и Паскаль-3... их эволюционная линия была бы очевидна". Напрашивается вопрос - что есть язык, а что есть диалект языка: например, стандартный Паскаль, расширенный Паскаль (Extended Pascal) и Turbo Pascal - три разных языка или три диалекта Паскаля?
Итак, подведем промежуточный итог: несмотря на неполноту традиционных классификаций, они позволяют выявить ряд важнейших характеристик языков программирования. То же самое можно сказать о проблеме сравнения языков. Периодически такие сравнения появляются как в солидных компьютерных журналах, так и в довольно "разношерстных" сетевых конференциях. Естественно, главная цель здесь недостижима, и большинство участников дискуссии обычно остается при своем мнении: одним язык Y нравится - другие его терпеть не могут. Однако, при малоинтересных общих выводах, сравнительные оценки деталей двух языков содержат иногда много оригинальных, интересных и плодотворных подходов.
Теоретически описания языка, например, в форме структурных диаграмм вполне достаточно, чтобы оценить его. Наверное, особо спорить с этим никто не будет, но на практике это утверждение никто всерьез и не воспримет. Пока новый язык не воплотится в эффективных и достаточно надежных инструментальных средствах, создатели могут только мечтать о признании. Увы, некоторые языки, сразу по появлении получившие высокие оценки теоретиков, подверглись суровой критике в отношении реализации. Так, например, было с Прологом и с Адой, затянулась реализация и для Модулы-3. Правда, пока никто не доказал, что ни один из этих языков невозможно реализовать эффективно. Если вопросы концепции языка, вопросы его описания в основном решаются интеллектуальными усилиями, то вопрос реализации упирается в проблему денег. А поскольку изобретателей гораздо больше, чем инвесторов, то даже самым перспективным языкам не всегда удается обрести индустриальную поддержку, необходимую им для развития. С точки зрения чистой математики, при полном и непротиворечивом описании все детали реализации - чисто технические, не влекущие фундаментальных проблем, однако на практике хорошая поддержка одного языка и отсутствие сколь-нибудь значительных финансовых вкладов в другие приводят к тому, что из группы равноценных кандидатов вырывается один - лидер, который почти тут же расходится в тысячах промышленных копий по всему миру, остальные же, в лучшем случае, распространяются по Internet, поддерживаются и развиваются скромными силами немногочисленных энтузиастов.
При выборе языка решающим фактором для многих пользователей становится наличие гарантированной поддержки. Сейчас в Internet и в коллекциях CD-ROM можно найти много экзотических и перспективных языков. Однако трудно быть уверенным, что их компиляторы не содержат грубых ошибок, что в процессе их эксплуатации не выявится серьезных ограничений и что их авторы захотят и сумеют в обозримо короткий срок создать исправленную версию. В силу этих причин подавляющее большинство разработчиков программного обеспечения использует для практической работы только индустриальные инструменты хорошо зарекомендовавших себя фирм. Такой подход безусловно оправдан, но в то же время он является серьезным тормозом развития новых языков.
Другим важным доводом в пользу того или иного языка программирования является объем литературы по этой теме. С этим тесно связаны вопросы обучения и освоения языка. Так, например, Паскаль создавался для обучения программированию, и, поскольку он оправдал ожидания, возникла идея прежде всего сократить длительность цикла обработки студенческих задач. В результате открылись богатые возможности создания эффективных компиляторов. Язык Си в этом смысле противоположен Паскалю - он в первую очередь был задуман как наиболее эффективно реализуемый язык, и популярность Си принесла реализованная на нем операционная система Unix. Хотя университетская наука во многом способствовала и популярности Unix, все же роль Си как учебного языка была не первой. И лишь в последнее время многие учебные заведения по всему миру стали ориентироваться на Си, вернее, на С++. С другой стороны, очевидно, что при прочих равных возможностях любой молодой специалист предпочтет язык, известный ему со студенческих лет.
Однако все же не стоит преувеличивать существующие различия между языками программирования. Во многих случаях они чисто внешние, а приспособляемость и, отсюда, взаимовлияние языков настолько большие, что можно говорить о конвергенции языков. Практически любой, даже начинающий, программист знает сейчас несколько языков, и освоить еще один в сжатые сроки особого труда ни для кого не составляет. Поэтому, с известными упрощениями, можно утверждать, что студентов важнее обучить не какому-нибудь конкретному языку для конкретного компьютера, а основным идеям современного программирования на основе любого современного языка на любой современной компьютерной платформе.
Как уже было сказано, исторически многие из этих идей стимулировали создание того или иного языка, и поэтому на их основе традиционно проводится классификация языков. Подойдем теперь с другой стороны, и назовем некоторые из важнейших идей современного программирования, отраженные практически во всех современных языках.
Серьезной проблемой, корни которой также уходят в структурный подход, является хорошо известная проблема строгой типизации данных. Казалось бы, иерархическому принципу построения системы родственных объектов в ООП как нельзя лучше соответствуют принципы строгой типизации языка Паскаль в его стандартном, классическом варианте. Но на примере того же Паскаль можно видеть, что строгая типизация - "палка о двух концах", и в случае передачи массивов как параметров при вызове процедуры она может сделать язык неудобным для практического использования. Преодоление этой проблемы в стандарте Паскаль привело к появлению уровня 1 языка, а потом и к новому стандарту - Extended Pascal. С переходом к OO-парадигме строгая типизация возможна, но и здесь, чтобы она не стала слишком строгой, нужно нетривиальным образом разрешить ряд вопросов. Не имея возможности за недостатком места даже кратко перечислить их, сошлемся на объектно-ориентированный язык Eiffel, как на пример в высшей степени нетривиального подхода к вопросам типизации данных.
Другая область, смежная с проблемой типизации, - идея защитного программирования. Впервые наиболее полно защитное программирование было представлено в Паскале. Суть его заключается в тестировании программных переменных на корректность принимаемых ими значений, аналогично тому, как проходит тестирование на соответствие типов. Но если последнее производится на стадии компиляции, то тестирование на соответствие значений (например, заданным диапазонам) производится уже на стадии исполнения программы. Здесь также возможны различные решения, а примером нетривиального подхода может служить все тот же Eiffel.
Особая группа тестов на стадии выполнения программы - это тесты на неинициализированные переменные. Интересно отметить, что в старых компиляторах (например Pascal 8000 для IBM 360/370) такое тестирование было одним из мощнейших средств отладки программы. Но, к сожалению, во многих новых языковых средах оно не реализовано.
К заслугам Паскаля относится также воплощение абстрактных типов данных, в частности, "записей" (records). Формально развитием "записей" явилось ООП с его "тремя китами": инкапсуляцией, наследованием и полиморфизмом. Естественно, это чисто формальный взгляд - реально ООП является принципиально иным подходом к декомпозиции и представлениям кода и данных. И тут снова, несмотря на мощь и прогрессивность ООП, нельзя не сказать об его слабостях - вернее, "не его", а человеческих слабостях, нередко сводящих на нет выигрыш от применения этого подхода. К сожалению, людям свойственно ошибаться, но если единичная ошибка в каком-нибудь операторе программы, написанной в соответствии с процедурно-ориентированной парадигмой, не всегда влечет за собой необходимость переписывания значительной части программы заново, то для исправления плохо построенной иерархии объектов обычно требуются значительные трудозатраты. Нередко подобные проблемы возникают не по вине разработчика, а по вине заказчика. Можно много говорить о важности детальной проработки технического задания, однако аппетит, как известно, приходит во время еды, и в некоторых случаях заказчик желает, чтобы в готовое программное изделие были внесены существенные изменения и дополнения.
Инициатива изменения технического задания может исходить и от исполнителя. Помимо заказного программного обеспечения большая доля программистских работ требует экспериментирования, чаще всего такая необходимость возникает при применении всевозможных эвристических алгоритмов. Что касается коммерческого ПО, то и там жизненный цикл программы достаточно краток: обновление через каждые три-четыре месяца - обычная практика многих фирм. Конечно, далеко не каждое такое обновление требует коренных переделок программы - очень часто дело ограничивается "косметическим ремонтом", однако на стадии разработки версии 1.0 какого-нибудь продукта практически невозможно предсказать, что понадобится для версии 8.0. Поэтому не удивительно, что сегодня огромную роль в поддержке программного обеспечения играют всевозможные автоматические и полуавтоматические средства проектирования.
С человеческим фактором неразрывно связаны и упомянутые задачи стандартизации. Людям свойственно сопротивляться искусственным барьерам, и каждый программист бывает склонен к недовольству ограничениями, сковывающими его свободу. Стандартизация в области программного обеспечения всегда представляла и представляет собой комплекс трудноразрешимых проблем. Недаром многие специалисты в этой области отмечают, что источник трудностей стандартизации кроется в самой природе объекта "программное обеспечение", слишком "абстрактной и гибкой" для уверенной стандартизации. Если рассмотреть эту проблему применительно к языкам программирования, то обнаруживаются дополнительные трудности. Так, например иногда указывается на принципиальную неформализуемость современных языков программирования, в результате противоречия, неясность и неполнота определений языков программирования характерна и для их стандартов (вплоть до лучших международных). Более того, традиционный идеал точного определения языков программирования заводит стандартизацию в тупик... Выход —введение стандартов "с дефектами"...
Разные языки программирования по-разному поддаются стандартизации. Отчасти это обусловлено их аппаратно-зависимыми особенностями. Например, Паскаль создавался как язык абстрактной Паскаль-машины, не привязанной к какой-либо конкретной компьютерной платформе, что повлекло за собой лавину обвинений в "отсутствии средств ввода-вывода", но зато обеспечило сравнительно легкую стандартизуемость Паскаля. Другим не менее важным для стандартизуемости свойством явилась простота этого языка - малый словарь ключевых слов, малый объем авторского описания языка и т. д.
Появление и развитие сред и систем, основанных на графическом интерфейсе пользователя (GUI), поставило проблему ввода-вывода на новый уровень. Понадобились всевозможные расширения языков, возросла их аппаратурная и системная зависимость. И хотя многое решается на уровне применения библиотечных модулей, для большинства популярных сегодня платформ может быть отмечен ряд специфических конструкций. Так, при программировании на Macintosh не обойтись без двойного, или косвенного, указателя - ссылки на ссылку (handle). Например, на Паскале:
type aStringPtr = ^string; aStringHandle = ^aStringPtr; var h : aStringHandle; begin h := aStringHandle (newHandle (sizeof(string)); h^^ := 'any string';
Другой характерной для GUI субстанцией являются всевозможные ресурсы. В случае Macintosh различные типы ресурсов можно по своему назначению разделить на две большие группы: "кодовые" и "все остальные". К первой группе относятся: исполняемый код прикладных программ; элементы управления, например кнопки диалогов и т. д. Другая группа определяет такие элементы GUI, как окна, диалоги, пиктограммы, а также данные - скажем строки. Для описания ресурсов применяется специальный язык Rez. Текстовое описание нужных программе ресурсов транслируется с языка Rez с помощью соответствующих инструментов. Однако возможно и непосредственное редактирование ресурсов в графическом виде с помощью редакторов ресурсов, наиболее известен из них ResEdit (Apple Computer, Inc.). В редакторе ресурсов программист работает с графическими ресурсами в режиме WYSIWYG, а исходный текст любой программы в значительной мере определяется ее ресурсами. Из ресурсов принципиально возможно генерировать исходный текст на языке высокого уровня. Такой генератор был создан одним из авторов этого обзора - программа MT2Trivial, доступная через ftp-/web-архивы и опубликованная в ряде коллекций CD-ROM. Новая версия этой программы позволяет генерировать исходный текст на Паскале или на Си, по выбору пользователя. В качестве другого примера генерации исходного текста можно привести генераторы HTML-документов, например формирование текста HTML-документов, используемого в Web-дизайне.
По-видимому, генераторам исходных текстов программ принадлежит большое будущее. Их популярность будет расти по мере роста требований к программному обеспечению, его сложности и функциональности. Конечно, далеко не всегда можно полностью автоматизировать процесс генерации, и некоторые типы работ должны производиться в полуавтоматическом режиме. Здесь возможны два подхода: диалоговый и пакетный, с применением разнообразных препроцессорных методик. Развитие языковых оболочек и интегрированных средств, казалось бы, сильно потеснило высокоуровневые макросредства, средства условной компиляции и т. д., однако сейчас в ряде случаев можно наблюдать тенденцию возврата к этим средствам.
Особым видом генераторов исходного кода являются генераторы компиляторов, в том числе типа YACC (например PC YACC для IBM PC, Abraxas Software, Inc., США). Эти программы, получая на входе описания языка на некотором формальном метаязыке типа нотации Бэкуса-Наура, генерируют сканер и синтаксический анализатор для этого языка. Таким образом, остается дописать генератор объектного кода - и компилятор у вас в кармане. Идея - очень привлекательная, правда, на практике сгенерированные модули, хотя (как правило) и способны корректно читать исходные тексты и осуществлять их синтаксический разбор, но делают это не эффективно. А оформлены сгенерированные с помощью PC YACC исходные тексты сканера и синтаксического анализатора далеко не самым удобочитаемым (и, следовательно, не самым "удобомодифицируемым") для человека образом. И тут мы опять имеем дело с человеческим фактором - программа может быть плохой не потому, что она плохо исполняется компьютером, а потому, что ее исходный текст трудно читать человеку.
К сожалению, несмотря на приспособляемость и заимствование прогрессивных подходов, многие языки унаследовали от своих прародителей синтаксис, провоцирующий программиста на опечатки. Так С++, казалось бы, максимально отвечающий практическим нуждам, содержит немало подобных провокационных моментов.
В подавляющем большинстве современных языков проводится четкое разграничение между оператором присваивания и операцией проверки на равенство, различия чисто внешние - какими символами их обозначать. Не компьютер, но, увы, каждый человек может вместо
if (xy == ZZ_CONSTANT) ...
набрать
if (xy = ZZ_CONSTANT) ...
и долго после этого искать, что же неверно в его программе! Запись типа a = b = c = d = e = f тоже требует некоторой привычки, как и выражения вида:
offRowBytes = (((theDepth * (OffRight - OffLeft)) + 15) >> 4) << 1; if ((**TEH). teLength > 0 && (*((**TEH). hText)) [(**TEH). teLength-1] == ' ') n++;
(отметим, что два последних примера придуманы не нами, а взяты из документации фирмы Symantec, где иллюстрируются решения практической задачи).
Другим источником частых ошибок являются средства инициализации переменных. Казалось бы, очень удобная вещь, но сколько проблем может породить ошибка в следующей записи и сколько досаднейших опечаток в ней возможно:
int a[ ] = {2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2, 2, 2};
Конечно, только начинающий программист не сможет разобраться в этих конструкциях, однако, в соответствии с образной формулировкой В. Кауфмана, в практическом программировании действует "принцип чайника", проявлением которого является "пристрастие программистов к самым тривиальным оборотам" [9]. В своем развитии языки программирования постоянно стремятся к максимально лаконичной грамматике, вопрос лишь в том, до какой степени можно сокращать язык, чтобы он остался понятен человеку. Обратно противоположная проблема - до какой степени можно расширять язык. В этом плане уже Turbo Pascal версии 5.5 вызывал опасения, которые, к сожалению, частично оправдались. Так, например, преподаватели ведущих университетов все чаще и чаще сетуют на то, что обучать студентов на Turbo-языках становится все труднее и труднее. Поэтому внешне не очень развитые языковые среды, такие, например, как Dr. Pascal, охватывающая многие из распространенных сейчас платформ и наиболее полно поддерживающая стандартный Паскаль, продолжают пользоваться популярностью в университетских кругах.
Упомянутые редакторы ресурсов являются хорошим примером совокупности подходов, называемых "бескодовым программированием" (codeless) и "визуальным программированием" (visual). Кроме специализированных применений (например редактирование ресурсов или Web-дизайн) сегодня предложено много универсальных языков бескодового программирования. Одним из них является объектно-ориентированный язык Prograph (Pictorius Inc., Канада), где отдельные программные элементы и организуемые из них структуры представляются с помощью графических примитивов: прямоугольников, трапеций и т. д. Количество текстовых сообщений сведено к минимуму. При всей привлекательности подобного подхода, применимость его для достаточно больших программных проектов вызывает определенные сомнения. (Хотя бы уже потому, что язык Prograph реализован в виде интерпретатора, а не компилятора.) Возможно, что и тут некий разумный компромисс - гибрид текстового и графического представления - окажется наиболее продуктивным. Здесь стоит напомнить об успехе интегрированных сред детского языка программирования Logo, известных также под названием "черепашья графика".
Интересным примером может служить и реализация командного языка AppleScript операционной системы MacOS. Форма исходного кода языка - текстовая, однако в состав системного обеспечения входит редактор Script Editor, позволяющий записать действия пользователя в виде script-файла (командного файла). Например, пользователь, нажав кнопку "Запись", выходит из редактора, переведя его в фоновый режим, открывает папку на рабочем столе и активирует телекоммуникационную программу, выбрав ее документ и дважды нажав на него (с помощью мыши), затем пользователь может остановить запись своих действий: для этого ему нужно вернуться в Script Editor и нажать кнопку "Стоп". Записанная последовательность действий может быть откомпилирована и указана как исполняемый командный файл - аналог прикладной программы. Теперь каждый раз, когда пользователь активирует его исполнение, ОС будет производить записанные действия: открывать папку, выбирать документ, открывать его в телекоммуникационной программе. Такой же script-файл можно было бы получить вводом соответствующего текста и его последующей компиляцией средствами Script Editor, однако первый подход дает возможность программировать MacOS пользователям, не знающим языка AppleScript. Следует отметить, что AppleScript базируется на открытой архитектуре (OSA); следуя ее спецификациям, разработчик прикладной программы может обеспечить сколь угодно полную управляемость своей программы командными файлами ОС. Кроме возможностей гибридных подходов в бескодовом программировании приведенный пример представляет также важную группу специализированных языков программирования - командных языков операционных систем.
Реализация AppleScript использует поддерживаемый MacOS механизм взаимодействия задач - обмен сообщениями, так называемыми "событиями высокого уровня" (hi-level events). И здесь мы вплотную подходим к языкам и средствам параллельного программирования.
Классические языки, имеющие средства параллельного программирования, - Параллельный Паскаль, Ада и Оккам. При всей их универсальности им присущ ряд принципиальных ограничений, обусловленных тем, что создавались они для реализации на сложных и дорогих вычислительных комплексах. Сегодня же основной интерес к средствам управления параллельными процессами исходит из сферы значительно более массовой - персональных компьютеров. Этот интерес стимулируется, с одной стороны, легкостью объединения обычных ПК в локальную сеть (в том числе и гетерогенную) и ориентацией прикладных разработок на применение параллельных процессоров (например новая компьютерная платформа Be-box, заимствующая ряд технических решений Macintosh); с другой стороны - острой необходимостью эффективной обработки больших объемов информации на ПК: графической, аудио- и видео-информации, решения задач мультимедиа. Причем зачастую к обработке информации предъявляются жесткие требования реального масштаба времени.
Другое направление, всегда привлекавшее множество энтузиастов, представляют собой идеи программирования компьютера на естественном языке или хотя бы на языке, максимально приближенном к естественному. В связи с достижениями в создании систем распознавания и синтеза речи и прежде всего с появлением этих систем на ПК, можно ожидать новых серьезных попыток и в этом направлении. Правда, для их успеха сначала придется решить ряд непростых проблем, связанных с принципиальными отличиями языков программирования от языков естественных. Различий тут больше, чем сходств.
Прежде всего, естественные языки образовывались постепенно и без участия сознания - иными словами, без планирования. Языки искусственные создавались совершенно осознанно и на базе жесткой структуры, исключающей случайности. На естественных языках можно описать все многообразие мира и человеческих отношений - искусственные же обслуживают достаточно жестко ограниченные области человеческой деятельности. Из принципиальной разницы в "происхождении" и целях вытекают все прочие отличия.
С точки зрения теории информации, естественные языки обладают большей избыточностью. Например, в естественном языке мы можем опустить слово, или несколько слов, или даже фразу без ущерба для понимания. Реализовать подобные возможности в языках программирования очень не просто. Естественные языки метафоричны - языки программирования максимально лаконичны и основаны на предельно упрощенных грамматиках. Любой язык программирования содержит гораздо меньше слов, чем даже языки первобытных народов, и (в этом смысле) он больше всего смахивает на язык людоедки Эллочки.
Интересно отметить, что искусственный язык нужен не только для общения "человек-компьютер", но и для общения "человек-человек". Например, грузчик не будет кричать: "Останови автокран!" - его просто не услышат за шумом стройплощадки, а поднимет скрещенные руки. Подобный невербальный язык формировался на основе естественных человеческих движений, вначале неосознанных, а затем постепенно прошедших отбор и стилизацию и ставших интернациональными. Возвращаясь к вербальным средствам общения, нельзя не упомянуть созданный Людвигом Заменхофом "Эсперанто", вобравший в себя черты как естественных (романских) языков, так и искусственных - жесткую структуру словообразования, исключающую случайности: все существительные оканчиваются на "о", все инфинитивы - на "i" и т. д.
С проблемой принципиальных различий естественных и искусственных языков тесно переплетена и проблема локализации, т. е. перевода инструкций, документаций, меню, комментариев в исходных текстах с одного естественного языка на другой - например с английского на русский. По многочисленным попыткам русификации можно видеть, как непросто, оказывается, перевести простейшую команду меню на русский. В целом же нужно помнить, что, хотя программирование компьютеров ориентировано на искусственные языки, естественный язык также обязательно присутствует. Львиная доля работ, связанных с разработкой любого серьезного проекта, - это работы по документированию. Хотя в профессиональном программировании прочно утвердилась практика дифференциации специальностей: программиста-разработчика, ответственного за кодирование, дизайнера, ответственного за создание ресурсов (в частности текстов сообщений), архивариуса, ответственного за ведение и поддержку архивов рабочих версий, и технического писателя, ответственного за инструкции для пользователя, - обязанности комментирования исходного текста программы, выбор осмысленных имен-идентификаторов и т. д. все равно остаются на программисте. Таким образом, к профессиональным качествам хорошего программиста относится умение не только найти и безошибочно реализовать решение, используя активные - значимые для компилятора - средства языка, но и лаконично задокументировать это решение с помощью пассивных средств языка программирования.
Хорошо известна давняя история о том, как запятая в программе явилась причиной гибели космической ракеты. Отсутствие запятой в комментарии не окажет влияния на исполняемый код, но может привести людей, которые будут когда-либо читать исходный код, к серьезным ошибкам. Иными словами, не только Си, Паскаль и Ада, но и русский, английский, французский в широком смысле слова также относятся к языкам программирования. А если более строго - то к языкам программирования не компьютеров, но программистов. Описание алгоритмов, идей и методов программирования конкретных задач обычно также ведется на естественном языке. Попытки использования искусственного языка, подобно тому, как это сделал Кнут, введя гипотетическую машину MIX, нередко затруднительны хотя бы уже за недостатком места — в небольшой статье это исключено. Приведенные примеры не исчерпывают и малой части информационных технологий, активно развивающихся сегодня. И каждая из этих технологий выдвигает свои, нередко очень специфические требования к языкам программирования и инструментальным средствам их поддержки. Острая конкурентная борьба разработчиков программного обеспечения еще более ужесточает эти требования: каждый пользователь языковых средств хочет, чтобы его проект был реализован в максимально сжатые сроки с минимальными затратами, чтобы языковая среда не преподнесла в процессе работы над проектом и в процессе дальнейшей поддержки программного продукта неприятных сюрпризов в виде ошибок компилятора при генерации кода или не документированных ограничений в отношении распределения памяти и т. д. Каждый разработчик хочет иметь не просто хороший компилятор с хорошего языка, но и мощную интегрированную среду, включающую отладчик на уровне исходного кода, профайлер для поиска критических по времени выполнения участков программы, средство просмотра, навигации и анализа исходного текста, средство поддержки проекта и ведения архива версий (projector) и т. д.
Сегодня существует множество языковых средств, поддерживающих различные языки программирования и с успехом применяемых для профессиональных целей, научных исследований и образования. Независимо от того, нравится кому-нибудь ситуация Вавилонского столпотворения или нет, реальность такова, что языки программирования вынуждены сосуществовать друг с другом. И человечество еще долго не придет к согласию в вопросе об едином "компьютерном эсперанто". Такая ситуация порождает ряд дополнительных проблем, связанных с практическим использованием языков программирования и, в частности, проблему перевода с одного языка высокого уровня на другой язык высокого уровня.
К сожалению, существующие на сегодняшний день инструменты подобной трансляции оставляют желать много лучшего. Есть, например, трансляторы с Паскаля на Си, и наоборот, однако даже тривиальные фрагменты исходных текстов, оттранслированные этими инструментами, нуждаются в серьезной переработке программистом. В теоретическом плане возможность стопроцентного перевода также вызывает сомнения. Однако, если теоретика интересует чистый результат, то практику приходится довольствоваться тем, что есть. И в практической работе даже "восьмидесятипроцентная трансляция" экономит разработчикам столько времени на рутинных операциях, что полезность столь грубых инструментов не вызывает сомнений.
Другим важным моментом является скачкообразный рост доступности языковых средств в общемировом масштабе. Тут две основных причины: развитие Internet и повсеместное распространение дешевых и емких CD-ROM. Кроме того, происходит объединение возможностей этих двух в принципе независимых направлений. Например, пользователь начинает работу с HTML-документами, находящимися на CD-ROM в его компьютере, затем, обратившись по гипертекстовой ссылке, он через Сеть получает следующий документ с противоположной точки земного шара. Подобным образом организована периодически пополняемая гипертекстовая библиотека документации и исходных текстов для разработчиков ПО для Macintosh. Сейчас, когда эффективные сетевые технологии стали реальностью, по-новому проявляются вопросы ориентации в доступной программисту лавине информации. В прошлом было предложено множество решений для анализа гипертекстовых баз данных (многие из них основывались на приложениях теории графов). Сегодня речь фактически идет о тех же задачах, однако их сложность многократно возросла - ведь объект теперь не какая-нибудь локальная база данных, а вся "всемирная паутина" - World Wide Web.
Михаил Трофимов (mtrofimov@glas.apc.org), ИОХ им. Зелинского РАН, Мария Трофимова -- Союз Театральных деятелей (СТД) России.