Juraj Kircheim. What the Haxe!

В своем докладе Юрай в легкой форме рассказал о том, что же такое Haxe, какова его история и какое применение он нашел в Docler Holding. По выражению Marcelo Serpa данное выступление можно назвать попыткой объяснить Haxe пятилетнему ребенку. По-моему это очень удачное сравнение :)

Видео-версия доклада доступна на сайте Haxe.

Что же, черт побери, такое Haxe?

Это сложный вопрос. Haxe это не просто язык программирования, это своего рода единорог, уникальное существо. Звучит, конечно, слишком хорошо, чтобы быть правдой.

Но это язык, который может работать где-угодно. Его можно скомпилировать в JavaScript, C++, PHP, Python, Lua, C, C#, Java, HashLink, Neko VM (эти две виртуальные машины предназначены в основном для использования при разработке под Haxe) и Flash (который скоро уйдет в небытие).

А также он может всё: объектно-ориентированное программирование, функциональное программирование, языково-ориентированное программирование, аспектно-ориентированное программирование. В его системе типов столько средств, что мало кто сможет объяснить их все.

И самая любимая для Юрая вещь в Haxe - это макросы - система метапрограммирования (исполняемая во время компиляции программы).

Поэтому когда вы пытаетесь объяснить кому-то, что же такое Haxe, то вы становитесь похожим на этого персонажа:

Что ж, давайте разбираться дальше…

Первый вопрос, который может прийти в голову после такого описания Haxe: если он настолько хорош, то почему я не слышал о нем, почему никто им не пользуется?

Это хороший вопрос.

Для начала давайте посмотрим, кто же им пользуется:

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

TiVo - одна из первых крупных компаний, начавших использовать Haxe. Перевели миллион строк ActionScript-кода на Haxe, и в результате улучшили скорость работы своих приложений.

Разработчики игр, конечно же, составляют существенную часть Haxe-сообщества:

FlowPlay - еще одни безумцы, решившие портировать более миллиона строк ActionScript-кода на Haxe.

InnoGames - крупная немецкая компания, занимающаяся разработкой браузерных и мобильных онлайн-игр. Также используют Haxe для портирования игр на Flash, однако, построенный ими процесс сборки приложения позволил сохранить исходный ActionScript-код, который автоматически преобразуется в Haxe и затем компилируется в JavaScript. Об этом процессе также был доклад на Haxe Summit 2017 в Амстердаме.

Motion Twin - компания, в которой родился Haxe. Созданная ими игра Dead Cells (написана, конечно же, на Haxe) получила награду как лучшая action-игра на The Game Awards 2018.

Shiro Games - компания Nicolas Cannasse - создателя Haxe.

Proletariat Games - компания, использующая совместно Haxe и Unreal Engine. Такое сочетание технологий позволило им привнести в Unreal функцию “live code reloading” (возможность редактировать код программы и видеть изменения тут же в запущенном клиенте без полной перекомпиляции и перезапуска). И это крутая штука, т.к. если вы работаете с Unreal, то вы либо пишете код на C++, и тогда должны смириться с длительным временем компиляции проекта и с не очень информативными сообщениями об ошибках компиляции, либо вы используете блюпринты, которые хотя и удобны для непрограммистов, но если вам нужен по-настоящему быстрый код, то они вам с этим не помогут.

Этот список игровых разработчиков можно продолжать и продолжать, но я ограничусь только перечисленными.

Хотя нет, есть еще один известный разработчик:

Ок, Haxe все-таки используется, но откуда он появился, почему его применение сильно ограничено, что в итоге с ним произойдет?

Иногда для понимания вещей бывает полезно вернуться к их истокам. Давайте и мы вернемся в прошлое:

15 лет назад (вечность по меркам интернета) было довольно дикое время:

Тогда актуальными были:

  • Netscape еще жив, iCab был дефолтным браузером для Мас, существовал Internet Explorer для Mac!
  • PHP4 с его опцией register_globals. Если вы не знаете, что это такое, то лучше не читайте об этом, если вам дорого ваше душевное здоровье.
  • у нас были Java-апплеты и множество разных плагинов (RealPlayer, VLC и многие другие)

А впереди нас ждали:

  • практически все фреймворки для бэкенда
  • то же самое касается и фреймворков для фронтенда. GWT (Google Web Toolkit) и Silverlight (замена Flash от Microsoft) тогда еще не существовали, а сегодня они уже мертвы
  • Даже JSON тогда не было. Его спецификация появилась только в 2006.
  • Facebook, Youtube, Twitter
  • Mac’и с процессорами от Intel, дистрибутивы Linux, которыми могли бы пользоваться обычные смертные, смартфоны, мобильный интернет
  • Не было Node.js, не было менеджеров пакетов для JS, ничего. И, конечно же, Safari и Chrome тоже тогда не было.

Это были темные века!

И примерно в это время появилась идея RIA - Rich Internet Applications:

В общем случае RIA включали в себя:

  • веб-сервер, со своим кодом на PHP или любом другом существовавшем тогда языке для написания серверов
  • Клиент, написанный на:
    • DHTML - Dynamic HTML - просто связка HTML и JavaScript
    • Flash (или другой плагин для браузера)

И самое крутое в этом было то, что RIA по функциям можно было сравнить с полноценными настольными приложениями, но работали они на всех платформах, так как FlashPlayer работал на 99% компьютеров. Для тех времен это было великолепно.

Но разработка такого приложения означала, что вам придется столкнуться с такими проблемами как:

  • совместимость браузеров (тогда она была просто адовой)
  • я уже говорил, что для бэкенда не было фреймворков. Так что приходилось писать все с нуля
  • приходилось писать на трех разных языках. Совсем не весело.
  • даже передача данных между тремя разными платформами была непростой задачей. Так как JSON тогда не существовал, приходилось использовать либо XML, либо CSV, либо придумывать свой формат данных. Да уж.

Примерно в это время Motion Twin начали работать над ActionScriptMetaLanguage, который представлял собой ActionScript с типами (да, тогда в ActionScript не было типов). Затем этот проект разделился на два отдельных: MTASC - первый компилятор ActionScript 2 с открытым исходным кодом (в ActionScript 2 появилась-таки поддержка типов) и MotionType - язык, который использовался в Motion Twin. MotionType мог компилироваться под NekoVM для работы на серверах, его основной идеей была возможность использования одного кода и на сервере и на клиенте.

И в 2006 году из этого родился haXe - один язык, который мог бы работать и на сервере (NekoVM) и на клиенте (DHTML и Flash) и позволял бы легко передавать данные между платформами (например, из Neko в JavaScript).

Это то, что в 2010 году получило название Full Stack Development!

И увидев это, вы бы предположили, что все бы побежали использовать haXe в работе:

Но все пошло не так:

Большинство решило остаться в каменном веке.

Одна из причин, почему так произошло - это то, что людям тяжело изменять своим привычкам.

Но, конечно же, это не единственная причина непопулярности Haxe:

  • у Haxe нет поддержки со стороны крупных корпораций
  • изначально Haxe был проектом, созданным одним человеком
  • у него отсутствовала документация
  • отсутствовали инструменты для разработки
  • сама идея Haxe была слишком сумасшедшей (наверное, люди не были к ней готовы :)
  • и вообще работа с ним была спартанской

Но есть и хорошие новости, кое-что изменилось:

  • появился Haxe Foundation - организация, занимающаяся сбором средств и финансированием работы команды
  • теперь за развитие Haxe отвечает команда. И вот один интересный факт: если считать за члена команды программиста, сделавшего 100 и более коммитов, то команда Haxe насчитывает 14 человек (а, например, команда React - 12 человек)
  • у нас теперь есть руководство по языку и сборник рецептов
  • поддержка языка со стороны IDE стала гораздо лучше. Haxe Foundation вкладывает основные усилия в поддержку плагина для VSCode - vshaxe
  • но идеи Haxe все еще безумны
  • и работа такая же спартанская

И, наверное, это даже неплохо.

Но вы можете задаться вопросом: в 2019 году уже есть язык, который работает везде - JavaScript, какой тогда смысл использовать Haxe?

И по-моему ответ на этот вопрос состоит из двух частей:

  • первая, о которой мало кто беспокоится (по крайней мере мне так кажется), это то, что Haxe - качественный язык с развитой системой типов, системой макросов, быстрым компилятором (по крайней мере если сравнивать его с TypeScript), и он прошел испытание временем (этот язык с нами с каменного века интернета)
  • и вторая часть - это то, что Haxe генерирует по-настоящему нативный код. В то время как другие более популярные проекты не могут таким похвастаться. Например, Airbnb в итоге отказалась от использования React Native для мобильной разработки.

В качестве проекта на Haxe, демонстрирующего возможности Haxe, можно привести HaxeUI. С ней один и тот же код для пользовательского интерфейса можно скомпилировать практически под любую платформу, кроме iOS (но и эта платформа может быть охвачена).

Один и тот же код, скомпилированный под Linux (Ubuntu), MacOS и Windows:

Также проект с HaxeUI можно скомпилировать и под Android:

И на выходе вы получите нативный Java-код, а не JavaScript, который бы выполнялся в отдельной среде и взаимодействовал с системой с помощью дополнительной абстракции, как в случае с React Native.

Есть даже возможность собрать проект с использованием библиотеки PDCurses:

Так что Haxe действительно работает везде.

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

У нас был критически важный продукт, который использовал Flash. В какой-то момент было решено переписать его на ActionScript 3. И мы словили синдром второй системы - все хотели улучшить продукт, добавить в него новых функций. Но нам повезло, так как Flash отлично справился и с этим чрезмерным усложнением системы.

