Привет из мезозоя

Habrahabr 9

image

Парадный портрет автора, заодно иллюстрирующий идею современной веб-разработки

Сразу честно признаюсь: я существо отсталое. Ну чтобы потом меня пальцами на этот счет не тыкали. Программировать я начал чуть позже изобретения палки-копалки, но намного раньше постройки пирамид — в общем, когда еще птеродактили по небу летали.

Несмотря на некоторые недостатки — в виде ревматизма и отсиженного перед монитором зада — такой опыт дает и отдельные преимущества. Можно уютно расположившись в теплом болоте с умилением смотреть, как перед глазами мельтешат молодые и прогрессивные, неистово изобретая очередной велосипед с квадратными колесами.

Я с интересом ознакомился с эпохальной статьей "Объясняем современный JavaScript динозавру", где мне на пальцах разъясняют, почему горы мусорного кода крайне необходимы, а мазохизм полезен и приятен. Ничего нового оттуда я, впрочем, не узнал, поскольку сам занимаюсь веб-разработкой, и все это давно испытал на своей шкуре — ну кроме приятной части.

Да, я уже давно слежу за гуру веб-программирования, как они поставили посреди площади огромный железный котел и варят в нем лапшу с макаронами, бросая туда для вкуса всё, что найдут на чужих огородах, — генерацию кода, обрывки функциональщины, ручной учёт зависимостей, статическую проверку типов, реактивные потоки и бог весть что ещё. Особенно мне нравится полноценная компиляция — со всем полагающимся лексическим и синтаксическим разбором — Яваскрипта в Яваскрипт же. Ребята, вы серьезно?

Каждый год часть лапши разбухает и уходит на дно. Много там уже лежит такого, что в приличном яваскриптовском обществе сейчас и упомянуть стыдно — Бэкбоны там разные, Ангуляры, не говорю уже о Jquery или там библиотеке Prototype. Зато сверху тут же взбухает новая пена, и тогда вокруг котла радостно танцуют и поют, как наконец-то на этот раз все проблемы решены.

Но я не только смотрю, я еще периодически из этого кипящего чана пробую — много лет пробую — и каждый раз обнаруживаю, что результат по-прежнему несъедобен. Мутно, сложно, некрасиво и при этом… примитивно. Горы хлама, монбланы оверинжиниринга ("горе от ума"), число используемых языков уже приближается к десятку (если считать разные версии JS и языки конфигов различных припарок), время сборки всего этого добра растет экспоненциально… И что же в результате?

Может, компилятор мегаязыка, который сам напишет программу по телепатической постановке задачи? Расчет необходимых и достаточных условий для счастья человечества? Моделирование эволюции с кнопками Step и Step over? Отказоустойчивый код запуска реактора ядерного звездолёта? Универсальная кнопка "Пыщь — сделать красиво"?

Да нет, дневничок "To Do" с тремя кнопками, не дотягивающий даже до своего бумажного дедушки.

Мой мезозойский опыт давно научил меня: если гора кода рождает мышь, значит, с ней что-то не то. Если в проекте задействована генерация кода как текста — ищи подвох. Если для нормальной работы нужно вызывать пяток-другой костылей — разного рода учётчиков зависимостей и преобразователей — дело пахнет керосином.

Всё вышеупомянутые признаки говорят об одном: решение не вытянуло сложность задачи и оказалось под ней погребено. В программировании — обычное дело, потому что вся эта сфера деятельности — суть непрерывная борьба со сложностью. И оружие прекрасно известно — повышение уровня абстракции используемых инструментов.

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

Но вы не можете написать что-то вроде

application ToDo
{
  use grid;
  layout "todo";
  init { ...}
}

и обойтись при этом без всяких подстановщиков кода и систем сборки, как это делали в девяносто лохматом году счастливые программисты на ТурбоПаскале. Почему?

На то, как писали в советских газетах, есть целый ряд объективных и субъективных причин. И самая главная среди них одна — ваша среда поддерживает чёрт знает что, включая классы слабых бантиков сбоку. Всё, кроме того, для чего она предназначена, — браузерных интерфейсов.

Давайте посмотрим на ситуацию сверху — с полета археоптерикса, так сказать. Это ведь несложно, достаточно всего лишь вынырнуть из смоляной ямы, именуемой "современным веб-программированием". Трудно оценить проблему со стороны, когда ты сидишь в ней по уши настолько давно, что другой жизни уже и не представляешь.

Для чего, собственно, используется браузер? Для двух не очень-то пересекающихся вещей.

