Далее мэйнстримовая, структурная парадигма некоторое время дополнялась (например, шаблонами 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 — это успех не языка программирования (часто критикуемого за концептуальную уродливость), а успех утилиты для легкой разработки веб-приложений. Так и повелось.
Хроники чистого разума
…включая сам Common Lisp.
Следствие Морриса
Практически во всех областях человеческого знания существует некий «естественный», «самоочевидный» подход (от уже неоднократно помянутого литературного классицизма до летательного аппарата, машущего крыльями). Но по мере развития и взросления человеческого подхода к этой области появлялись альтернативные варианты «как это делать», жертвующие «естественностью и понятностью» ради «чистого искусства», или «идеологической стройности», или «практической необходимости». Зачастую новые подходы оказывались даже единственно верными («аппарат, летающий как птица» так и не был построен, а «противоестественные» самолеты, вертолеты и дирижабли — пожалуйста).
Если смотреть на голую идею программы-как-текста и программирования-как-творчества, отвлекаясь от того печального факта, что «все это надо как-то превратить в машинные коды», то можно прийти к нескольким разным ментальным моделям разной степени абстрактности. Любая из них имеет право на жизнь и, будучи воспринятой, зачастую «открывает новые горизонты восприятия». Неудивительно, что языков программирования, исходящих из таких вот «абстрактных моделей» и оттого совсем не похожих и друг на друга, и на линейку Fortran-C-Java, — вагон и маленькая тележка.
Появление таких языков часто порождает целые новые течения в «программировании-как-искусстве» — соратников, ненавистников, эпигонов и экспериментаторов; но до поры до времени эти течения оставались далеки от «широких масс». Дальше мы пройдемся с широкой сетью по самым заметным из них.
Smalltalk
Бывает так: сначала ты «снаружи», вокруг все очень обычное, очень знакомое и скучное. А потом мимо проносится Белый Кролик, при часах и при жилете. Дальше известно что: погоня, несколько минут свободного падения вниз по кроличьей норе — и ты уже «внутри», а из привычного и знакомого остался только ты сам, да и это ненадолго. Такие переживания — когда за поворотом вдруг распахивается целый новый мир — составляют если не смысл жизни, то что-то вроде того.
Три года назад у меня было ощущение, что ничего радикально нового в области технологии разработки ПО мне уже не откроется, а мечта о гибкой, живой и мощной среде программирования… Что ж, на то она и мечта.
Потом случилось так, что я сменил место жительства, а с ним и работу. Там, где я оказался, Smalltalk — не просто инструмент, а жизненная позиция, и даже предмет поклонения. Несколько недель у меня ушло на преодоление собственных предрассудков. Я считал, что сборка мусора несовместима с гарантированными временными характеристиками, что полиморфные вызовы медленны — а тут у меня на глазах ядро авиационного тренажера бодро крутило цикл расчетов сорок раз в секунду.
Труднее всего оказалось воспринять отсутствие границы между инструментами и разрабатываемой программой. «А вдруг, — рассуждал я, — создаваемая программа сделает что-нибудь не так? Ведь тогда рухнет не только она, но и среда разработки!» На практике все оказалось не так страшно. В частности, все изменения пишутся в специальный журнал (change file), откуда в случае аварии виртуальной машины легко восстанавливаются.
Постепенно я стал понимать, что отсутствие границы между инструментом и созидаемым объектом — это чуть ли не главное, что есть в Смолтоке. Вокруг места, где не проходит эта граница, концентрируются вещи, составляющие дух и душу Smalltalk-системы.
Написание кода в отладчике — не эффектный кунштюк, а удобный и практичный способ программировать, когда можно вживую пообщаться с каждым из участвующих в приостановленном вычислении объектов, а не вспоминать мучительно, как называется нужный метод и что именно он возвращает. Возможность затачивать Инспектор под конкретные типы объектов — не просто полезная особенность, а путь к иному способу думать о графическом интерфейсе, когда каждый объект системы способен говорить с человеком на специфическом диалекте единого визуального языка (первоначальное понимание роли графического интерфейса, которое можно проследить в Smalltalk-76 и в Fabrik, было именно таким; теперь мы возвращаемся к сходным взглядам на новом витке спирали в таких средах, как Morphic и Oberon/Bluebottle).
Самое же главное в Смолтоке — это то, что через него программисты необратимо меняются к лучшему. Можно потом писать хоть на ассемблере, хоть на VBA, но это будут уже другой ассемблер и другой VBA. Знание Смолтока необходимо для глубины восприятия. Необходимо, но, конечно, не достаточно, потому что остаются еще Haskell и Erlang, OCaml и Oz, и Scheme, и еще много других путей вниз по кроличьей норе, прямиком в Страну Чудес.
Декларация независимости
Академическое стремление иметь математически стройные средства программирования (к которым были бы применимы математические же методы проверки, доказательства, порождения и вывода) привели к созданию концепции декларативного программирования — идеологически стройного описания программы, которое «выполняется» неким умным компилятором-"думателем" [С термином есть некоторая путаница. Языки описания чего-либо, не являющиеся полноценными «языками программирования» (например, HTML/XML), также называют декларативными]. Разновидности (существенно разные): функциональное программирование — программа описывается как математическая функция, зависящая от других математических функций, затем вычисляется; логическое программирование — задается набор предикатов-утверждений, затем результат выводится из этих предикатов; программирование в ограничениях (constraint programming) — задаются только ограничения на результат, а компилятор вычисляет все результаты, удовлетворяющие этим ограничениям.
Из вышеперечисленных разновидностей лишь функциональное программирование воплотилось в широкую семью языков (языки-потомки «первого функционального» ML, максимально «идеологически чистый» Haskell, странный REFAL, практичный Erlang и другие [Lisp и его потомки постфактум тоже считаются функциональными]) и собрало широкое сообщество «функциональщиков». Сегодня термин «функциональное программирование» определяет как изначальную концепцию (программа-как-вычисление-функции), так и более широкий набор концепций (часть из них не являются обязательным свойством функциональных языков, но возникли именно в этой среде): функции как данные, анонимные функции, продолжения (continuations), сопоставление с образцом (pattern matching) и т. п.
Что до других способов программирования, традиционно называемых декларативными, то они оказались более узкоприменимы. К примеру, из языков общего назначения полностью и исключительно логическим является разве что Prolog; зато концепции логического программирования (и программирования в ограничениях) «окопались» во многих областях в форме библиотек к другим языкам и специализированных приложений.