Haxe — “Unity” среди языков программирования

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

Haxe toolkit позиционируется как решение для кросс-платформенной разработки, которое основывается на:

  • современном высокоуровневом языке программирования со строгой типизацией;
  • легковесном и быстром компиляторе (кросс-компиляторе);
  • кросс-платформенной стандартной библиотеке и платформо зависимом API;
  • возможностях взаимодействия с конкретными платформами для написания (при необходимости) платформо-независимого кода;
  • утилитах для разработки и распространения кросс-платформенных программ (интеграция с IDE, Haxelib package system);
  • и, наконец, приличное количество уже разработанных не только для него, но и на нем, инструментов, фреймворков, приложений и утилит.

Более чем просто интригующее начало, не правда ли?) Попробуем рассмотреть основные аспекты Haxe toolkit более подробно. Начнем с небольшой исторической справки.

Примерно с 2005 года Nicolas Cannasse является автором Haxe компилятора и дизайнером языка программирования Haxe (тогда еще haXe). Т.е. на сегодняшний день языку, как и компилятору, ни много, ни мало 14 лет. Само собой, с тех пор язык сильно изменился. Изменился не только список языковых возможностей, но и список людей, разрабатывающих данный язык и все связанные с ним инструменты. Примерно в 2012 году был основан Haxe Foundation, и в это же время закончился процесс переноса всех прав на Haxe платформу от компании Motion-Twin (которой Nicolas Cannasse являлся соучредителем).

Примерно в 2016 году был создан Haxe Evolution — проект позволяющий влиять на дальнейшее развитие языка Haxe. Для этого нужно создать своего рода предложение (proposal) с описанием изменений (фактически нужно заполнить подготовленный шаблон предложения, ответив на все вопросы), плюсов, если эти изменения будут реализованы, возможных последствий для совместимости со старыми версиями Haxe, если таковые имеются, и возможными реализациями. После чего начнутся процессы обсуждения предложения и голосования. И, если голосание завершится положительно, то вы можете самостоятельно реализовать новую возможность или подождать, когда она будет реализована кем-то еще. В репозитории можно посмотреть как на уже принятые предложения, так и на предложения, находящиеся в процессе утверждения.

Кроссплатформенная разработка

Сравнивая в заголовке статьи Haxe с Unity, хотелось сразу показать одну из основных отличительных черт языка. Это — возможность компиляции и/или транскомпиляции под множество платформ. На момент написания статьи Haxe поддерживает 12 целевых платформ:

 

Название целевого ЯП Тип Статическая типизация2 Поддержка System API
Flash байт код Да Нет
JavaScript исходный код Нет Нет
ActionScript 3 исходный код Да Нет
PHP исходный код Нет Да
C++ исходный код Да Да
Java исходный код Да Да
C# исходный код Да Да
Python исходный код Нет Да
Lua исходный код Нет Да
PHP 7 исходный код Нет Да
Название целевой ВМ Тип Статическая типизация Поддержка System API
Neko байт код Нет Да
HashLink байт код + исходный код Да

Основные возможности языка