Первая — это представление статической информации. Ровно то, для чего он и задумывался исходно сэром Бернерсом-Ли. За двадцать лет на этом пути достигнуты невиданные успехи — ценой всего лишь двух совершенно не похожих друг на друга языков (один берет начало в древних форматировщиках текстов, а другой — из Лиспа) и долгой и упорной борьбы с кривыми браузерами, мы можем наслаждаться не только унылыми научными статьями, которые хотел нам впарить вышеупомянутый сэр, но также картинными галереями, фанфиками, портретами кошечек и тоннами рекламы. Нельзя сказать, правда, что всё идеально — верстка даже полностью статических приложений до сих пор не столько ремесло, сколько шаманство, пусть и низкооплачиваемое. И никуда не делась проблема, тяжёлая, как чёрная дыра, — разное разрешение экранов, которое в общем случае делает невозможным навести красоту, как это делают, например, в бумажных изданиях. Красота — это ведь прежде всего правильные пропорции. Текст растянуть и переверстать автоматически можно, картинки, если они векторные, — растягиваются, но золотые сечения между ними машине, увы, недоступны. И нет никакой надежды — разве что на специализированный искусственный интеллект с повышенными отметками по шкале эстетизма.

А вторая вещь — это разработка приложений. Те самые интерактивные программы, взаимодействующие с пользователем. Я даже уточню — 90% этих самых приложений на самом-то деле ничего не вычисляют, не лазят в базы и не ищут простые числа на ходу. Они показывают всего лишь интерфейс.

В мире веб-программирования считается хорошим тоном блуждать в трех соснах, которые называются M, V и C. В результате этого саморасходящегося процесса смысл каждой из этих букв стал настолько неопределенным, что даже пророчества Нострадамуса стали на их фоне просто-таки эталоном ясности. Хотя всё проще простого: модель живет на сервере, вид — это то, что маячит перед пользователем на экране, а роль контроллера выполняет браузер, с одной стороны посылающий и принимающий запросы HTTP, а с другой — слушающий мышку и клавиатуру, и передающий их события в программу. Банально, не правда ли?

Программирование серверной части давно устоялось и не вызывает ни у кого затруднений. Браузер дан нам свыше.

Видите, мы уже избавились от двух букв. Осталась всего лишь одна — Вид.

Неужели отображение данных на экране для просмотра и редактирования — такая неслыханная и невиданная задача, что никто не знает, с какой стороны к ней даже подступиться? Я вас умоляю. Она решалась столько раз, что даже просто перечислить — заболит палец, жмущий на клавишу "запятая". Так, навскидку, то, о чем я слышал: терминалы IBM 3270, Смоллтолк, экранный Постскрипт, WinAPI, TurboVision, VisualBasic, dBase, Tcl/Tk, XUL, Rebol/View, XAML.

Отличается ли чем-то именно браузерное отображение от этой традиционной задачи? Да, отличается. Тем, что модель живёт где-то далеко и ее надо тягать по сети, откуда возникает латентность и необходимость что-то делать в случае ошибок. По сети же надо передавать обновления обратно и проверять, что они правильно пришли. Ещё что-то? Да нет, больше ничего.

Все перечисленные выше аббревиатуры, если глядеть на них абстрактно, состоят из трех больших частей:

а) системы интерфейсных элементов, именуемых в народе виджетами, причем в виджеты обязательно входят не только поля ввода и кнопочки, но и какие-то агрегирующие элементы, позволяющие группировать индивидуальные виджеты и оперировать с ними как с единым целым. В системах, которые строятся на базе ООП, имеется бонус: можно порождать собственные элементы на основе уже готовых. б) системы упаковки этих виджетов на экран — то ли специальными компонентами-упаковщиками (layout managers), то ли вручную — в редакторах форм. Впрочем, упаковщики и в последнем случае могут пригодиться, например, при смене разрешения экрана. в) некий язык, который придает всему этому хозяйству динамизм и вычислимость по Тьюрингу. Лучше, конечно, специализированный DSL, но и достаточно гибкий универсальный тоже сойдёт.

Есть ли в этом что-то сложное и непостижимое среднему уму? Отнюдь. Тяжело ли всё это написать? Ну, задание, конечно, не для студенческой курсовой: виджеты требуют кучу нудной работы, упаковщик — некоторой изощрённости, интерпретатор языка — знакомства с рекурсивным спуском, но в целом это всё-таки не квантовая механика и не ракетная техника.

