А.Родионов 1988
Существующее на настоящий день обилие алгоритмических языков, используемых для программирования компьютеров, может смутить любого начинающего программиста, пытающегося выбрать из них тот, который подходит ему больше всего для решения данной конкретной задачи. Советы более опытных коллег могут зачастую лишь внести дополнительную путаницу, так как может оказаться, что один из них любит пользоваться компилятором с языка Pascal, другой — пишет только на С, а третий, вообще, никогда не знал ничего другого, кроме интерпретатора с языка BASIC. Если же коллеги начинающего программиста работают на достаточно мощных компьютерах, то ему могут посоветовать писать все свои программы на таких языках как Prolog или Аda, программисты «старой закалки» могут порекомендовать Algol, Fortran, Cobol, PL/I или даже RPG, найдутся и горячие сторонники языков APL и Lisp, так же как и отдельные энтузиасты других, менее распространенных языков или языковых диалектов… Чаще всего каждый из них будет утверждать, что для данной задачи лучше всего подходит тот язык и тот компилятор (или интерпретатор), которым пользуется он сам и к которому он больше всего привык.
Какой же язык выбрать? И, вообще, зачем нам так много языков? Не лучше ли придумать один, Универсальный Язык Для Программирования Любых Классов Задач? Можно учить этому языку детей в школах (и, заодно, навести порядок в курсе школьной информатики). Можно обеспечить компиляторами с этого языка все существующие типы ЭВМ (единовременное мероприятие такого рода обойдется не так уж дорого) и обязать разработчиков снабжать все новые модели компьютеров компиляторами с этого языка.
Как следствие, «всеобщая компьютерная грамотность» получит благоприятную почву для дальнейшего развития и расцвета, программы с компьютеров одного типа можно будет легко переносить на компьютеры другого типа, не надо будет учить новые языки программирования только из–за того, что на новом компьютере нет компилятора с уже знакомого языка…
Блестящие перспективы! Остается проверить, первыми ли мы до этого додумались? К сожалению, нет. История уже знает попытки создания языков программирования, претендующих на роль универсальных — все они потерпели неудачу. Рассмотрим причины этих неудач на двух наиболее известных примерах: языках PL/I и Algol-68.
Язык PL/I, был разработан фирмой IBM для компьютеров IBM/360 (позднее IBM/370) и предполагался для использования в качестве «универсального языка программирования» как системными, так и для прикладными программистами. Он включал в себя большинство известных к тому времени прогрессивных средств программирования, таких как операции с различными типами и структурами данных, имел средства динамического распределения памяти, мог базировать переменные по абсолютным и относительным адресам памяти (указатели), обладал мощными средствами работы с файлами, большим набором форматирующих функций, модульностью и развитыми средствами макрогенерации, объектный код мог быть оптимизирован (существовали разные варианты компиляторов: для быстрой отладочной компиляции и для создания оптимизированного кода). Важной особенностью PL/I было также то, что полный вариант языка был РЕАЛИЗОВАН (в отличие от языка Algol-68, полный вариант которого не был реализован вообще).
Тем не менее, несмотря на мощность языка и наличие нескольких хороших компиляторов с PL/I, предполагаемый всеобщий переход программистов на PL/I так и остался несбывшейся мечтой его разработчиков. Прикладные программисты, конечно же, использовали новые компиляторы с PL/I, но продолжали также пользоваться и компиляторами с языков Fortran, Cobol, RPG, а системные программисты писали свои программы на языке Assembler (в значительной степени это определялось тем, что Assembler/360 обладал одним из самых мощных макрогенераторов) и лишь сравнительно небольшая группа энтузиастов работала исключительно на PL/I. Оказалось, что желание «угодить на любой вкус» при разработке PL/I, пагубно сказалось на самом языке. Его конструкции «утяжелились», обилие средств и операций породило множество громоздких операторов — язык не приобрел такой желанной для программистов «простоты» при достаточной выразительной силе. И, конечно же, не могло быть и речи о том, чтобы перенести компилятор с такого языка на машины с другой архитектурой из–за слишком большой «привязанности» его к архитектуре IBM/360/370 и операционным системам этой корпорации. Справедливости ради отметим, что такие попытки все же предпринимались, но терпели неудачу.
Здесь же заметим, что язык C, разработанный гораздо позже для машин PDP-11 Д.Ритчи, и не претендующий на роль «универсального языка для всех», а создаваемый как язык системного программирования для операционной системы UNIX, сегодня приобретает все большее число сторонников среди системных программистов, работающих на самых разных компьютерах (к их числу относится и автор этих строк). Это связано в первую очередь с простотой и четкостью языка, а также его большой выразительной силой. Однако этот язык не может быть рекомендован для начинающих, так как он требует известных знаний в области системного программирования и имеет некоторые скрытые особенности и даже противоречия.
На приведенных выше примерах мы можем видеть, что существуют как минимум два различных класса задач, требующих различных языковых выразительных средств: это класс задач системного программирования (программы для разработки других программ) и класс прикладных задач (программы для получения результатов в других областях науки, техники, искусства и пр.). Первый из них обычно требует доступа к ресурсам компьютера и операционной среды для управления этими ресурсами, тогда как второй ориентирован только на уже существующие стандартные системные возможности. Совмещение различных средств этих двух классов в рамках одного языка может оказаться крайне затруднительным и привести к созданию громоздкого «мертвого» языка, даже при успешной разработке высокоэффективных и надежных компиляторов.
Язык Algol-68 постигла еще более незавидная участь, чем PL/I. Этот язык, задуманный не просто как «язык для всех программистов», но и как «язык для всех машин», так и не был полностью реализован ни на одном компьютере. Несмотря на то, что Algol-68 был гораздо более стройным с концептуальной точки зрения чем PL/I, он требовал очень громоздких компиляторов в силу чрезмерного обилия средств, предлагаемых программисту и сверх–универсальности в подходе к языку, как к объекту разработки (достаточно заметить, что в языке присутствовали даже элементы программы–генератора компиляторов!). Можно сказать, что Algol-68 погубила его универсальность — он предназначался для всех сразу и ни для кого в отдельности.
До сих пор мы рассматривали два основных класса задач: задачи системного программирования и задачи проблемного программирования. Но не следует забывать, что внутри этих классов существует свои подклассы задач, сильно отличающихся друг от друга и, следовательно, налагающих определенную специфику на разрабатываемые для них языки. В частности, в области системного программирования методы и средства используемые для разработки, например, компиляторов кардинально отличаются от методов и средств, используемых для разработки, к примеру, графического интерфейса системы, а в области прикладного программирования разработка системы заказов железнодорожных билетов не имеет практически ничего общего с системой для расчета траекторий небесных тел или с системой, предназначенной для автоматизации труда композитора. Приведенные в качестве примеров задачи описываются в совершенно различных терминологических плоскостях и, следовательно, требуют в идеале для их КОМПАКТНОГО описания своих, специализированных языков программирования. Можно возразить, что, тем не менее, такие задачи можно решать средствами какого–либо одного универсального языка программирования, мирясь с его неприспособленностью применительно к каждому отдельно взятому классу задач и неудобствами, из этой неприспособленности проистекающими. Но если развить эту мысль несколько дальше, то любую из этих задач можно в принципе решить и с помощью машины Тьюринга (отвлекаясь от ее быстродействия)! Речь здесь идет не о принципиальной возможности реализации какого–либо алгоритма на компьютере (этот вопрос достаточно разработан в теории конечных автоматов), а скорее об оптимизации использования человеческого времени, необходимого для программирования задач различных классов (хотя, конечно, возможны компромиссы на уровне специальных библиотек прикладных программ и/или макросов).
Таким образом, наш Универсальный Язык Для Программирования Любых Классов Задач начинает приобретать все большую бесплотность из–за удручающего обилия Классов Задач и их терминологической и операционной несовместимости друг с другом. Однако, еще не все потеряно!
Особым классом языков, имеющим право претендовать на «универсальность», являются расширяемые языки — то есть такие языки, выразительные средства которых могут расширятся за счет доопределения пользователем новых конструкций языка, и, таким образом, настраиваться на новые классы задач. К ним относится в первую очередь язык Forth, а также языки Lisp, Рефал и некоторые другие. Но доопределение синтаксиса и, тем более, семантики языка, требует серьезных знаний в области системного программирования и не может быть рекомендовано для всех пользователей без исключения тем более, что базовые версии таких языков обычно включают в себя относительно небогатый исходный набор языковых примитивов. Таким образом, этот класс языков также не подходит для использования в качестве «универсального». Кроме того, языки расширяемого типа преимущественно используют механизмы интерпретации конструкций языка во время исполнения программы вместо полной компиляции, что отрицательно сказывается на времени, требуемом для решения задачи (последнее замечание в значительной степени относится и к языку Prolog, а также к различным вариантам непроцедурных или комбинированных языков типа Snobol).
Итак, Универсальный Язык Для Программирования Любых Классов Задач не может быть создан? Как ни странно это может показаться на первый взгляд, но такой язык уже существует! Это наш, человеческий язык общения друг с другом — язык содержательной постановки задач. Направление, в котором может осуществляться дальнейшее развитие методов программирования лежит в рамках области под названием «искусственный интеллект», которая быстро развивается на наших глазах. Задачи, относящиеся к этой области требуют гигантских объемов оперативной и долговременной памяти и одновременной работы многих высокопроизводительных процессоров. Тем не менее, автор не теряет надежды, что уже при его жизни мощная экспертная система сможет в процессе диалога с человеком определить класс решаемой задачи, информационная система — запросить и уточнить исходные данные (в том числе, пользуясь базами знаний, доступными по линиям связи), блок порождения и оценки алгоритмов — оценить возможности и пути решения задачи (продемонстрировав их предварительно человеку), а генератор программ — создать и выполнить соответствующую программу. Автор также надеется, что в результате всей этой работы сможет появиться, как минимум, статья, подобная той, которую Вы только что прочитали.