Автор: Виктор Шепелев
Как уже было сказано, подавляющее большинство языков программирования из «настоящего мэйнстрима» ведут свою идеологическую родословную напрямую из машинного языка/ассемблера. Конечно, по этому пути они успели зайти далеко, но каждый следующий шаг был лишь логическим продолжением предыдущего.
В этом «естественном развитии» популярных языков новые концепции вводились путем постепенного «уточнения парадигмы»: новые возможности вводятся как почти «синтаксический сахар» (более краткая, удобная и понятная альтернатива существующим конструкциям, ничего принципиально не меняющая), но возникающие нюансы и вопросы и разрешение этих нюансов и вопросов приводит к формулировке новых идей [Для примера: использование именованных переменных вместо регистров и адресов памяти поднимает вопросы о типах данных; структуризация кода с помощью процедур и модулей порождает вопрос «области видимости» переменных, а также разницы между «передачей по ссылке» и «передачей по значению»; и т. п.].
Господствующая парадигма [Любим мы это слово. По большому счету, оно означает «подход», «модель построения программы или ее частей», «способ думать об архитектуре программы» - что-то в этом духе] - программирование императивное: программа - суть набор инструкций «сделай то, потом сделай это», результаты действий сохраняются и изменяются в именованных ячейках - «переменных». Отслеживая историю развития промышленного программирования, можно заметить, что все новые «победившие» языки развивали, а не опровергали эту парадигму.
Первым [Попытки «автоматизировать программирование» были и до Фортрана - «язык» A-0 для компьютера UNIVAC (1952), экспериментальный «транслятор формул», созданный в MIT (1954); но эти реализации показывали чудовищную неэффективность сгенерированного машинного кода, что породило стереотип «никакая автоматизация не сможет заменить человека-программиста, пишущего на ассемблере». Фортран этот стереотип разрушил] реально используемым высокоуровневым языком программирования стал Fortran [Имена ранних языков программирования, как правило, писались большими буквами (FORTRAN, COBOL, ALGOL, LISP…). Причина тут не в склонности к аббревиатурам (довольно вымученным, вроде FORmula TRANslator), а убогость тогдашних средств ввода/вывода, зачастую оснащенных только шрифтами с заглавными буквами. «Как правильно» писать название языка - иногда непонятно и самим авторам, пишут и так и эдак. Мы предпочли вариант, более симпатичный с типографской точки зрения (кроме случаев, когда название языка - явная аббревиатура: PL/I, PHP)] (первое описание - 1954, первая реализация - 1957). В немалой степени перво-Fortran - это «подсахаренный» ассемблер; но это был огромный шаг вперед, хотя бы в том смысле, что вычисление A+BхC можно было записать так, как понятно математику, а не как набор операций по загрузке значений в регистры и вычисления ответов в других регистрах.
Проблемы со структурой программ на Фортране (вкратце: структуры не было) привели к разработке языка Algol (1958). Судьба его весьма показательна: совместная разработка американских и европейских ученых, к которой приложили руку многие «легенды» IT; в процессе работы над Алголом были разработаны концепции структурного программирования (логические структуры для ветвления кода; разбиение программы на процедуры, положившее начало созданию библиотек кода для повторного использования, и т. п.); следующие тридцать лет Алгол будет де-факто стандартом для описания алгоритмов. При этом уклон авторов Алгола в «теорию» (эффективного компилятора нет; стандартных операторов ввода-вывода нет) привел к тому, что использование этого языка в промышленном программировании было мизерным.
Из первоязыков еще стоит упомянуть Cobol (1959), чудовищный как язык, но крайне успешный как платформа для создания бизнес-приложений. Что показательно.
Следующее десятилетие - эпоха экспериментов на ниве создания «самого лучшего языка». В широком использовании продолжают царствовать Fortran/Cobol, к ним добавляются языки класса «все-все-все-в-одном» PL/I и CPL [В этот же период созданы Lisp и Snobol, речь о которых - в следующей статье], тяжелые и для изучения, и для реализации. В районе 70-х происходит первая «большая чистка»: парк компьютеров растет лавинообразно, возникает необходимость в языках простых и практичных, которые легко выучить, легко реализовать под различные аппаратные платформы, легко писать и читать код; при этом возрастает количество «программ-ради-программ», не решающих некую бизнес-задачу, а облегчающие работу с самим компьютером.
Си вкупе с Unix; разномастные Бейсики как встраиваемые языки для первых домашних (и мелких недомашних) компьютеров; Pascal в Apple II/Apple III, чуть позже Паскали от фирмы Borland для простого написания программ под DOS, Windows - все это языки весьма простые [Basic, в частности, настолько прост, что даже структурного программирования в нем не было. Зато это давало возможность сделать крайне нетребовательную к ресурсам реализацию языка, что поспособствовало его распространению на «несерьезных» компьютерах. С ростом ресурсов этих компьютеров (и запросов программистов) структурность в Бейсике появилась (в середине 80-х)] и практичные; это, по большому счету, те языки, с которыми программирование стало действительно массовым занятием.
По мере расширения круга задач, решаемых на «простых» языках, количества повторно используемых библиотек и «времени жизни» этих библиотек стали возникать концепции более сложного структурирования кода. Самая популярная из них - объектно-ориентированное программирование; совмещение концепций «набора процедур и данных (модуля)» и «типа данных со сложной внутренней структурой» дало понятие «класса» и «объекта» [Большая часть концепций классического ООП была разработана в середине 60-х в рамках работы над языком Simula (Ole-Johan Dahl, Kristen Nygaard). Судьба его достаточно близка к судьбе Алгола: разработанные концепции были приняты и воплощены во многих успешных проектах, но сам язык использовался весьма ограниченно]. Мэйнстримовая разновидность ООП [О более радикальном наборе концепций с тем же названием - в следующей статье.9 Интересно, что другая разновидность «объектно-ориентированного C», известная под именем Objective C и зачастую воспринимаемая как забавный курьез, была языком вполне постмодернистским, смешавшим концепции классического C и модернистского Smalltalk. Распространение этого (и других «странных») языков исключительно в мире Apple весьма показательно] - естественное эволюционное развитие структурно-императивного подхода. Неудивительно, что и объектно-ориентированные языки, принятые «широкими массами», были естественным развитием все тех же C, Pascal, Basic - Visual Basic, C++ [Интересно, что другая разновидность «объектно-ориентированного C», известная под именем Objective C и зачастую воспринимаемая как забавный курьез, была языком вполне постмодернистским, смешавшим концепции классического C и модернистского Smalltalk. Распространение этого (и других «странных») языков исключительно в мире Apple весьма показательно], Object Pascal (позже Delphi).
Далее мэйнстримовая, структурная парадигма некоторое время дополнялась (например, шаблонами C++, позволяющими писать «обобщенные» классы и «обобщенные» алгоритмы). Но картина мира вновь начала меняться, что привело к очередной «большой чистке» языков и смене расклада, двадцать лет казавшегося незыблемым. По своей важности эти перемены близки к событиям, в результате которых Fortran, Cobol и PL/I сменились Cи, Бейсиком и Паскалем.
Причин тому было много, так что нельзя выделить одну, главную. Важнейшие, видимо, таковы: рост производительности железа, с одной стороны, и востребованности программистов (даже неквалифицированных) - с другой. Поскольку надежность софта становится важнее его быстродействия [В определенных, естественно, пределах. Тем не менее некогда одна из важнейших целей разработчиков C++ - «почти бесплатность (по производительности)» новых концепций - стала анахронизмом]; возникновение и популяризация компьютерных сетей «для всех» (в том числе и Интернета/веба), в результате чего «сетевое программирование» стало всеобщей деятельностью. С точки зрения пресловутых «парадигм» программирования важнейшая тенденция «нового времени» - компонентно-ориентированное программирование: независимые друг от друга компоненты могут быть написаны на разных языках, поставляться в скомпилированной форме, заменяться на лету, взаимодействие между ними должно быть легким, надежным и масштабируемым.
Попытки использования «компонентного» стиля без смены языка (COM/OLE, CORBA) выявили некоторые концептуальные трудности; собственно, попытка создать целостное решение этих трудностей и породила платформы Java и .Net [История Java, впрочем, довольно извилиста; в разное время у Sun было множество разных версий насчет «что это мы делаем и зачем оно надо». Тем не менее на сегодняшний день платформа Java - более или менее прямой конкурент и аналог .Net.]. Их свойства (богатая стандартная библиотека, автоматическое управление временем жизни объектов, наличие виртуальной машины и т. п.) - прямой ответ на те вызовы, которые бросает компонентность. Что же касается языков Java и C# [Заметим, что платформа .Net принципиально многоязычна; Java, изначально бывшая «платформой для одного языка», сегодня движется в том же направлении. Тем не менее мы-то здесь рассматриваем в первую очередь историю языков программирования], то их архитектура и дала мне основания назвать происшедшее «второй большой чисткой»: как в свое время C, эти языки стремились вобрать в себя все «хорошие идеи» своего времени, но вдобавок избавиться от наследственной сложности, неоднозначности и других проблем, свойственных C++/Delphi/Visual Basic. Первые версии обоих новых языков таки были проще предшественников, но дальнейшее развитие снова пошло по спирали накопления возможностей и впитывания концепций. Сегодняшний C# - сложный, лаконичный и мультиконцептуальный язык; Java - консервативнее в своем стремлении к простоте и однозначности, но постепенно подбирается к той же планке.
Некоторые языки программирования, близкие «классицизму», но не попавшие в статью, весьма достойны упоминания - хотя бы совсем краткого.
Перечисленные языки - неотъемлемая часть истории развития средств написания программ; тем не менее всеобщая популярность обошла их стороной.
Линейка Pascal. Дело в том, что тот Паскаль, который стал популярным в руках фирмы Borland и который многие из нас учили в школе, от изначальной концептуально-чистой разработки Никлауса Вирта отличается довольно сильно, причем одни считают суть этого отличия «практичностью», другие - концептуальной грязью. Сам Вирт придерживается последнего мнения; будучи невысокого мнения о целостности и чистоте вообще всех широко используемых языков, Вирт и его ученики разработали несколько своих, «чистых и красивых» (Oberon, Modula, Zonnon).
Eiffel. Судьба Эйфелей и их создателя Бертрана Мейерса похожа на судьбу «настоящих Паскалей». Мейерс, как и Вирт, достаточно амбициозен в продвижении своих идей (в основном - об объектно-ориентированном программировании), называя их «единственно правильными»; распространен Eiffel нешироко, влияние его огромно.
Ada. Наконец, создатели самых разных языков программирования среди «вдохновляющих» называют язык Ada, разработанный в 80-х под руководством Пентагона. В каком-то смысле он был аналогом PL/I (не слишком удачная попытка собрать все возможные концепции в одном языке), но некоторые элементы Ada (в частности, ее система типов) оказали большое влияние на мышление авторов других языков.
Эволюционно нынешние «главные языки» ушли бесконечно далеко от машинных кодов. Накопление парадигм и подходов (а равно и снижение актуальности «простой модели компьютера», которая лежит в основе императивного программирования) практически исчерпало потенциал «классического», структурно-императивного взгляда на программирование, который в сегодняшних компонентных приложениях узнается с трудом. Что придет ему на смену? - этот вопрос мы пытаемся рассмотреть в заключительной статье темы.
Следует упомянуть и еще несколько языковых проектов, вполне классицистических, вполне успешных, но стоящих слегка на отшибе от «главного исторического вектора».
Во-первых, это юниксовский sh и его производные (bash, ksh, csh и далее со всеми остановками). Первые оболочки *nix-систем ведут свой род от Алгола; юниксовский подход к объединению маленьких самостоятельных утилит считается одним из первых примеров компонентно-ориентированного программирования. Среди отдаленных потомков sh - как постмодернистский Perl (о нем мы еще поговорим), так и безусловно классицистический Tcl (а о нем не будем).
Во-вторых, язык веб-программирования PHP - тоже вполне популярен и вполне классицистичен. Его часто называют среди наследников Perl, но от последнего PHP перенял в основном способ именования переменных и область применения; в остальном первые PHP - это почти чистый C (вплоть до имен библиотечных функций). Небывалый успех PHP - это успех не языка программирования (часто критикуемого за концептуальную уродливость), а успех утилиты для легкой разработки веб-приложений. Так и повелось.