Теперь давайте поглядим критическим взором, что же из перечисленного реально имеется в экосистеме браузер + Яваскрипт.

1) Взаимодействие модели с видом (то есть, сервера с браузером): частично. Есть HTTP-запросы и веб-сокеты, но всё приходится писать самими — кто во что горазд. Стандартные протоколы отсутствуют, не говоря уже о каких-то абстракциях на эту тему, включающих автоматическую проверку подтверждения при обновлении.

2) Набор виджетов. Фиксированный, встроенный в браузер, да ещё и не совсем совместимый между браузерами разных типов. Расширяется с большим скрипом — понадобилось больше десяти лет на то, чтобы дождаться поля ввода даты. О стандартных сортируемых таблицах до сих пор остается только вздыхать.

3) Агрегирующие виджеты. Практически отсутствуют, если не считать вездесущий <DIV>, который, собственно, не определяет ни верстки, ни поведения.

4) Создание виджетов на базе готовых, в стиле ООП. К сожалению, отсутствует. Вы не можете стандартно взять виджет и использовать его как прототип. Честно говоря, и виджета-то никакого нет, а есть только узел DOM, который за неимением лучшего играет роль интерфейсного элемента.

Собственно, все модные нынче библиотеки пытаются решить именно задачи 2-4), но увы, делают это кто в лес, кто по дрова. В результате имеем 100500 отдельных систем, каждая из которых предлагает лишь необходимый минимум, потому что силы разработчиков распылены на повторное переизобретение колес — причем нередко эллипсоидальных. И да — каждая выдумывает ещё один язык, разумеется, тоже несовместимый с другими. Нужно ведь больше языков.

Об определении новых тегов, чтобы кратко выразить новые контролы хотя бы в том, что есть — в HTML — разумеется, даже мечтать не приходится. Проект Polymer скорее мёртв, чем жив, во всяком случае для практической работы пока что не пригоден. Возможно, так во младенчестве и умрёт.

Вместо новых тегов — то есть, высокого уровня, разработчики браузеров нам предлагают подумать о теневом DOMе, то есть, о низком уровне. Я ничего не имеют против теневого DOMа, но я не хочу его видеть, как не желаю дома видеть канализационные трубы. Это подробности реализации, они не относятся к моей задаче.

5) Упаковщик. Его скорее нет, чем он есть. Его функции размазаны между двумя левыми языками — причем один из них предназначен для представления текстов (!), а второй — просто список атрибутов для графического дооформления элементов (ну там мигание включить или цвет поменять), функциональность которого к тому же дублируется в третьем языке (JS). Говорить о чем-то типа упаковщика можно лишь применительно к появившимся недавно флексам*. Всё, что было до того, — такое же извращение, как вёрстка таблицами или размещение декларативного языка прямо внутрь Яваскрипта. Смесь, так сказать, французского с нижегородским.

6) Визуальное проектирование интерфейсов стандартными инструментами — это вообще из области фэнтези с эльфами Гэндальфами. Сама идея может лишь вызвать в широких массах недоумение.

7) Язык… Ну Яваскрипт** — конечно, не самый плохой язык, люди и не на таких монстрах пишут. Но он, вообще-то, не специализированный — в нём нет ничего для решения той самой задачи: представления данных. Да и как универсальный тоже так себе. Никакой, если честно. Самый близкий его родственник — Lua, маленький скромный язычок для скриптования больших приложений. JS весьма минималистичный, потому не слишком выразительный, и вряд ли кто-нибудь о нём вообще знал бы, если бы его не засунули в самый популярный тип программ. В нём даже модулей до последнего времени не было и их приходилось имитировать с помощью совершенно посторонних средств.

Из интересных идей разве что прототипное наследование — но от него всё равно нет никакой практической пользы — см. п. 4.

Да, полно недостатков проектирования — но с другой стороны, чего вы хотели за пару недель? Поставленная задача была выполнена — сделать язык для деятелей искусства, чтобы те могли погонять баннеры по страничке, скопировав из книжки пару строчек кода. Скорей надо удивляться, как его вообще смогли растянуть до нынешних задач.

А теперь развитие языка и вовсе попало в руки комитета и проходит совершенно хаотично — вперемешку идут попытки исправить огрехи первоначального дизайна (результат — дублирующаяся и отличающаяся в неочевидных местах функциональность), добавить нечто теоретически полезное (генераторы, ни разу пока не смог их приспособить для реальных программ), ввести что-то вовсе непонятное вроде ослабевших множеств, и порадовать любимыми ключевыми словами перебежчиков с Явы, которые органически не могут в прототипы***.

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

