Языки программирования будущего - анализ или пророчество

Автор: (c)Крис Касперски ака мыщъх

Какими будут языки программирования через несколько лет? С пятидесятилетним опытом за плечами мы можем дать вполне объективный прогноз. Языки эволюционируют намного медленнее программного обеспечения, создаваемого с их помощью и на радикальное изменение ситуации рассчитывать не приходится, если только не...

Введение

Попытка создания принципиально нового языка каждый раз наталкивается на что-то до боли знакомое старое. Идеи закончились уже в семидесятых и все последующие языки были лишь комбинациями предыдущих. На древних машинах никакого "программирования" вообще не существовало - грохочущий агрегат проектировался под конкретную задачу и ничего другого попросту не умел. ЭВМ следующего поколения управлялись при помощи разъемов и перемычек, прямым наследником которых являются современные ПЛИС'ки (Программируемая Логическая Интегральная Схема). Программное обеспечение еще не было отделено от аппаратного и программисту приходилось совмещать в себе навыки радиоинженера с мастерством математика. Изобретение перфокарточного ввода, а вместе с ним и машинного кода произвело настоящую революцию. Теперь уже можно было говорить о нотации - способе выражения программисткой мысли в некоторой транспортабельной форме, с помощью которой можно было читать, размножать, передавать по наследству и т.д. В общем, возникала письменность.

Правда, работать с машинным кодом было ужасно неудобно. И вовсе не потому, что восьмеричные коды команд с трудом удерживались в голове (настоящие программисты помнили их все). Машинный код всячески препятствовал модификации программы. Команды загрузки данных, условные и безусловные переходы требовали "жесткого" (hard coded) адреса, "связывающего" программиста по рукам и ногам. Если требовалось вставить несколько инструкцией в середину программы, приходилось корректировать ссылки на все последующие адреса или цеплять так называемую "соплю" - дописывать несколько команд в конец программы, делать на них jump, а затем возврат на предыдущее место выполнение. Многие программы на 90% состояли из "соплей" и разобраться с ними было очень непросто. Появление ассемблера решило эту проблему, хотя и было встречено в штыки старым поколением программистов.

Забытые идеи или динозавры в исторической ретроспективе

Ассемблер существенно упрощал программирование за счет меньшей свободы и эффективности исполнения программ. В частности самомодифицирующейся код на ассемблере невозможен, то есть возможен, конечно, но отнюдь не языковыми средствами. Самомодификация на ассемблере - грязный хак, а в машинном коде это было нормой. Ассемблер в некоторой степени абстрагирует от конкретной архитектуры, однако программы по-прежнему страдали непереносимостью, а их разработка требовала большого самообладания. Вот так, например, выглядела программа "hello, world!" для PDP-10:

;; Hello world in MIDAS

title   hello