Затем Apple объявила смерть Flash, что повлекло за собой решение портировать продукт на Haxe (с использованием as3hx).

И результат портирования оказался совсем не так хорош, как мы ожидали. Спустя примерно год работы мы получили один большой JavaScript файл, код работал ужасно медленно и содержал множество ошибок.

Последовавшая реакция в компании была примерно следующая:

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

Мы совершили множество ошибок:

  • мы использовали огромную базу унаследованного кода
  • не использовали сильные стороны Haxe, ограничившись небольшим подмножеством его возможностей (можно сказать, что писали ActionScript-код на Haxe)
  • использовали сложные и громоздкие конструкции даже для простых вещей. Например, обычная кнопка реализовывалась с помощью шести разных классов
  • мы не рассматривали наш продукт как веб-приложение. Изначально он состоял из двух отдельных частей на Flash и JavaScript, и эта составная природа обуславливала его дополнительную сложность.

Где-то в это время Юрай пришел работать в Docler. В ходе обсуждений сложившейся ситуации был составлен список вещей, которые могли бы помочь решить имеющиеся проблемы:

  • использование системы шаблонов вместо querySelector и innerHTML
  • использование связывания данных (data binding) для упрощения автоматического обновления отображения при изменении данных
  • использование более простого механизма для композиции приложения вместо механизма внедрения зависимостей (Dependency Injection), который не всегда предсказуемо вел себя

План действий был следующий:

  • создание слоя данных, на изменение которых можно было бы подписаться
  • однонаправленный поток данных с предсказуемым и понятным поведением логики
  • использование описательной разметки для UI, который позволил бы избавиться от необходимости вручную изменять состояние UI

То есть нам нужна была связка React и MobX?

Когда вы только приступаете к разработке нового приложения, то начать использовать React и MobX довольно просто. Однако у нас на руках была огромная кодовая база со своей логикой поведения, и использование React и MobX, которые навязывают свое поведение, нам не подходило.

Реализация принятого плана и требуемых технических решений:

  • был создан своего рода аналог JSX для Haxe - HXX, который может создавать DOM и vDOM элементы, которые в свою очередь отрисовываются с помощью библиотеки virtual-dom
  • реализованы свои Observables, более легковесные и простые по сравнению с их аналогом из React
  • создан Coconut - фреймворк для управления состояниями и компонентами UI

И, конечно же, я ожидал, что команда сразу подхватит мои разработки:

Но многие все-таки предпочли работать по-старому (по многим причинам):

Но потом наша команда постепенно начала использовать эти наработки:

Хотя это был непростой процесс.

На графике представлена динамика этого процесса с августа 2017 года по настоящее время. Размер скомпилированного и минифицированного приложения уменьшился с 4 Мб до примерно 1,2 Мб.

Каждый скачок вверх на графике означает добавление новых фич, а каждое падение - наведение порядка в коде.

А красная линия на графике показывает снижение числа строчек кода в проекте - практически в 2 раза. И это при том, что новые фичи продолжают добавляться в проект. Что довольно круто.

Но наша компания все же переходит на использование React. И мне пришлось написать специальный рендер для Coconut, который связывает компоненты Coconut и React и позволяет использовать функции React в Coconut.

И он работает, по крайней мере наш QA-отдел говорит так.

Этот переход на React не потребовал больших изменений в проекте - всего около 400 строк кода (из 55 тысяч строк всего проекта).

Релиз планируется примерно в марте.

И наш опыт можно рассматривать как демонстрацию возможностей Haxe.

И еще хотелось бы кратко коснуться такой сильной стороны Haxe - это то, что можно “научить” компилятор понимать новые концепции, и пользуясь этими концепциями упростить такие вещи, как, например, наш переход на React, или переход с React на Vue и т.д.

Простой пример такого “обучения” компилятора - библиотека, которая встраивает CSS-семантику в Haxe. При этом для работы автодополнения никаких изменений в работу IDE не нужно делать: мы “учим” компилятор, компилятор предоставляет функции автодополнения для IDE, компилятор также валидирует новый код. Например, если вы сделаете опечатку и вместо элемента form запросите from, то компилятор выдаст ошибку о том, что from - не является известным тегом:

И если сравнивать Haxe c TypeScript, то в некоторых случаях система типов TypeScript более выразительная, но компилятор TypeScript нельзя научить таким новым трюкам, заставить его контролировать формат введенной строки.

И это не все, на что способен компилятор Haxe, и мы сможем “научить” его новым вещам и, таким образом, упростить себе жизнь в дальнейшем.

В Docler любят Haxe и хотят разделить свою любовь со всеми. Именно поэтому было организовано это мероприятие, поэтому Docler стал стратегическим партнером Haxe Foundation и поэтому в 2017 году был организован Haxe Summit в Амстердаме.

Партнерство Haxe и Docler взаимовыгодное, и я надеюсь, что работа с Haxe принесет выгоду и вам!