И вот все эти "частично", "нет" и "нужно делать руками" по всей системе в результате как раз складываются в ту самую адскую трудоемкость, глючность, тонны памяти и непрерывный поиск святого Грааля, который каждый раз оказывается лишь одноразовым пластиковым стаканчиком.

Не исключено, что, дойдя до этого места, кое-кто уже обиделся и посылает в мою сторону лучи ненависти из большого гиперболоида. Дескать, что ты забыл в вебе? Не нравится тебе здесь — и вали в свой трубопаскаль.

Отвечаю. По сравнению с куда более мощными и тщательно разработанными десктопными интерфейсами у веба есть два мощных козыря: идеальная независимость от платформы и ненужность инсталляции приложений. Вся инфраструктура, необходимая для работы пользователя уже есть практически везде, и люди знают, как ей пользоваться. Это бесценно, и ради этого программисты вынуждены тратить свои драгоценные жизни, всё глубже закапываясь в шуршащие монбланы опадающих с экрана буковок и знаков препинания.

Да, понятно, что у веба было трудное детство, деревянные клавиатуры, борьба коммерческих миров, груз совместимости. Всему можно найти подобающее историческое оправдание — но заказчикам эти слёзы неинтересны. Они хотят, чтобы их приложение быстро писалось — не тот кусок, конечно, что формулу счастья ищет, — тут они согласны подождать. А вот тот, который всего лишь показывает уже посчитанную формулу. Чтобы он был без глюков и просто работал. Чтобы не жрал память и ёмкость сети. Чтобы не замораживал браузер и всю систему. Не выводил непонятных дурацких ошибок.

И в этих скромных требованиях они, чёрт возьми, совершенно правы.

Есть ли свет в конце туннеля? Теоретически да. Если, наконец, производители браузеров выкинут всё накопившееся старьё и напишут с нуля нормальную интерфейсную среду с нормальной иерархией виджетов и упаковщиком, договорятся об автоматическом обмене информацией с сервером, приделают к браузеру самую банальную виртуальную машину общего назначения, можно даже не особо быструю****, ну и в качестве вишенки сочинят стандартный DSL для построения интерфейсов, то веб-разработка станет тем, чем она и должна быть, — несложным ремеслом, и главную роль в ней будет играть не программист, а дизайнер UX. А программисты, наконец, займутся нейронными сетями, обработкой больших данных, расчётами холодного термояда, сочинением увлекательных игр и другими полезными вещами, о которых в старости не стыдно будет рассказать детям. "Я всю жизнь добивался, чтобы при нажатии на одну кнопочку другая меняла цвет" — это как-то всё-таки не то.

И, может быть, кто-то из тех детей когда-нибудь доживёт до той самой светлой поры, которая теоретически не может не быть. А мы уж ладно — будем терпеть и гордиться собой, что мы такие гениальные и творим дела, недоступные простым смертным. Ещё бы — ведь у нас каждый год появляется новая система упаковки множества мелких файлов в один большой-пребольшой.

Примечания

* Есть какая-то злая ирония в том, что соответствующая технология нормального упаковщика не то что придумана десять лет назад, но даже сделана, вставлена прямо в распространённый браузер и обкатана. Любой, у кого стоит Firefox, пользуется её плодами каждый день — потому что в нём на языке XUL полностью запрограммирован пользовательский интерфейс.

** Филологическое отступление. Язык Javascript был назван по аналогии с другим языком по имени Java. Этот язык получил своё название по имени сорта кофе, который выращивается на определённом острове. Остров этот по-русски называется Ява, а не Джава, соответственно, кофе на нём яванский, а языки именуются, следовательно, Ява и Яваскрипт. Между прочим, Яваскрипт первоначально хотели окрестить по имени другого сорта кофе "Mocha", который на русский переводится как "мокко". Боюсь подумать, как бы тогда называли новый язык любители тупо переписать латинские буквы кириллицей.

*** Прототипная модель очевидно шире классовой. Последняя имитируется на ней совершенно тривиально: заведите объекты, назовите их с большой буквы и порождайте экземпляры только от них. А вот из классовой прототипы никак не устроишь. Другое дело, что конкретно в Яваскрипте даже метод родителя вызывается с таким ужасным синтаксисом, что его и запомнить-то невозможно. Но это недостаток языка, а не модели.

**** Где же ты, WebAssembly ?