Особо амбициозные разработчики любят заявлять о том, что используемый ими язык программирования или фреймворк будет жить лет сто. Дерзкое заявление, учитывая, что разработке программного обеспечения, как таковой, всего около 65 лет. А фреймворки ещё моложе.
Можно почитать Пола Грэма и его разглагольствования о столетнем языке. Но он имеет в виду немного другое: язык, потомки которого будут с нами и через 100 лет в более или менее узнаваемом виде. То есть, если язык C заменит Algol, это нормально. Пола интересует другое, а именно, какие функции Algol достаточно хороши, чтобы выжить при переходе на новый язык.
А мне интересно, какие языки все ещё будут использоваться через 100 лет.
Есть такое популярное мнение: «чтобы язык можно было использовать и через сто лет, он должен иметь хорошую производительность». Как бы вы оценили подобное заявление? По каким критериям проводили оценку?
Это не риторический вопрос. Приступаем к оценке.
Что могут рассказать нам древнейшие языки?
Раз уж языкам программирования всего 65 лет, у нас попросту нет примеров столетнего языка. Зато есть несколько языков программирования, которым 60 и более лет. Что мы можем о них сказать?
Как только язык становится популярным, он обычно не умирает, но часто мутирует. Версия Fortran 2008 года не совсем похожа на Fortran77 или оригинальный Fortran (1957 год). Но у них много общего. Язык остаётся узнаваемым, у него есть стабильное сообщество программистов. Фортран всегда был популярен в научном и математическом сообществах и остаётся таковым до сих пор. Например, Fortran является основным языком программирования для суперкомпьютеров, для которых в приоритете высокая производительность, а значит, нужен максимально быстрый язык. Fortran значительно быстрее, чем, к примеру, C. Именно потребность в высокой скорости и преданное сообщество разработчиков дают Фортрану дыхание жизни.
Получается, когда мы говорим про столетний язык, мы на самом деле не имеем в виду преемственность синтаксиса или функций. Мы ищем сообщество непрерывного программирования. Интересным примером является Perl: версия Perl 6 (теперь называемая Raku) должна была внести значительные изменения в язык, что и произошло. Но процесс занял много лет. В итоге Perl 5 оформился как отдельное сообщество, а Perl 6 пришлось пробиваться, как новому языку. Если бы изменения Perl 6 случились быстро, мы бы, конечно, до сих пор имели единое Perl-сообщество. Perl 1 и 2 сильно отличались от Perl 5, но никто и не думал считать их разными языками. То есть именно раскол среди разработчиков превратил Perl 6 в другой язык.
Ещё одним старым языком является LISP, у которого множество потомков, включая Guile, Scheme, Clojure и Racket. Они никогда не были особо популярны, но исчезать не собираются. LISP не умер, хотя умерли многие его небольшие диалекты. У более крупных диалектов (например, CommonLISP) есть свой собственный импульс, и они тоже никуда не делись. Итак: речь не всегда идёт именно об одноязычном сообществе программистов. Вы можете возразить, что даже если Clojure разрастётся до огромных размеров, сам LISP останется мёртвым. Но речь идёт об очень конкретном, оригинальном LISP. Как и в случае с Perl 1, его потомки сильно изменились, но в них по-прежнему легко узнаётся один и тот же базовый язык. Что еще более важно, вы можете легко выучить новый диалект LISP, если знаете старый. У них много общего.
Как могут умереть языки?
Я говорю "язык мёртв" в том же смысле, в каком мертва латынь. Существуют небольшие изолированные сообщества академических и религиозных носителей латыни. Но это не повседневный язык для значительного числа людей.
Если вы этого ещё не сделали, советую прочитать статью об истории языков программирования.
Алгол (1958) мёртв. У него есть потомки, но они мало похожи на Алгол. Итак: это один из вариантов того, как может умереть язык. Его могут полностью “съесть” более совершенные потомки, которые заберут всю его пользовательскую базу. «Семейство алголов» включает в себя таких дальних родственников, как Perl, и менее используемые ответвления вроде Delphi. А ещё туда входят C, C++, Visual BASIC и Java. Это чрезвычайно популярные языки, которые давно поглотили всё сообщество разработчиков Алгола. В частности С был молодым и чрезвычайно популярным, поэтому не было никаких причин продолжать использовать Алгол.
Некоторые языки так и не прижились. FLOW-MATIC был первым языком Грейс Хоппер до того, как она написала COBOL. Да, он оказал влияние на COBOL, но сам по себе никогда особо не использовался. И многие ныне мёртвые языки были такими: мало пользователей, маленькие коммюнити.
Сюда также можно включить множество «протоязыков». Это языки, которые по большей части мутировали во что-то другое. FLOW-MATIC превратился в COBOL, поэтому его крошечное сообщество пользователей перетекло в сообщество COBOL. Именно так многие языки умирают: они присоединяются к другому языку, и их сообщество становится частью сообщества этого нового языка.
«Предшественники» и протоязыки не обязательно должны разрабатываться одними и теми же людьми. FACT (1959) сейчас считается предшественником COBOL, поскольку он повлиял на дизайн, и его пользователи стали частью сообщества COBOL. Но FACT не был проектом Грейс Хоппер. Точно так же C был назван в честь BCPL. Пользователи BCPL (их было не так много) стали пользователями C. Но BCPL был написан Мартином Ричардсом, который не входил в число авторов C. Так что дело даже не в том, кто написал язык. Речь идёт о том, куда переходит сообщество его пользователей.
Что значит мёртвый?
Прежде чем идти дальше, остановимся на очевидном и несколько глупом использовании слова «мёртвый» в отношении языков программирования.
Языки программирования пишутся людьми для людей. Это человеческий язык и человеческая культура. Они точно так же создают функциональные компьютерные программы. Они точно так же восприимчивы к тенденциям, как и любой другой человеческий язык или культура.
Когда вы слышите, что «Rails мертв», это вовсе не означает, что «ни одна программа больше не использует Rails», «ни одна компания больше не использует Rails», или «никакие новые проекты не написаны на Rails». Они означает лишь, что «Rails больше не популярен, и вместо него вы должны изучить что-то другое».
Это также может означать, что для программистов на Rails меньше рабочих мест, чем для тех, кто владеет другими языками, меньше фреймворков или технологий. Может означать, что основатели стартапов предпочитают что-то другое. Но совершенно не означает, что Rails в принципе больше не используется или вообще непригоден.
Так что определение “мёртвый” в смысле потери популярности можно спокойно игнорировать для любого языка, которому более 25 лет. Он в любом случае не является новым, популярным или сильно раскрученным. Абсолютное, 100%-е требование к 100-летнему языку — быть, по этому определению, мертвым.
Что ослабляет языки
Языки не умирают сразу. Они медленно угасают, пока последний пользователь не перейдёт на другой язык.
Кое-что серьёзно раскалывает сообщество. Perl 5 и Raku стали намного слабее после разделения. Переход Python от версии 2 к версии 3 был трудным и стоил ему части аудитории. Переход Ruby с 1.8 на 1.9 был схожим, хотя и не таким медленным и драматичным. Когда вы делаете что-то, что раскалывает сообщество, оно естественным образом становится слабее. В крайних случаях, таких как с Perl, сообщество становится настолько маленьким, что может вымереть.
Это одна из причин, по которой языки уничтожаются их собственными потомками. Зачем оставаться с Algol, когда существует C? Зачем оставаться с FLOW-MATIC, если автор перешёл на COBOL? Потомок языка часто более привлекателен для сообщества, чем сам язык.
Что ещё более важно, языковые сообщества, как правило, переходят на тот инструмент, который решает те же проблемы. Разработчики переходят с Perl на Ruby или Python — языки, решающие схожие задачи аналогичным образом. Между Python и R существует постоянное противостояние, потому что оба решают задачи для статистического анализа данных. То есть языком пользуются не ради самого языка, а ради решения вполне конкретных проблем.
Проблема обычно не только в том, что у языка появляется новый конкурент, а в том, что старое решение перестаёт работать. Ruby и Rails имели репутацию феноменально простого стека для разработки веб-приложений по сравнению со старыми средами Java, такими как JBoss. Но в более поздних версиях Rails пришлось всё больше и больше укреплять инфраструктуру из-за угроз безопасности, что усложнило развёртывание. В свою очередь облачная PaaS-платформа Heroku перестала предлагать бесплатный тарифный план, а очевидной замены для неё не оказалось. Следовательно, Rails сейчас сложнее развёртывать, чем раньше. А значит, она хуже решает проблемы пользователей. Это ослабляет фреймворк и сам язык Ruby, который подпитывался от сообщества пользователей Rails.
Итак, языки начинают угасать, если они перестают решать проблему так же хорошо, как раньше, или если появляется конкурент, который решает проблему лучше.
Что укрепляет язык?
Если язык должен решать проблемы пользователей, это означает, что проблема имеет первостепенное значение.
Нишевость укрепляет язык. Язык R отлично подходит для статистического анализа данных и занимает важное место в этом сообществе. Никто не станет писать на нём операционную систему, с этим согласны все участники, но оно и не требуется.
Java — самый популярный в последнее время язык общего назначения. Все последующие являются нишевыми: Python хорош в математике, научном программировании и программировании искусственного интеллекта, а Ruby отлично обрабатывает динамические веб-приложения. JavaScript доминирует в браузерном программировании и нескольких связанных с ним ниш на стороне сервера. Fortran, когда-то универсальный, теперь используется в суперкомпьютерах и некоторых математических приложениях. C отступает в нишу операционных систем и драйверов, поэтому теперь он исключён из общего программирования приложений.
Ниши — это сила.
Я не утверждаю, что «Java — это последний язык общего назначения». Учитывая 65-летнюю историю языков программирования, заявлять такое было бы странно. 27 лет существования Java не означают, что не будет другого популярного языка общего назначения. Но, действительно, скорее всего такие языки будут появляться редко.
Чтобы язык действительно мог претендовать на универсальность, следует избегать нишевости. Даже у Java есть некоторые существенные ограничения на сферы применения, например, большой объем памяти делает его сомнительным для небольших встроенных процессоров. Может быть, «универсальность» вообще сойдёт на нет, размывшись многими и разными нишами.
С другой стороны, ниша означает, что у языка есть фокус и цель. Нишевость сильна, потому что она сообщает своему сообществу, для кого предназначен язык и какие задачи решает.
Нужна ли производительность столетнему языку?
Fortran, один из старейших процветающих языков, который продолжает жить благодаря производительности. Это серьёзный аргумент “за”.
LISP, ещё один из старейших, совершенно не заботится о производительности, но процветает благодаря гибкости и простоте реализации. Это аргумент “против”.
Впрочем, у нас тут не голосование. На деле цель Fortran — производительность, а LISP уделяет основное внимание гибкости и удобству. Так что мы опять возвращаемся к цели использования. Нужна ли производительность? Смотря какие задачи решает язык.
Производительность — это неплохо. Но если вы спросите сообщество Fortran: «Нужно ли вам больше простоты в реализации вашего компилятора?» они скажут «Без разницы». Сообщество LISP не может сказать, что их вообще не волнует производительность, но им интересны и гибкие варианты с низкой производительностью. Вопрос не в том, «хороша ли высокая производительность?» Вопрос в том, «какое место она занимает в вашем списке приоритетов?»
BASH, оболочка командной строки, имеет довольно низкую производительность и всегда была таковой. Но не этим он притягивает пользователей. Сообществу требуется совместимость, стабильность и простота использования. Если бы вы сделали массовую высокопроизводительную версию BASH, её использовало бы всего несколько человек. Точнее его бы использовали, если бы это не было не в ущерб одной из основных целей. Таким образом, наш гипотетический высокопроизводительный BASH уже мертворожденный, потому что «универсальная доступность» и «100% совместимость» слишком важны для его сообщества.
Меняются ли приоритеты?
Если что-то не было приоритетом на заре появления языка, это не означает, что сейчас оно таковым не является. Первые версии Ruby или Python могли быть очень медленными, но часть их нынешних пользователей может заботиться о производительности гораздо больше, чем о выразительности. Возможно, PHP изначально был ориентирован на простоту развертывания, но нынешняя база пользователей фреймворка Laravel может захотеть более чистого, более выразительного основного языка, ради которого готова пожертвовать совместимостью и доступностью.
Тут нет ничего невозможного.
Проблема в преемственности сообщества. Очень сложно отогнать одно сообщество, а потом привлечь другое. Слишком много старой информации о языке теперь неактуально. Неверная информация привлекает людей, которые вам не нужны, и отталкивает тех, которым нужно ваше текущее предложение. Итак: изменение того, что было фишкой приложения, — одна из самых опасных вещей, которые могут произойти с языком. Это может стать причиной раскола сообщества, о котором говорилось ранее.
Вы можете внести небольшие изменения. Немного дополнительной производительности никого не оттолкнет. Но в какой-то момент вам придётся идти на компромисс.
Ruby стал использоваться более крупными компаниями. В целом это хорошо. Но это также было и переломным моментом. Стоит ли снижать выразительность из-за того, что неограниченная выразительность вызывает проблемы с большими командами? Может быть. А если внести это изменение, увеличит ли оно размер сообщества Ruby и продолжительность его жизни? Подобные изменения несут в себе самые большие риски смерти языка.
Между прочим, это одна из причин, по которой проекты по оптимизации Ruby, такие как YJIT и TruffleRuby, не стремятся ограничивать возможности Ruby. Дополнительная производительность — это хорошо, но именно благодаря выразительности Ruby получил своих фанатов. Производительность — это круто, если она не ставит под угрозу выразительность. Да, это касается не всех языков, но для Ruby оно работает именно так.
Быстрее, медленнее
Беспокойство по поводу приоритетов стало причиной того, что переход с Python 2 на Python 3 занял так много времени и мучений. Python делает очень медленные, преднамеренные изменения с отличной обратной совместимостью. В тех редких случаях, когда он нарушает обратную совместимость, пользователи воспринимают это как предательство.
С Ruby было легче, потому что Ruby не обещает такой большой обратной совместимости. Их философия скорее такая: «в основном мы сохраняем некоторую обратную совместимость, но иногда, когда мы хотим сделать язык лучше, что-то ломается, так что вам придётся подстроиться». В результате Python рулит в математическом/научном сообществе, которому нужны программы, способные работать и 20 лет спустя, а Ruby завоевал сообщество веб-программистов, где стандарты меняются каждые 5 лет.
Между прочим, это не означает, что Ruby прав, а Python нет, или наоборот. Это снова к вопросу о нишах. Можно быть настолько крутым в чём-то одном, что вы обойдёте язык общего назначения, который вроде бы подходит для всего, но уступает в конкретных параметрах нишевым языкам.
Язык, завоевавший веб-нишу, быстро меняется, как Ruby. Язык, завоевавший нишу математики/науки, медленно меняется, как Python.
Не исключено, что один язык может быть НАСТОЛЬКО хорош, что завоюет сразу оба сообщества. Но в конце концов ему придётся конкурировать с потомками, которые снова будут превосходить его по тому или иному критерию.
Кстати, именно так и вышло с Perl. Давным-давно Perl был единственным языком сценариев. И он всё ещё неплох для объединения больших кусков текстовых файлов. Но теперь он конкурирует с Ruby, который быстро меняется, и с Python, который меняется медленно. Ruby занял веб-нишу, которую Perl, соответственно, потерял.
Это не ограничивается языками сценариев. C++ меняется намного быстрее, чем C, по разным причинам. C получил преимущество в некоторых областях за счёт медленных изменений. Как часто разработчики драйверов меняют языки? Им нравится стабильность. C++, который меняется быстрее, получил популярность в других областях, таких как программирование приложений. Это далеко не единственная разница между C и C++. Но она важна.
Слон в посудной лавке
Мы ещё не успели толком поговорить про COBOL. Программистов на COBOL немного, и практически все они обслуживают старый код. Многие миллионы строк COBOL поддерживают работу огромного количества банковской инфраструктуры. Он оставался на своём месте, практически без изменений, в течение десятилетий. Там, где от COBOL избавлялись, обычно делали механический перевод с COBOL на другой язык (в основном Java).
С точки зрения количества строк кода, COBOL ещё жив и бодр. Если завтра весь код COBOL испарится, вся международная банковская система мгновенно рухнет.
С точки зрения людей, которые используют его для новых проектов, COBOL почти мертв. На COBOL написано очень, очень мало новых проектов.
Так он жив или мёртв? Скорее мёртв, чем жив.
Не существует большого сообщества разработчиков COBOL. COBOL — это то, что происходит, если вы никогда не вносите изменений в язык, в конечном итоге, ваше сообщество уже не заботится о том, что именно вы предоставляете. Меняться — значит рисковать. Язык может умереть немного или насовсем. Никогда не меняться — значит умереть абсолютно точно.
Может, дело в том, что COBOL устарел? Не совсем. COBOL немного моложе Fortran, а Fortran на удивление здоров и энергичен в своей нише.
Это напоминает нам о том, что фраза «этот язык никуда не исчезнет, его использует компания X» не совсем верна. Google широко использует C++, но это не должно быть единственной причиной его выживания. Ruby используется Shopify, но это только одна из причин его долговечности.
Вам нужны новые пользователи, новые обещания, новая кровь.
Как устаревают обещания?
Одним из старых обещаний (особенностей) C было вести себя как компьютер PDP-11. Он по-прежнему делает это, но это больше не является приоритетом. Как ни странно, ранний LISP делал то же самое. Это старые языки. Эти функции можно рассматривать как старые обещания.
Первоначально LISP требовалось совсем немного производительности — ровно столько, чтобы сделать его математическую выразительность работоспособной. Со временем процессоры стали быстрее, и от этого отказались — car и cdr определённо не дают простой доступ к регистрам в современных реализациях LISP, и никого это не волнует. Если производительность приемлемая, остальное не так важно. И планка «приемлемости» на тот момент была довольно низкой. CommonLISP, например, был намного медленнее, чем C, и в основном это считалось нормальным.
Вы можете подумать, что для C такие изменения будут проходить сложнее. Если C обещает быть похожим на процессор, то что он делает, когда процессор меняется? Новые процессоры используют инструкции SIMD, например, инструкции Intel SSE. Они выполняют операции с большими массивами, и это очень важно для быстрого выполнения объёмных задач. C никогда не добавлял для них никакой абстракции. Он по-прежнему выглядит в основном как PDP-11, в котором такого не было.
Но часто мы не понимаем, какие особенности важны для нашего сообщества разработчиков. C не должен быть самым быстрым языком. В противном случае такие проблемы, как перекрытие указателей, заставили бы его первых пользователей обратиться к Фортрану, более быстрому языку без этой проблемы.
C всегда был достаточно быстрым, приятным языком, который обеспечивал хороший баланс между мощностью и производительностью. И, честно говоря, в период расцвета у него было меньше конкурентов, поэтому он мог особо не беспокоиться о своих особенностях и своей нише. Современный C в его современной нише (операционные системы, драйверы устройств, низкоуровневое системное программирование) больше ориентирован на управление, чем на производительность. Его производительность должна быть приемлемой, но, в первую очередь, речь идёт о точных побитовых макетах. Если тут и там сбрасывается несколько циклов процессора, остаётся ещё много свободного времени. Поэтому драйверы устройств всё ещё пишутся не на Фортране.
Особенности меняются с годами. Они должны. Не нужно избегать любых изменений. Нужно правильно управлять ими.
Как сильно меняются обещания языков программирования? Язык C родом из 1972 года, он появился 50 лет назад. Благодаря всем изменениям, которые он претерпел за эти годы, он находится на полпути к тому, чтобы стать столетним языком. Он всё ещё крепок и полон сил. Думаю, у него получится.
Итак, как продержаться 100 лет?
Что, если вы хотите, чтобы ваш язык просуществовал 100 лет? Что, если вы состоите в языковом сообществе, которое вам нравится, или вы языковой дизайнер? Что бы вы сделали, чтобы создать столетний язык?
Вы не можете просто сосредоточиться на особенностях языка и синтаксисе. Чтобы сохранить язык живым, нужно знать, что вы обещаете своему сообществу. Почему они здесь? Это бывает непросто: конкретная новая функция — это хорошо или плохо? Иногда вы попадаете в конфликт с основной причиной, по которой аудитория пользуется языком. В Fortran пользователи не оценят выразительность в ущерб скорости. В Руби наоборот. В Rust безопасность памяти занимает одно из первых мест в списке приоритетов. В C возможность возиться с памятью всякими странными способами ценится больше, чем безопасность.
Ни один из приоритетов не является неправильным. Они определяются нишей.
Это также означает, что нужно быть осторожным, «вырываясь из своей ниши». Ваша ниша — ваша сила. Есть все признаки того, что в будущем языки будут больше ориентированы на ниши, а не меньше.
Это также означает, что следовать различным известным советам Генри Форда стоит очень осторожно. «Если бы я спросил людей, чего они хотят, они бы попросили более быструю лошадь.». «Автомобиль может быть любого цвета, если этот цвет – черный». Иногда вы можете проявить дальновидность и дать своему сообществу то, о чем они и не думали просить. Но если они говорят, что им что-то не нужно, стоит прислушаться. На каждого Генри Форда в мире приходится очень много людей, которые совсем не Генри Форды и угадывают хреново.
Спросите себя: «Каковы ваши обещания и особенности?» Подумайте о том, как они устаревают. Будут ли люди нуждаться в том, что вы предлагаете, через 100 лет? Что им было нужно 100 лет назад?
Да, это сложно. Но стоит понимать, что 100 лет – это на самом деле очень долго.
А как вы считаете, какой язык программирования в ближайшее время умрёт?