Чтобы начать знакомиться с Haxe, даже не нужно ничего устанавливать, т.к. можно воспользоваться онлайн компилятором на сайте Try Haxe. Синтаксис языка довольно прост. Если вы программировали на одном из C-подобных языков программирования (C/C++/C#, Java, ActionScript, JavaScript и т.п.), то Haxe в этом плане особых проблем не доставит. Довольно часто, чтобы не добавлять в язык новых возможностей, тем самым усложняя и перегружая его, добавляются мета-атрибуты (метаданные), выполняющие ту или иную функцию.

Рассмотрим основные конструкции языка.

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

Простые типы, такие как числа (Int, Float) или логический тип (Bool), не являются классами в Haxe, они реализованы с помощью абстрактных типов времени компиляции. Так же имеется в наличии специальный тип Void, который используется для выражения отсутствия значения или типа.

На динамических целевых платформах (JavaScript, PHP, Neko, Flash 6-8) переменные с простыми типами могут принимать значение null, в то время как на статических целевых платформах (Flash, C++, Java и C#) не могут. Но для таких случаев в Haxe имеется обертка в виде Null<T>, которая позволяет явно указать, что переменная (в том числе и простого типа) может принимать значение null.

 

Как и в большинстве других ООП языков, классы (и интерфейсы) в Haxe являются основными строительными блоками, и, в принципе, мало чем отличаются от классов, например, в Java или C#. Поддерживаются конструкторы, поля, сеттеры и геттеры, наследование и множественная имплементация интерфейсов и дженерики. Сюда же отнесем статические расширения.

Перечисляемый тип (Enum type) в Haxe представляет из себя алгебраический тип данных (своего рода обычных enum, но на стероидах). Например:

Переменная с типом Color может принимать одно из четырех значений: Red, Green, Blue или Rgb. Причем для последнего типа (и только для последнего) доступны дополнительные данные: цвет по каналам. К данным перечисления можно обратиться, используя сопоставление с образцом (pattern matching), о котором позже. Таким образом. с помощью Enum можно описывать составные типы сразу с необходимыми данными. И последний, но не менее важный момент: перечисления в Haxe могут быть рекурсивными; иными словами, содержать поля с типом текущего перечисления. Например:

Анонимные структуры — это тип, описывающий только набор полей объекта.

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

 

Расширения используются для того, чтобы показать, что структура имеет все поля заданного типа. Также можно указать дополнительные поля:

Благодаря структурной типизации и оператору расширения >  мы описали тип анонимной структуры, которая “унаследовала” все поля Iteratable, геттер WithLength и метод push. Далее, в переменной с этим типом мы можем присвоить ссылку на любой объект, соответствующий всем полям, свойствам и методам нашей структуры. И, само собой, работая с переменной temp, мы можем обратиться только к объявленным в структуре IteratableWithLengthAndPush полям, свойствам и методам.

 

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

Метод test имеет тип Int -> String -> Bool , (что значит метод принимает два параметра: число и строку) и возвращает значение логического типа. Тип Int -> (Int -> Void) -> Void  представляет из себя метод, принимающий число и другой метод, также принимающий число. Пример использования типа:

Несмотря на то, что Haxe является статически типизированным языком, он также при необходимости поддерживает и динамическую типизацию. Для этого существует тип Dynamic. Как динамическое значение может быть присвоено чему угодно, так и что угодно может быть присвоено переменной с динамическим типом. Динамическая типизация является продвинутой и опасной возможностью языка. Вступая на динамическую тропу, следует помнить:

  • компилятор не проверяет корректность динамических типов, и эта проблема ложится на плечи программиста;
  • часть оптимизаций кода при компиляции на статические целевые платформы отключается;
  • банальные опечатки в названии переменных, полей и методов+ теперь не могут быть найдены компилятором;
  • функция удаления мертвого кода не может автоматически определить используемые поля, если обращение к ним происходит через динамику.

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

 

Абстрактные типы представляют собой типы времени компиляции поверх run-time типов и ничего общего с абстрактными классами и методами из C#/Java не имеют. Другими словами, абстрактные типы существуют только во время компиляции, в run-time никакой информации о них не остается и они заменяются “подлежащим” run-time типом. Абстрактные типы позволяют создавать разного рода абстракции без накладных расходов (без объектов-оберток, что позволяет избежать лишних аллокаций). Абстрактные типы не только дают возможность писать краткий и простой платформозависимый код, но и позволяют довольно просто и эффективно реализовывать предметно-ориентированный код, причем без накладных расходов на тип, прозрачно с клиентской стороны и типобезопасно. Это одна из немногих возможностей в Haxe, которой мне реально не хватает в других языка.  

Пример абстракции типов градусов по цельсию и фаренгейту:

Два новых абстрактных типа: Celcius и Fahrenheit. Подлежащим типом для обоих абстрактов является Float. Оба типа имеют методы для преобразования Float значения в значение с типом одного из абстрактов и методы для преобразования из градусов цельсия в градусы фаренгейта и обратно.

Другим примером абстракта служит стандартный тип Uint, который нативно реализован только на платформах Flash и C#. Для остальных платформ он реализован в виде абстракта поверх типа Int. Несколько интересных примеров реализаций абстрактных типов можно посмотреть в книге рецептов. Не припомню, чтобы в каком-то еще современном языке программирования общего назначения была возможность наподобие Haxe-ый абстрактных типов, или память уже не та?

И последний основной вид типа в Haxe — мономорфы. Мономорфный тип — это тип, который может измениться (отсюда и название) в процессе проверки и вывода типов (Unification, Type inference). Явно из кода задать такой тип и как-то с ним провзаимодействовать нельзя. Поэтому мы не будем на нем останавливаться.

 

Интересные возможности языка

Экстерны

Экстерны — это возможность языка Haxe, позволяющая описывать взаимодействие с нативным (для целевой платформы) кодом в типобезопасной манере. В коде это выглядит как своего рода объявление интерфейса.

И далее мы можем использовать вышеописанный экстерн в коде так, как если бы это был просто класс:

Пример C++ экстерна SDL:

Стандартная библиотека Haxe идет вместе с большим количеством экстернов для Flash и JavaScript платформ. Для популярных библиотек и фреймворков есть готовые наборы экстернов, которые можно найти, например, на haxelib. Но даже если для какой-то библиотеки их нет, то довольно просто описать минимальный экстерн самому и дополнять его по мере необходимости. Стоит также отметить, что для платформ Flash, Java и C# Haxe позволяет автоматически генерировать экстерны. просто подключая нативные (для этих платформ) библиотеки к вашему проекту.

 

Сопоставление с образцом

Switch в Haxe (как и многое другое) — это не просто стандартный оператор из C, а нечто куда более мощное. В некоторых популярных языках (например в C#) сопоставление с образцом появилось совсем недавно. А в Haxe этот инструмент есть давным-давно, и работает он со всеми целевыми платформами. Если вам знаком термин «сопоставление с образцом» из какого-нибудь функционального языка, то это именно оно и есть. Сопоставление с образцом — это своего рода switch на стероидах, в котором каждый case описывает некоторый шаблон, который может проверить одно или несколько значений из составных объектов. Кроме непосредственно сопоставления, pattern matching в Haxe позволяет извлекать (захватывать) значения в локальные переменные для последующего использования в коде. Сопоставление с образцом является основным способом работы с алгебраическими типами данных (Haxe перечисления), но также работает с любыми другими объектами и даже массивами.

Ах да, switch в Haxe, как вы могли уже заметить, не требует break! Более подробно про сопоставление с образцом можно почитать здесь.

Метапрограммирование (макросы)

Макросы — это одна из тех возможностей, которой реально не хватает в других популярных языках. Не стоит путать Haxe макросы с C/C++ макросами, т.к. макросы в Haxe — это не просто текстовый препроцессор, а довольно мощная штука, позволяющая выполнять реальный код во время компиляции проекта. Иными словами, вы можете скачать файл по сети, добавить git хеш коммита в билд, провалидировать ассеты и т.д. Более того, Haxe макросы позволяют генерировать код, то есть модифицировать/генерировать/анализировать и валидировать код прямо во время компиляции. Потенциальные возможности становятся еще шире благодаря возможности обращаться к типам, выражениям языка и метаданным через API компилятора. Все вместе это открывает просто фантастические возможности, многие из которые просто нереально реализовать другими способами. В результате чего мы имеем более простой, компактный и безопасный исходный код и, как следствие, более компактный и быстрый сгенерированный код. Само собой, макросы и метапрограммирование — это не ноухау Haxe. Подобную возможность можно встретить и в других языках: Lisp, OCalm, D, Scala, Nim, Rust и другие.

 

Пожалуй самое частое использование макросов, из тех, что я видел это менеджмент ресурсов. Пример из движка Heaps:

Благодаря макросу мы можем работать с ресурсами в папке res в типобезопасной манере. На стороне макроса генерируется соответствующий код (с учетом типа ресурсов), а на стороне программиста мы можем работать с ресурсами как с обычным кодом. IDE будет подсказывать файлы в соответствующей директории (и поддиректориях), как если бы это был обычный код. Кроме того, если файл будет перемещен или удален, приложение не скомпилируется, т.е. упадет в build-time, а не как, в случае без макроса, в run-time. Таким образом макросы избавляют нас от работы со строками, в которых очень легко ошибиться. В принципе, если требуется, то можно расширить возможности такого макроса и добавить обработку ресурсов (скачивание, сжатие, упаковку в соответствующий формат и тд. и т.п), и все это будет работать в compile-time. Разве это не прекрасно?).

Макросы в Haxe действительно позволяют многое. Например, в версии Haxe 3 не поддерживается синтаксис коротких лямбд ( myList.filter(y => y > 2); ) и, благодаря макросам, это вполне решаемая задача (одна из возможных реализаций). Т.е. можно не только генерировать и выполнять код во время компиляции, но и вводить свой синтаксический сахар. Все, что для этого нужно, — это чтобы этап лексинга исходного кода прошел успешно (т.е. можно использовать уже существующие токены, а вводить новые — нет). Другой аналогичный пример — это попытка реализации сишного оператора for (да, в Haxe нет сишного for’а().

Еще одним примером применения макросов может служить реализация профайлера, реализация перегрузки операторовHaxe перегрузка операторов, к сожалению, из коробки доступна только астрактам). Или реализация биндинга данных, благодаря которой автоматически генерируются события изменения свойств класса (методы также поддерживаются). Или более сложный пример: реализация live coding’а для Haxe. Record-macros — это объектно-реляционно преобразующая библиотека на макросах, позволяющая работать таблицами в объектно-ориентированной манере без ручного написания sql запросов.  Еще несколько примеров есть в Haxe-cookbook.

Макросы — крайне мощная вещь, позволяющая автоматизировать большое количество различных задач и сделать исходный код лаконичнее, безопаснее и производительнее. Но у всего есть своя цена, в том числе и у макросов. Нужно отдавать себе отчет в том, что метапрограммирование в Haxe — это, видимо, самая продвинутая и сложная функция, и для ее эффективного использования нужно понимать, как компилятор работает с кодом. Чрезмерное злоупотребление макросами может усложнить кодовую базу и увеличить время сборки вашего приложения. Также из-за того, что это продвинутая фича, возможно, не все участники вашей команды смогут работать с ней, так как код макросов значительно сложнее всего остального кода на Haxe и для большинства, к сожалению, он сродни магии. Кроме того, макросы усложняют и замедляют работу автодополнения кода (Code completion/IntelliSense) в IDE. И хотя Haxe предоставляет сервисы для решения этой проблемы, но они все равно медленней стандартного автодополнения IDE, да и не все IDE, в полной мере, поддерживают эту возможность. Этот момент также стоит учитывать, а для кого-то он может быть критичным.

Оптимизация

Haxe генерирует код, насколько это возможно, близко к целевой платформе. Иными словами, код получается компактным и без всякого рода оберток и, как следствие, производительным. Чтобы достичь максимальной производительности, следует минимизировать использование рефлексии и динамической работы с типами в run-time. Благо, Haxe предоставляет мощные средства для переноса разного рода функционала на этап компиляции (например: абстрактные типы, метапрограммирование).

Благодаря тому, что Haxe имеет всю информацию о типах во время компиляции, это позволяет выполнять мощный статический анализ и оптимизацию исходного кода на этапе сборки. Само собой, это актуально только для строго типизированного кода, а злоупотребление динамикой идет лесом. Оптимизации, выполняемые компилятором совместно с JIT-анализаторами целевых платформ, не только позволяют получить быстродействие на уровне, но и небольшой размер приложения даже для “тяжелого” кода. Haxe поддерживает приличное количество оптимизаций:

  • Инлайнинг функций
  • Инлайнинг объектов
  • Удаление неиспользуемых полей и классов (dead code elimination)
  • Анализ и оптимизация выражений
    • const propagation
    • copy propagation
    • expression fusion
    • local dead code elimination
    • purity inference
    • code motion

Пример исходного кода:

Пример генерируемого кода для JS платформы:

Согласитесь, впечатляет?)

 

Haxe 4

На данный момент актуальной релизной версией Haxe является 3.4.7. Haxe 4 версии добрался до отметки release candidate. Учитывая, что с недавних пор над компилятором работает аж целых два full-time разрботчика (что для Haxe много, но значительно меньше, чем хотелось бы),  Haxe довольно скоро доберется до релиза. В принципе, многие уже сейчас используют Haxe 4, так как он довольно стабилен. Кроме кучи багфиксов, рефакторинга и мелких улучшений, Haxe 4 добавит к уже имеющемуся богатству функционала следующие возможности:

  • переработанный макро интерпретатор Eval, работающий до 4-х раз быстрее предыдущей реализации;
  • стрелочные функции (aka short lambda — arr.map(x -> x + 2));
  • ключевое слово final , которое можно использовать вместо var при объявлении полей класса и в анонимных структурах (final x = 0;);
  • новый синтаксис объявления типов функций, позволяющий писать более читаемый и более понятный код: (x: Int, y: String) -> Int ;
  • вместо ключевого слова  untyped  добавлены методы js.Syntax.code() ,  php.Syntax.code()  и аналогичные. Новые методы, в отличие от  untyped , типобезопасны и дружелюбны к анализу;
  • интеграция с VS Code. Новый протокол на основе JSON-RPC для сервисов подсказки кода. На основе этого протокола в VS Code реализованы следующие функции: контексто-зависимые подсказки, автоимпорт, рефакторинги, генерация описания структур функций, полей и т.д. В том числе — возможность находить ссылки на использование кода, а также разного рода диагностики: ошибки, неиспользуемые переменные/импорты и т.д.;
  • много мелких синтаксических улучшений;
  • поддержка Unicode на всех целевых платформах;
  • встраиваемая XML разметка (inline XML markup). Эта фича позволяет использовать xml разметку прямо в Haxe коде для своих нужд, например, для написания DSLs/React/ и т.п.. Примером может служить библиотека DomKit для работы с UI компонентами, где  xml разметка используется для описания разметки компонентов пользовательского интерфейса;
  • null-безопасность! (на данный момент с эксперементальным статусом);
  • поддержка генерации ES6 JavaScript кода;
  • сервер компиляции — кэширует код и ускоряет компиляцию и подсказки кода;
  • key value iteration ( for (key => value in map)  );
  • целая уйма улучшений, оптимизаций и багфиксов.
  • реализация async/await для всех платформ (разработка активно ведется, но релиз, скорее всего будет в Haxe 4+)

Следующая часть функционала удалена:

  • поддержка PHP5. Минимальная поддерживаемая версия теперь PHP7;

довольно много функционала стандартной библиотеки переехало в hx3compat;

 

Tooling

Пожалуй, самой продвинутой на данный момент IDE для Haxe разработки является FlashDevelop и ее брат-близнец HaxeDevelop (ребрендинг аля Adobe Flash и Adobe Animate). Эта IDE работает на семействе операционных систем Windows, начиная с Window XP. Также поддерживается работа на Mac OSX/Linux с помощью Wine/CrossOver. Есть все необходимое, начиная от подсказок кода и заканчивая разного рода рефакторингами, шаблонами, генераторами кода и отладкой кода.  FlashDevelop, как и Haxe, является решением с открытым кодом и довольно часто обновляется, хоть и по мелочи.

 

Вторым вариантом IDE является кроссплатформенный VSCode с плагином для разработки под Haxe. Также можно сразу установить целый набор расширений Haxe Extension Pack. Для комфортной работы с Lime/OpenFl можно установить Lime extension, а для Kha, соответственно, Kha Extension Pack. Лучше всего поддерживается Haxe версии 4.0.0-rc.2 и выше. Так же, как и во FlashDevelop, есть практически все необходимое, но не более того. Если вы до этого работали (или работаете) с VSCode, то, видимо, это ваш выбор. Плагин активно разрабатывается.

 

Другим кроссплатформенным решением является плагин Intellij-Haxe для среды разработки Intellij Idea. Поддерживаются как коммерческая версия IDE, так и версия для коммьюнити (с соответствующими ограничениями). В данный момент довольно активно ведется разработка плагина, но, по моим ощущениям, плагин по своему функционалу пока не дотягивает до FlashDevelop/VSCode. Но пользоваться можно, особенно если вы фанат IDE на платформе Intellij Idea.

 

Закрывается список IDE для разработки под Haxe в большинстве своем  блокнотами с поддержкой подсветки синтаксиса и элементарной подсказкой кода. Сюда относятся Haxe Bundle для Sublime Text, Vaxe Plugin для VIM, Haxe Plugin для Atom и Haxe Brackets Extension для Brackets. Более подробный список можно посмотреть здесь.

Для логирования в Haxe есть функция trace , которая, кроме собственно вывода сообщения, выводит дополнительную информацию о месте вызова (имя файла, имя класса и название метода), номере строки и пользовательскую информацию, если она задана. Это довольно удобно. Вывод можно переопределить, используя haxe.Log.trace . Также есть возможность полностью отключить все трейсы в приложении (так как будто их вообще не было) с помощью аргумента компилятора --no-traces . На JavaScript платформе есть возможность обратится к объекту консоли  js.Browser.console  и соответствующим функциям log/info/warn/error. Кроме этого, благодаря тому, что Haxe умеет генерировать мапы исходников, мы можем отлаживать Haxe код под JavaScript платформу прямо в браузере (поддерживаются три основные браузера: хром, фаерфокс и сафари). Например, с помощью функции  js.Lib.debug  мы можем добавить точку остановки в нужном месте в коде. Аналогично, Haxe умеет генерировать мапы исходного кода для PHP целевой платформы.

Полноценный дебаггер поддерживается для Flash платформы, и им можно пользоваться во FlashDevelop и Intellij Idea. С некоторыми шаманствами можно запустить дебаггер под C++ платформу в VSCode с помощью hxcpp-debugger и в Intellij Idea.

На остальных целевых платформах остается использовать стандартную для целевой платформы отладку, благо генерируемый код довольно читаемый, и отсутствие мап сильно не сказывается на качестве отладки.
Haxelib или Haxe library manager — пакетный менеджер для Haxe проектов. Своего рода nuget из мира .Net (только библиотеки распространяются в исходном коде) но только для Haxe-а. Позволяет находить необходимые библиотеки онлайн (по тегу, автору или популярности) и легко подключать к своим проектам. Само собой, можно распространять через него и свои библиотеки. Минимальный функционал, но все, что необходимо, есть.

Язык — ничто без наличия необходимого минимума библиотек и фреймворков. С этим у Haxe все в порядке. Если вам нужно портировать Flash приложение, то первое, что приходит на ум, — это OpenFL (Open Flash Library). Библиотека предоставляет API, практически на 100% совместимое с Flash, что позволяет практически безболезненно портировать вашу игру или приложение. Приложения и игры, написанные на OpenFL, работают под Windows, MacOs, Linux, iOS, Android, Flash (да флеш тоже поддерживается), AIR и HTML5. На данный момент существует две версии OpenFL. Одна — это Haxe версия, распространяемая через Haxelib; другая версия, распространяемая через NPM и спроектированная для использования с TypeScript, — JavaScript. Для еще более быстрого портирования существует as3hx, позволяющий перевести большую часть вашего ActionScript 3.0 кода на его Haxe эквивалент, что, несомненно, положительно скажется на скорости портирования.

 

OpenFL сама по себе написана поверх другой библиотеки: Lime. Lime это гибкая легковесная прослойка для кроссплатформенной разработки. Поддерживаются основные нативные целевые платформы, плюс Flash и HTML5. Lime предоставляет унифицированный API для работы с окнами, вводом, событиями, аудио, контекстами отрисовки, доступом к сети и API для работы с ассетами.

 

Другим популярным решением для работы с мультимедиа является ультрапортативный фреймворк Kha. Не зря я упомянул, что этот фреймворк является ультрапортативным, т.к. он поддерживает просто колоссальное количество целевых платформ. Посудите сами: HTML5 (WebGL 2, WebGL и канвас), Windows (Direct3D 12, Direct3D 11, Direct3D 9, Vulkan и OpenGL), UWP (Direct3D 12, Direct3D 11), MacOS (Metal и OpenGL), Linux (Vulkan или OpenGL), Android (C++ или Java), iOS (Metal или OpenGL), tvOS, Raspberry Pi, Playstation 4, Xbox One, Nintendo Switch, Tizen, Flash, Unity 3D (да, Kha может работать поверх другого игрового движка), Node.js (для создания серверных версий), Java и AWT, C# и WPS. Этот список просто ошеломляет. Пока вы читаете эти строки, Kha уже портируется под еще одну платформу).  Kha позиционируется как высокопроизводительное решение (с поддержкой аппаратного ускорения), имеющее современное и унифированное 2D и 3D API для работы на разных целевых платформах. Пожалуй, самыми известными приложениями, использующим Kha, являются уже впечатляющий своими возможностями игровой движок, интегрированный с BlenderArmory3D и приложение для рисования physically-based текстур — ArmorPaint.

 

Другим, при этом не менее популярным, игровым движком (от создателя языка Haxe) является Heaps. Heaps поддерживает 2D и 3D графику с аппаратным ускорением. Само собой, он является кроссплатформенным решением и поддерживает билд под все основные платформы, как десктопные, так и мобильные. В том числе и игровые приставки: PS4, XBox и Switch.

 

Существует также ряд других игровых библиотек, таких как HaxeFlixel, HaxePunk. Сюда же можно отнести экстерны к Unreal Engine — unreal.hx и экстерны к Defoldhxdefold. Таким образом, выбор библиотек в игровом плане на Haxe более чем приличен, и, думаю, каждый сможет найти что-то подходящее для себя.

Для реализации 2D физики существует Nape, порт Box2D и HaxeBullet для 3D.

 

Список библиотек одними игровыми не заканчивается. Если вам нужно протестировать ваш код, то в стандартной библиотеке Haxe есть простые классы, позволяющие это сделать. Если этого мало, то на помощь приходят более «навороченные» библиотеки: utest, munit (плюс mcover), haxUnit и buddy и другие.

 

Для внедрения зависимостей можно использовать minject, hexInject, dodrugs (проверка всех зависимостей в dodrugs происходит во время компиляции, т.е. некорректный код даже не скомпилируется) и другие.

 

Для отрисовки UI можно использовать haxeui, StablexUI Например, для ранее упоминавшегося Heaps, DomKit . Для анимирования также можно использовать Actuate, tweenxcore или его старшего брата tweenx. Есть и другие.

 

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

 

Истории успеха

Довольно интересно и полезно (особенно если и вам необходимо произвести портирование вашего проекта с Flash платформы) почитать о том, как команде FlowPlay удалось портировать на Haxe просто огромную (более 10 миллионов строк кода) кодовую базу проекта Vegas World с Flash платформы на HTML5 и нативно для мобильных платформ (Android и iOS) относительно быстро и безболезненно. Таким образом, Haxe не только позволил портировать проект и добавить новую платформу, но и улучшил производительность, повысил безопасность приложения и поддерживаемость проекта. И это — без каких-то дополнительных расходов, что, согласитесь, дорогого стоит.

 

The Joy of Haxe — другая, но не менее увлекательная и успешная история портирования онлайн редактора шрифтов FontStruct с Flash платформы на HTML5. Кроме прочего, была объединена ранее состоявшая из нескольких частей кодовая база (на разных языках) в одну на Haxe, что, несомненно, упростило поддержку проекта в целом и ускорило дальнейшую разработку. Также отмечается, что была стабилизирована работа серверной части, значительно улучшена ее производительность, и повышена стабильность (не одного крэша с момента портирования).

 

Возможно, следующим участником подобной истории будете вы?

 

Успешные игры

Dead Cells была многократно номинирована и завоевала несколько призовых мест: «Best Indie Game» от Golden Joystick Awards; «Best Action Game» от The Game Awards 2018;

“Broadway Award for Best Indie Game” от New York Game Awards; «Control Design, 2D or Limited 3D» от National Academy of Video Game Trade Reviewers Awards. На данный момент продано более двух миллионов экземпляров игры, и продажи продолжаются.

 

Northgard — другая популярная игра от Shiro Games и создателя Haxe. По данным от steamspy, игра распродана в количестве от полумиллиона до миллиона копий, что более чем неплохо.
Другой не менее популярной игрой, так же реализованной на Haxe, является Evoland с ее 200 тыс. — 500 тыс. проданных копий.

 

Заключение

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

Несмотря на то, что Haxe — довольно мощный инструмент для кроссплатформенной разработки, он не является сложным в плане обучения (по крайней мере в начале обучений). Это — также  его положительная черта.

 

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

 

Полезные ссылки:

 

 

Добавить комментарий

1 комментарий

  1. slavara:

    «вместо ключевого слова untyped добавлены методы js.Syntax.code() , php.Syntax.code() и аналогичные. Новые методы, в отличие от untyped , типобезопасны и дружелюбны к анализу;»
    Это не правда, *platform*.Syntax — добавлен не вместо ключевого слова untyped, а для того чтобы использовать платформ-специфик код, который нельзя написать на haxe, и т.д.

    «сервер компиляции — кэширует код и ускоряет компиляцию и подсказки кода;»
    Существовал и до Haxe 4

    «munit (плюс mcover)» — не очень понимаю зачем перечислять зависимости, тем более даже не все, а то там и hamcrest есть и еще некоторое…

    «Само собой, если требуется что-то более узко направленное, то придется писать экстерны (или просто подключить, это зависит от целевой платформы). Если есть соответствующая нативная реализация, то,в худшем случае, придется либо портировать, либо писать с нуля.»
    Если есть нативная реализация, то тут как раз или подключить или написать экстерны, а вот если нет то писать с нуля, а портировать ее возможно имеет смысл, только если нужен такой же функционал на другие платформы. А то я представляю историю:
    — смотрите есть то что нам нужно, давайте подключим?
    — нет, давайте перепишем готовое с нуля, так интереснее!
    — а давайте

    Для flash кроме байткода так же можно получить и исходники(хотя кому это сейчас нужно)

    Я бы добавил, что Haxe 4 обратно не совместим с Haxe 3

    Про lime я бы взял официальное описание с https://lime.software/ а не придумывал бы свое :)