start:  .open   [.uao,,'tty ? 0 ? 0]
        .lose   %lsfil
        move    1, [440700,,[asciz "Hello, world"]]
loop:   ildb    2,1
        skipn   2
        .logou  1,
        .iot    2
        jrst    loop
end     start

Листинг 1. Программа, "hello, world", написанная на ассемблере для PDP-10.

Пока программы были невелики по размеру, с этим приходилось мириться, но в какой-то момент программировать на ассемблере стало просто невыносимо и тогда появился первый язык высокого уровня - Фортран, научивший компьютер человеческой форме "разговора". Большинство рутинных операций и технических деталей язык брал на себя, позволяя программисту сосредоточиться непосредственно на самой решаемой задаче. Программа "hello, world", написанная на Фортране, выглядела так:

*
C Hello World in Fortran 77
C (lines must be 6 characters indented)
*
      PROGRAM HELLOW
      WRITE(UNIT=*, FMT=*) 'Hello World'
      END

Листинг 2. Программа "hello, world", написанная на языке Фортран.

Вслед за Фортраном появилась целая серия подражателей - Алгол, PL/I, Си... им даже название придумали - императивные. Программа, записанная на императивном языке, в грубом приближении представляет последовательность инструкций, передаваемую компьютеру на выполнение. К императивным относятся практически все современные языки. Остальным завоевать популярность так и не удалось. Но они были!

Вот, например, язык логического программирования Пролог. Это неимперативный язык, в котором программа представляет собой совокупность утверждений - таких, например, как: sin(x) = 0.69; print(x). Вот так, например, на Прологе вычисляется факториал.

fak(0,1).
fak(N,R) :-  N > 0,  M is N - 1,  fak(M,R1),  R is N * R1.

?- fak(6,N).

Листинг 3. Вычисление факториала на Прологе.

Сморите - сначала программист говорит: fak(M, R1), затем определяет R1 как "R1 это N * R1". Все! Логическое соотношение между R и R1 задано. Обо всем остальном позаботится транслятор. Ну, разве не красота?

Функциональные языки. О! Это совсем другой зверь. Вместо последовательности инструкций типа: "отрой кран, наполни чайник водой..." они говорят, что есть такая штука как "кран" и есть такая штука как "чайник". Что произойдет при встрече чайника с краном - решает сам транслятор. Программист лишь описывает "характеры" чайника с краном. Функциональные языки либо совсем не содержат переменных (а, значит, и оператора присвоения), либо не очень-то поощряют его использование. Тем не менее, программировать на них вполне возможно:

sub fac 1,
  if == 1 @[0]
    1,
  ( * @[0] &fac -- @[0] ),

Листинг 4. Вычисление факториала на языке LIRL.

Отсутствие переменных предполагает отсутствие типов данных. LISP - язык, ориентированный на работу со списками. Это бестиповой язык, в каноническом варианте которого нет ничего, кроме списков. Даже чисел нет. Число n представляется списком из n элементов. Звучит пугающее, не правда ли? На самом деле, от чисел и переменных полностью никто не отказывается (в противном случае это чрезвычайно снизит эффективность) и типичная LISP-программа выглядит так:

(defun fact (n)
  (loop for i from 2 to n
        for fact = 1 then (* fact i)
        finally (return fact)))
(format t "Factorial of 6 is: ~A~%" (fact 6))

Листинг 5. Вычисление факториала на LISP'е.

Объектно-ориентированные языки (а это практически все современные языки) наоборот - исповедуют диаметрально противоположный подход, навязывая жесткую типизацию даже там, где она на хрен не нужна и только мешает. Каждый Windows-программист знает, что такое кастинг (явное преобразование типов данных) и как он раздражает.

Короче говоря - за что боролись, на то и напоролись. Языки в процессе своей эволюции выродились в настоящих монстров, ужасающих своей сложностью. Насколько же проще было выучить пару сотен машинных команд, чем освоить Си++! Вместо того, чтобы заниматься решением задачи, программист вынужден воевать с транслятором, пытаясь заставить его сделать то, что он должен.

Как все начиналось? Программисту предоставляется набор хорошо продуманных примитивов, из которых, как из кирпичиков, и собирается программа. Сейчас же кирпичики сменились готовыми бетонными блоками, к каждому из которых прилагается увесистая спецификация. Программист больше не возводит стены, не устанавливает окна, унитаз и двери - он листает спецификации в поисках модулей, которые лучше всех должны подойти. Программирование медленно, но неуклонно выражается в проектирование. Это в императивных языках можно приступать к программированию, даже не имея никакого проекта, со смутными представлениями в голове. Жестких иерархий нет. Исходный код гибок и подвижен. Отрезали отсюда, приклеили сюда. Объективно-ориентированный подход заковывает программу в неподвижный скелет. Неудачно спроектированную систему классов на поздних стадиях разработки уже не исправить и ее придется тащить за собой как асфальтовый каток.

Хуже всего, что языки становятся неотделимыми от среды разработки и библиотек. Можно ли представить себе Си++ без RTL? Что же тогда от него останется?! Не будут работать ни исключения, ни оператор new. А какой же без этого ООП? То же самое и со средой. В Microsoft Visual C++ еще можно отказаться от мастеров, но ряд языков (DELPHI, Флора) без нее уже немыслим. Чем плохи мастера? Претензий к ним много, вот только несколько из них: мастера генерируют ужасно запутанный код. Лучше удавиться, чем в таком ковыряться (а без ковыряний дело все равно не обходится, особенно когда в программе требуется что-то доработать или подкрутить). Мастера провоцируют на дурной стиль программирования, перемешивающий интерфейс с его реализацией. Мастера делают процесс обучения непоследовательным, нелинейным и трудновоспроизводимым. Вместо исходного текста программы, который можно начертать и прочитать, новичку предлагается последовательность нажатий клавиш, каждая из которых бессмысленна сама по себе...

В общем, языки программирования вырождаются в жестикуляцию. Но по другому сегодня уже нельзя. Возросший оверхид (от английского "overhead" - накладные расходы) в прямом смысле слова раздувает муху до размеров слона. Представляет интерес проследить эволюцию программы "hello, world!" (http://www.sources.ru/news/20010609.shtml). Насколько же проще было программировать на ассемблере под PDP-10, чем на современных "высокоуровневых" языках под OLE/ActiveX! Что ждет нас в будущем? Бесконечный мусор многолетних наслоений или необычайно удобный и элегантный язык?

Прогнозировать трудно. Языки всегда были консервативны. Мы до сих пор находимся под влиянием синтаксиса Си и парадигмы объектно-ориентированного программирования. Язык, каким бы замечательным он ни был, находится под властью требований обратной совместимости миллионов исходных строк, которые никто не будет переписывать с нуля. Необходима обширная инфраструктура - компиляторы/интерпретаторы, библиотеки, отладочные средства, учебные пособия... Взять хотя бы Haskell, магическое очарование которого влюбляет в себя с первого взгляда. Многие были бы не прочь писать на нем и только на нем, но... "Я пробовал изучать Haskell, и был впечатлен его элегантностью, и тем, как он позволяет мне писать код, который работает с первой попытки (или со второй). Однако я не исследователь. Я занимаюсь коммерческой разработкой программного обеспечения и мне требуется документация и стабильность" - Alexander Jacobson.

fac n
  | n == 0    = 1
  | otherwise = n * fac (n-1)

Листинг 6. Вычисление факториала на языке Haskell.

Дележ неубитого медведя или мысли о новом языке

Чего нам не хватает в приплюснутом Си и даже в прочих языках? Простоты. Именно той простоты и элегантности, на которой сделали карьеру UNIX и классический Си. Кстати, UNIX до сих пор остается простой и дружелюбно настроенной к программисту средой, предлагающей элегантный интерфейс с минимальным оверхидом. Свой первый драйвер под Windows я писал несколько дней, и всю последующую неделю ходил как пришибленный с чумной голой, а под Linux загружаемый модуль ядра я написал через несколько минут(!) после того, как открыл документацию. "Как просто... Невероятно... Так не бывает..." - носилось у меня в голове.

Гениальность создателей UNIX'а в том, что они предоставили унифицированный интерфейс ко всем компонентам системы. Одна и так же функция открывает и файл, и устройство, и даже оперативную память! Microsoft же на все заводит свой API. Отсюда и неподъемная сложность. Библиотеки (типа MFC) ничего не меняют. Вместо того, чтобы учить API операционной системы, мы вынуждены учить и API, и разлапистую иерархию библиотек. Тот же самый пресловутый ActiveX/OLE можно было сделать намного, намного проще, если подойти к делу с головой.

Итак, новый язык прежде всего должен быть прост. Платформа .NET на эту роль явно не тянет. Да она, похоже, на нее и не претендует. Это узкоспециализированный набор языков, ориентированный на базы данных и клиент-серверные приложения, но ни словом, ни делом не поддерживающий ни векторное, ни параллельное программирование. А зря! Тактовая частота не резиновая и быстродействию процессоров очень скоро наступит предел. Многопроцессорные системы уже не за горами (достаточно вспомнить хотя бы тот же Hyper-Threading). Существует множество диалектов приплюснутого Си, поддерживающих параллельное программирование, существуют библиотеки, поддерживающие векторные команды (они же "мультимедийные"), но все это жалкие костыли. Неудобные, ни с чем не совместимые, да к тому же недостаточно эффектные. Это как морская свинка. Уже не морская, но еще не свинка. Векторно-параллельное программирование требует совсем другого подхода и типа мышления, а значит, и принципиально нового языка.

В новом языке (если такой вдруг появится) скорее всего будет встроен автоматический сборщик мусора (эта такая штука, которая освобождает неиспользуемую память) и поддержка динамических массивов с контролем границ (чтобы хакеры не могли переполнить буфер и заслать агрессивно настроенного червя). Стек, динамическая и глобальная память наверняка атрофируются, хорошо хоть сам термин "память" продолжит существовать. Программирование без переменных не только возможно, но и удобно! Синтаксис станет более свободным, демократичным, а контроль ошибок ужесточится до предела. В идеале все синтаксические ошибки транслятор будет исправлять сам. Наследие логических языков (типа Пролога) также найдет себе применение. Программист говорит, что он хочет, а компилятор/интерпретатор решает, как это реализовать.

Сколько это будет жрать ресурсов, лучше даже не представлять, а то ведь и свихнуться можно. Любые траты, направленные на упрощение программирования, по определению оправданы. Ведь для этого компьютер и создавался, чтобы служить человеку, а не наоборот. Трагедия в том, что по-настоящему удобный язык никогда не будет коммерчески успешным. Такой язык не требует ни полноводного потока учебной литературы, ни курсов повышения квалификации, ни армии консультантов... К тому же, язык программирования нельзя сесть и выдумать просто так. Настоящие языки возникают лишь естественным путем.

Заключение или пара пинков напоследок

Каким бы плохим/хорошим ни был приплюснутый Си, закидывать его камнями - это не хакерский путь. А почему бы не попробовать создать свой собственный язык? Запрограммировать на нем пару-тройку программ, показать друзьям, выложить в сеть... Не стоит ждать, пока кто-то это сделает за нас. Не стоит хвататься за уже существующие языки только потому, что работодатели их требуют. Работодатели у нас довольно демократичные и зачастую им самим приходится объяснить, что они требуют, а что нет. Конечно, подавляющее большинство отечественных фирм не могут позволить себе финансировать разработку нового языка и уж тем более не в состоянии продвинуть его на рынок, но... практически каждый второй студент вынашивает идею собственного языка и каждый десятый создает минимально работающий интерпретатор, а в некоторых случаях и компилятор, в процессе написания которого решаются сложные технические задачи и рождаются интересные идеи. Может быть, прямо здесь, в этот день, в этот час, в эту секунду кто-то пишет язык, которому предстоит стать основным языком программирования на последующие полсотни лет.

Классификация языков программирования

Интересные ссылки