Привет, Хабр! Представляю вашему вниманию перевод статьи The Joy of Haxe. FontStruct’s love affair with a neglected programming language.
Довольно грубая попытка воспроизвести логотип Haxe в редакторе шрифтов FontStruct
Недавно мы открыли исходный код наиболее важной части нашего модуля для создания шрифтов. Это библиотека fonthx для создания TrueType-шрифтов, и написана она на Haxe.
В данной статье практически нет кода. Если же вас интересует только код, то ознакомиться с ним можно на github, а его работа показана на примере простейшего редактора пиксельных шрифтов, построенного с использованием библиотеки fonthx.
Независимо от того, знакомы ли вы с Haxe или нет, проект fonthx возможно заинтересует вас, так как в нем демонстрируются некоторые удивительные возможности языка, малоизвестные вне сообщества Haxe — в частности, возможность писать код на одном приятном языке с дальнейшей компиляцией / трансляцией не только в Javascript, но и во множество других платформ, в случае fonthx такими платформами являются JVM (код проекта транслируется в Java), нативный код (C++ или C #), NodeJS или WASM.
Меня можно назвать убежденным полиглотом. Как и многие другие разработчики, я пишу код на разных языках и, уважая серьезные намерения тех, кто предпочитает специализироваться только на каком-либо одном языке, я также опасаюсь всех тех фанатов и мошенников-евангелистов, которые твердят об одном истинно верном языке. Но что же побудило меня использовать такой нишевый язык как Haxe для создания ключевых компонентов FontStruct, а также написать данную статью? И что же такое Haxe?
Haxe — это современный, строго типизированный язык программирования с некоторыми интересными функциями и небольшой стандартной библиотекой.
Наиболее существенной особенностью языка является его способность компилировать один и тот же код под разные целевые платформы.
Большинство разработчиков знакомы с понятием «изоморфной» разработки, когда программист пишет и серверный и клиентский код на одном языке. Чаще всего в качестве такого языка используется Javascript или TypeScript, но также может использоваться любой другой язык, способный компилироваться в Javascript. Haxe в данном направлении идет намного дальше, открывая ряд интересных дополнительных возможностей. Из одной кодовой базы на Haxe возможно скомпилировать исполняемый файл, Java-приложение, WASM-модуль, приложение на NodeJS, приложение, работающее в браузере, мобильное приложение, и даже библиотеки для таких языков как PHP, Lua или Python.
В FontStruct мы пользуемся данной возможностью, разрабатывая ключевые компоненты нашего приложения на Haxe. Серверные модули компилируются как Java-сервлеты, а модули клиента скомпилированы в виде приложений на JS. И хотя в настоящее время мы фокусируемся в основном на этих двух целевых платформах (а также на NodeJS для запуска тестов на скорость), мы также рассматриваем возможность использования инструментов, предоставляемых экосистемой Haxe, для создания мобильных приложений на C++. Кроме того, использование Haxe в дальнейшем может позволить создать нативное (не основанное на Electron) настольное приложение.
Благодаря тому, что Haxe поддерживает такое множество целевых платформ, он лучше всех (по крайней мере для наших задач) реализует идею “напиши код один раз — запускай его везде”, лучше чем любой инструмент, с которым я встречался за последние двадцать лет. Это дает ощущение того, что один и тот же код возможно скомпилировать под любую платформу или среду выполнения.
FontStructor — бесплатный редактор шрифтов на FontStruct.com
Более 10 лет назад, когда FontStruct был запущен, для редактора шрифтов FontStructor, а также для виджетов для просмотра шрифтов использовался Adobe Flash. А для создания TrueType-шрифтов на сервере использовался совершенно независимый код, написанный на Java.
Старая Flash-версия редактора FontStructor. Все элементы управления в нем рисовались во Flash. Обратите внимание на странные полосы прокрутки на панелях слева. Со временем главное меню в верхней части страницы постепенно расходилось визуально и функционально с html-меню, используемом в остальных частях сайта. Здесь нет ни DOM, ни CSS
Хотя FontStruct был и остается огромным успехом, наши первоначальные технические решения не прошли проверку временем. Развитие веб-стандартов и отсутствие поддержки плагина на мобильных платформах сделали Flash непригодным для использования в веб-приложениях. Также нам приходилось иметь дело с дублированием кода между сервером на Java и клиентом на Flash, а также между Flash и остальными частями клиента, написанными на HTML/JS/CSS. Было необходимо постоянно синхронизировать между собой три кодовые базы как с точки зрения функциональности, так и дизайна.
В 2013 году мы начали рассматривать возможность использования Haxe, в частности, в качестве средства для перевода редактора FontStructor с Flash на HTML5. В наших первоначальных исследованиях мы обнаружили, что уже имеются доступные инструменты для автоматического преобразования ActionScript кода в Haxe (as3hx) и даже Haxe-порт MVC-фреймворка Robotlegs, который мы использовали во Flash-версии. И так мы начали эксперимент по портированию клиента.
Наш первый год отношений с Haxe сопровождался нерешительностью и сомнениями.
По нескольким причинам, и не в последнюю очередь из-за ограниченности ресурсов, которые мы могли бы ему посвятить, процесс портирования оказался довольно медленным. Как уже отмечали другие авторы, инструмент для автоматического преобразования ActionScript-кода в Haxe (as3hx) оказался очень полезным, но не лишенным недостатков — полученный с помощью него код пришлось дополнительно просматривать и править. Во время этого процесса пришло понимание того, что наша кодовая база раздута и ошибочна в своем дизайне, поэтому параллельно с портированием мы решили внести улучшения, тем самым еще больше замедлив дальнейший прогресс. Кроме того, Haxe был для нас новым языком, поэтому мы неизбежно делали ошибки, изучая этот язык и его инструменты.
Безусловно, самой большой нашей ошибкой было решение дополнительно использовать сторонний UI-фреймворк на Haxe.
Для Haxe существует множество таких фреймворков (возможно, их даже слишком много), предоставляющих унифицированный API-интерфейс для отрисовки графики на различных платформах. OpenFL и NME являются примерами таких фреймворков и предоставляют инструменты для сборки приложений на Haxe под мобильные платформы и даже консоли. При этом они предоставляют реализацию Flash API для отрисовки графики. Все это казалось очень заманчивым — мы могли перенести наше приложение на HTML5, используя знакомое API и, возможно, даже создать одновременно с этим приложения для Android и iOS!
Поэтому для портирования нашего приложения на Haxe мы решили использовать еще и OpenFL.
Я не хочу проявить неуважение к OpenFL (мне даже хочется сказать, что: «Проблема не в OpenFL, а в нас»). OpenFL — фантастический проект, который хорошо показал себя во многих проектах, и мы можем вернуться к нему в будущем, но после портирования порядка 90% нашего приложения на Haxe, мы решили все же отказаться от него.
OpenFL оказался гораздо более серьезной зависимостью, чем мы ожидали. Изначально планировалось, что он будет использоваться лишь как внешняя библиотека для нашего UI, однако в итоге оказалось, что OpenFL тащит за собой свои собственные инструменты для сборки проектов, специальные форматы файлов для описания проектов, а также дополнительные внешние зависимости. Возможно это обусловлено тем, что как и большая часть экосистемы Haxe, OpenFL старается отвечать потребностями инди-разработчиков игр, при этом он развивается довольно быстро и основное внимание при его разработке уделяется добавлению новых функций, а не стабильности.
Самым главным для нас стало то, что сгенерированный с помощью OpenFL HTML5-код идеален для игр, но для такого приложения как FontStruct он не подходит. Все, чего мы хотели в итоге, это отобразить в браузере обычное DOM-дерево, которое мы могли бы стилизовать с помощью CSS, а не набор canvas-элементов или спрайтов, созданных OpenFL.
Нам был нужен обычный HTML5, а не попытка повторить Flash в браузере без использования дополнительных плагинов.
Как только мы отказались от использования OpenFL и поняли, что можем делать все, что хотели, используя только Haxe, все пошло быстрее, проще и, в конечном счете, намного веселее.
И работать с Haxe — это весело. Он, можно сказать, создан специалистами по веселью — независимыми разработчиками игр. Разработчикам игр нужна быстрая компиляция, а также единая кодовая база для создания приложений для настольных компьютеров, браузеров, iOS, Android и консолей. Разработчикам игр нужен производительный и стабильный код. И Haxe предоставляет все эти замечательные возможности, и не только для разработчиков игр.
Не побоюсь высказать идею о том, что одна из самых больших проблем Haxe — относительно небольшой размер его сообщества и связанная с этим разреженность его экосистемы — также является его преимуществом с точки зрения опыта для разработчика. Работая с Haxe, вы вряд ли будете тратить часы на гугление и поиск информации на stack-overflow, или на разбор и сравнение разных уроков по интересующей теме, а также во время изучения API какого-нибудь нового фреймворка или библиотеки — этих ответов, уроков и библиотек вполне может просто не быть. А если и есть, то, скорее всего, они будут единственными материалами по данным темам. С Haxe вы будете решать задачи программирования самостоятельно (!), самостоятельно писать библиотеки или, не испытывая ни малейшего чувства вины, даже самостоятельно заново изобретать или портировать колесо. Это весело и дает свободу, и это то, к чему многие разработчики стремятся после бесконечных фреймворков и современной разработки, связанной с копированием кода из разных источников. Используя принцип “меньше — значит лучше”, Haxe не уникален, но это его определенный плюс.
Haxe дает программисту уникальные возможности. Он дает особое ощущение удивительного открытия и радости. Используя Haxe, с его возможностью компилировать код под множество целевых платформ, возникает ощущение, что программист освобождается от одной из самых фундаментальных зависимостей из всех существующих — самой среды исполнения.
Запуск нашего первого компонента на Haxe для сайта FontStruct в апреле 2015 года был странным и неожиданным событием.
Галерея является центральной частью FontStruct: доступные для поиска, сортируемые списки тысяч шрифтов, созданных на нашей платформе. С самого начала в 2008 году у нас были постоянные проблемы со скоростью загрузки и с производительностью предварительного просмотра шрифтов как в самой галерее, так и на других страницах сайта.
Часть страницы галереи FontStruct, где показан предварительный просмотр шрифтов, созданных на платформе. Первоначально каждый отдельный элемент предварительного просмотра был представлен медленно загружаемым Flash-роликом!
Поскольку шрифты FontStruct («FontStructions») хранятся в проприетарном формате и часто редактируются, невозможно использовать их для рендеринга, как обычные TrueType-шрифты. Это означало невозможность создания на сервере растровых изображений для предварительного просмотра шрифтов (с использованием библиотеки FreeType).
Чтобы «решить» эту проблему, в самые ранние дни жизни FontStruct мы использовали отдельные Flash-ролики для элементов галереи. Каждый Flash-ролик загружал и парсил данные в нашем проприетарном формате, и затем показывал на клиенте изображение для предварительного просмотра. При отображении на странице 20 и более таких роликов, каждый из которых загружал данные и пытался отрисовать шрифт, время загрузки и потребление ресурсов пользовательской машины значительно увеличивались, и ситуация еще больше усугублялась со временем, так как шрифты, разработанные в нашем редакторе, становились все более и более сложными.
В конце концов мы использовали причудливое гибридное решение: при самом первом просмотре шрифта использовался Flash-ролик, который генерировал изображение для предпросмотра, затем данное изображение захватывалось и сохранялось на сервере в виде PNG-файлов. И для предпросмотра для следующих пользователей уже использовались сохраненные на сервере PNG. — Этот странный хак значительно улучшил время загрузки страницы, но он был грязным и, в конечном счете, неправильным что ли. Верным решением было бы написать совершенно новый серверный модуль — например, на Java или PHP — для загрузки и парсинга данных шрифта, и дальнейшего создания на его основе растрового изображения, но у нас просто не было ресурсов для этого.
Тем временем процесс портирования Fontstructor на Haxe продвигался очень медленно, и мы были далеки от релиза. Но мы продвинулись достаточно далеко, чтобы понять, что новый код на Haxe может решить наши проблемы с предварительным просмотром шрифтов в галерее.
У нас уже был Haxe код для загрузки и парсинга нашего формата шрифтов. У нас был код для отрисовки глифов (на Canvas в HTML5 или на спрайтах во Flash). У нас также были все компоненты, необходимые для решения проблемы на стороне клиента. Могли ли мы адаптировать этот код для использования на сервере?
Да! После того, как мы осознали это, процесс пошел очень быстро. Сначала мы думали задействовать C++ и библиотеку Cairo для отрисовки шрифтов (надеясь, что мы сможем написать расширение для PHP или какой-нибудь CGI-модуль), однако вместо этого было решено использовать Java. — Как замечательно иметь возможность сделать такой фундаментальный выбор с помощью нескольких строк кода и условной компиляции! Мы также могли использовать для этого NodeJS и node-canvas, но мы уже были знакомы с процессом создания и развертывания Java-сервлетов, и в Java есть все необходимые нам функции для отрисовки и манипулирования растровыми изображениями. Возможно, писать код на Java нам было не по нраву, но с Haxe такая необходимость отпадает.
Такой высокоуровневый код для отрисовки может быть скомпилирован и запущен как на клиенте (JavaScript), так и для сервере (в JVM)
В итоге на внесение изменений в нашей кодовой базе у нас ушло всего несколько дней, и новый рендерер был запущен. В одночасье мы значительно улучшили производительность галереи FontStruct, избавились от использования грязных хаков, и, самое главное, запустили наш самый первый компонент на Haxe на бой. Мы поняли, что Haxe способен удивлять и обладает гораздо большими возможностями, чем мы от него ожидали.
В ноябре 2015 года, то есть спустя 7 месяцев, мы успешно запустили первый клиентский модуль на Haxe — HTML5-версию виджета для предпросмотра шрифтов. Я думаю, что наши пользователи практически не заметили изменений, что с одной стороны как-то разочаровывает, но, в конечном счете, является показателем успешности данного начинания.
Теперь у нас оставался только один Flash-модуль на сайте — редактор FontStructor, и нам понадобился еще один год, чтобы, наконец, запустить в ноябре 2016 года его HTML5-версию. Так настало время окончательно отказаться от нашего AS3-кода и объявить FontStruct свободным от Flash.
Новая HTML5-версия FontStructor, запущенная в 2016 году
В августе 2018 мы портировали на Haxe модуль генерации шрифтов «FontMortar». Этот последний порт позволил нам полностью отказаться от кода, написанного на Java.
Я не готов использовать Haxe для всего. Да, я знаю, что есть проекты, где он используется в качестве основного инструмента для всех аспектов веб-разработки, но меня в качестве основы нашего веб-приложения очень устраивает превосходный Symfony фреймворк. В FontStruct продолжает использоваться множество различных языков программирования, но внедрение Haxe позволило нам уменьшить размер и сложность наиболее важных частей нашего кода. Для нашей крошечной организации такое упрощение оказалось жизненно необходимым для поддержания и развития платформы.
Если это первый раз, когда вы услышали о Haxe и мой рассказ заинтересовал вас, то я просто обязан перечислить еще несколько фактов о нем.
Большинство опытных разработчиков готовы к изменениям, но при этом они осторожны в выборе используемых технологий. Вероятно, что в какой-то момент большинство из Вас также испытывало желание сменить язык программирования в рамках проекта, особенно когда появляются новые, модные языки, с большим количеством интересных возможностей. Мы были осторожны насчет выбора Haxe, думая о том, как долго продлятся наши отношения. Примерно такими были наши мысли на его счет:
Звучит здорово, но у него такое маленькое сообщество. Что с ним будет через 5 лет? Сайт вроде нормальный, но почему-то он не выглядит современным. И это не внушает доверия.
Кажется, что он не так уж хорошо документирован.
Разве это не для инди-разработчиков игр?
Спустя пять лет использования Haxe, я искренне удивлен тем, что не сожалею о нашем выборе данной технологии. Несмотря на все недостатки и все сложности, вызванные ими, несмотря на относительно небольшое сообщество и отсутствие крупных корпоративных спонсоров, Haxe полностью справляется со своими задачами. С Haxe я чувствую свободу и независимость от какой-либо платформы. Теперь у нас единая кодовая база для основных компонентов FontStruct, в то время как раньше их было две. За прошедшие несколько месяцев работы новые версии сервлетов, отвечающих за генерацию шрифтов и изображений для предварительного просмотра, ни разу не давали сбоев. Новые HTML5-редактор и виджет для предварительного просмотра теперь работают во всех браузерах, включая мобильные, тогда как раньше мы были вынуждены работать с устаревшей и умирающей технологией.
И, если отбросить практическую выгоду, работа с Haxe приносит радость и чувство волшебства, радость Haxe!
Довольно грубая попытка воспроизвести логотип Haxe в редакторе шрифтов FontStruct
Недавно мы открыли исходный код наиболее важной части нашего модуля для создания шрифтов. Это библиотека fonthx для создания TrueType-шрифтов, и написана она на Haxe.
В данной статье практически нет кода. Если же вас интересует только код, то ознакомиться с ним можно на github, а его работа показана на примере простейшего редактора пиксельных шрифтов, построенного с использованием библиотеки fonthx.
Независимо от того, знакомы ли вы с Haxe или нет, проект fonthx возможно заинтересует вас, так как в нем демонстрируются некоторые удивительные возможности языка, малоизвестные вне сообщества Haxe — в частности, возможность писать код на одном приятном языке с дальнейшей компиляцией / трансляцией не только в Javascript, но и во множество других платформ, в случае fonthx такими платформами являются JVM (код проекта транслируется в Java), нативный код (C++ или C #), NodeJS или WASM.
Меня можно назвать убежденным полиглотом. Как и многие другие разработчики, я пишу код на разных языках и, уважая серьезные намерения тех, кто предпочитает специализироваться только на каком-либо одном языке, я также опасаюсь всех тех фанатов и мошенников-евангелистов, которые твердят об одном истинно верном языке. Но что же побудило меня использовать такой нишевый язык как Haxe для создания ключевых компонентов FontStruct, а также написать данную статью? И что же такое Haxe?
Haxe
Haxe — это современный, строго типизированный язык программирования с некоторыми интересными функциями и небольшой стандартной библиотекой.
Наиболее существенной особенностью языка является его способность компилировать один и тот же код под разные целевые платформы.
Большинство разработчиков знакомы с понятием «изоморфной» разработки, когда программист пишет и серверный и клиентский код на одном языке. Чаще всего в качестве такого языка используется Javascript или TypeScript, но также может использоваться любой другой язык, способный компилироваться в Javascript. Haxe в данном направлении идет намного дальше, открывая ряд интересных дополнительных возможностей. Из одной кодовой базы на Haxe возможно скомпилировать исполняемый файл, Java-приложение, WASM-модуль, приложение на NodeJS, приложение, работающее в браузере, мобильное приложение, и даже библиотеки для таких языков как PHP, Lua или Python.
В FontStruct мы пользуемся данной возможностью, разрабатывая ключевые компоненты нашего приложения на Haxe. Серверные модули компилируются как Java-сервлеты, а модули клиента скомпилированы в виде приложений на JS. И хотя в настоящее время мы фокусируемся в основном на этих двух целевых платформах (а также на NodeJS для запуска тестов на скорость), мы также рассматриваем возможность использования инструментов, предоставляемых экосистемой Haxe, для создания мобильных приложений на C++. Кроме того, использование Haxe в дальнейшем может позволить создать нативное (не основанное на Electron) настольное приложение.
Благодаря тому, что Haxe поддерживает такое множество целевых платформ, он лучше всех (по крайней мере для наших задач) реализует идею “напиши код один раз — запускай его везде”, лучше чем любой инструмент, с которым я встречался за последние двадцать лет. Это дает ощущение того, что один и тот же код возможно скомпилировать под любую платформу или среду выполнения.
Процесс принятия Haxe
FontStructor — бесплатный редактор шрифтов на FontStruct.com
Более 10 лет назад, когда FontStruct был запущен, для редактора шрифтов FontStructor, а также для виджетов для просмотра шрифтов использовался Adobe Flash. А для создания TrueType-шрифтов на сервере использовался совершенно независимый код, написанный на Java.
Старая Flash-версия редактора FontStructor. Все элементы управления в нем рисовались во Flash. Обратите внимание на странные полосы прокрутки на панелях слева. Со временем главное меню в верхней части страницы постепенно расходилось визуально и функционально с html-меню, используемом в остальных частях сайта. Здесь нет ни DOM, ни CSS
Хотя FontStruct был и остается огромным успехом, наши первоначальные технические решения не прошли проверку временем. Развитие веб-стандартов и отсутствие поддержки плагина на мобильных платформах сделали Flash непригодным для использования в веб-приложениях. Также нам приходилось иметь дело с дублированием кода между сервером на Java и клиентом на Flash, а также между Flash и остальными частями клиента, написанными на HTML/JS/CSS. Было необходимо постоянно синхронизировать между собой три кодовые базы как с точки зрения функциональности, так и дизайна.
В 2013 году мы начали рассматривать возможность использования Haxe, в частности, в качестве средства для перевода редактора FontStructor с Flash на HTML5. В наших первоначальных исследованиях мы обнаружили, что уже имеются доступные инструменты для автоматического преобразования ActionScript кода в Haxe (as3hx) и даже Haxe-порт MVC-фреймворка Robotlegs, который мы использовали во Flash-версии. И так мы начали эксперимент по портированию клиента.
Медленный и полный проблем старт
Наш первый год отношений с Haxe сопровождался нерешительностью и сомнениями.
По нескольким причинам, и не в последнюю очередь из-за ограниченности ресурсов, которые мы могли бы ему посвятить, процесс портирования оказался довольно медленным. Как уже отмечали другие авторы, инструмент для автоматического преобразования ActionScript-кода в Haxe (as3hx) оказался очень полезным, но не лишенным недостатков — полученный с помощью него код пришлось дополнительно просматривать и править. Во время этого процесса пришло понимание того, что наша кодовая база раздута и ошибочна в своем дизайне, поэтому параллельно с портированием мы решили внести улучшения, тем самым еще больше замедлив дальнейший прогресс. Кроме того, Haxe был для нас новым языком, поэтому мы неизбежно делали ошибки, изучая этот язык и его инструменты.
Безусловно, самой большой нашей ошибкой было решение дополнительно использовать сторонний UI-фреймворк на Haxe.
Для Haxe существует множество таких фреймворков (возможно, их даже слишком много), предоставляющих унифицированный API-интерфейс для отрисовки графики на различных платформах. OpenFL и NME являются примерами таких фреймворков и предоставляют инструменты для сборки приложений на Haxe под мобильные платформы и даже консоли. При этом они предоставляют реализацию Flash API для отрисовки графики. Все это казалось очень заманчивым — мы могли перенести наше приложение на HTML5, используя знакомое API и, возможно, даже создать одновременно с этим приложения для Android и iOS!
Поэтому для портирования нашего приложения на Haxe мы решили использовать еще и OpenFL.
Я не хочу проявить неуважение к OpenFL (мне даже хочется сказать, что: «Проблема не в OpenFL, а в нас»). OpenFL — фантастический проект, который хорошо показал себя во многих проектах, и мы можем вернуться к нему в будущем, но после портирования порядка 90% нашего приложения на Haxe, мы решили все же отказаться от него.
OpenFL оказался гораздо более серьезной зависимостью, чем мы ожидали. Изначально планировалось, что он будет использоваться лишь как внешняя библиотека для нашего UI, однако в итоге оказалось, что OpenFL тащит за собой свои собственные инструменты для сборки проектов, специальные форматы файлов для описания проектов, а также дополнительные внешние зависимости. Возможно это обусловлено тем, что как и большая часть экосистемы Haxe, OpenFL старается отвечать потребностями инди-разработчиков игр, при этом он развивается довольно быстро и основное внимание при его разработке уделяется добавлению новых функций, а не стабильности.
Самым главным для нас стало то, что сгенерированный с помощью OpenFL HTML5-код идеален для игр, но для такого приложения как FontStruct он не подходит. Все, чего мы хотели в итоге, это отобразить в браузере обычное DOM-дерево, которое мы могли бы стилизовать с помощью CSS, а не набор canvas-элементов или спрайтов, созданных OpenFL.
Нам был нужен обычный HTML5, а не попытка повторить Flash в браузере без использования дополнительных плагинов.
Веселье
Как только мы отказались от использования OpenFL и поняли, что можем делать все, что хотели, используя только Haxe, все пошло быстрее, проще и, в конечном счете, намного веселее.
И работать с Haxe — это весело. Он, можно сказать, создан специалистами по веселью — независимыми разработчиками игр. Разработчикам игр нужна быстрая компиляция, а также единая кодовая база для создания приложений для настольных компьютеров, браузеров, iOS, Android и консолей. Разработчикам игр нужен производительный и стабильный код. И Haxe предоставляет все эти замечательные возможности, и не только для разработчиков игр.
Не побоюсь высказать идею о том, что одна из самых больших проблем Haxe — относительно небольшой размер его сообщества и связанная с этим разреженность его экосистемы — также является его преимуществом с точки зрения опыта для разработчика. Работая с Haxe, вы вряд ли будете тратить часы на гугление и поиск информации на stack-overflow, или на разбор и сравнение разных уроков по интересующей теме, а также во время изучения API какого-нибудь нового фреймворка или библиотеки — этих ответов, уроков и библиотек вполне может просто не быть. А если и есть, то, скорее всего, они будут единственными материалами по данным темам. С Haxe вы будете решать задачи программирования самостоятельно (!), самостоятельно писать библиотеки или, не испытывая ни малейшего чувства вины, даже самостоятельно заново изобретать или портировать колесо. Это весело и дает свободу, и это то, к чему многие разработчики стремятся после бесконечных фреймворков и современной разработки, связанной с копированием кода из разных источников. Используя принцип “меньше — значит лучше”, Haxe не уникален, но это его определенный плюс.
Haxe дает программисту уникальные возможности. Он дает особое ощущение удивительного открытия и радости. Используя Haxe, с его возможностью компилировать код под множество целевых платформ, возникает ощущение, что программист освобождается от одной из самых фундаментальных зависимостей из всех существующих — самой среды исполнения.
Рендерер FontStruct
Запуск нашего первого компонента на Haxe для сайта FontStruct в апреле 2015 года был странным и неожиданным событием.
Галерея является центральной частью FontStruct: доступные для поиска, сортируемые списки тысяч шрифтов, созданных на нашей платформе. С самого начала в 2008 году у нас были постоянные проблемы со скоростью загрузки и с производительностью предварительного просмотра шрифтов как в самой галерее, так и на других страницах сайта.
Часть страницы галереи FontStruct, где показан предварительный просмотр шрифтов, созданных на платформе. Первоначально каждый отдельный элемент предварительного просмотра был представлен медленно загружаемым Flash-роликом!
Поскольку шрифты FontStruct («FontStructions») хранятся в проприетарном формате и часто редактируются, невозможно использовать их для рендеринга, как обычные TrueType-шрифты. Это означало невозможность создания на сервере растровых изображений для предварительного просмотра шрифтов (с использованием библиотеки FreeType).
Чтобы «решить» эту проблему, в самые ранние дни жизни FontStruct мы использовали отдельные Flash-ролики для элементов галереи. Каждый Flash-ролик загружал и парсил данные в нашем проприетарном формате, и затем показывал на клиенте изображение для предварительного просмотра. При отображении на странице 20 и более таких роликов, каждый из которых загружал данные и пытался отрисовать шрифт, время загрузки и потребление ресурсов пользовательской машины значительно увеличивались, и ситуация еще больше усугублялась со временем, так как шрифты, разработанные в нашем редакторе, становились все более и более сложными.
В конце концов мы использовали причудливое гибридное решение: при самом первом просмотре шрифта использовался Flash-ролик, который генерировал изображение для предпросмотра, затем данное изображение захватывалось и сохранялось на сервере в виде PNG-файлов. И для предпросмотра для следующих пользователей уже использовались сохраненные на сервере PNG. — Этот странный хак значительно улучшил время загрузки страницы, но он был грязным и, в конечном счете, неправильным что ли. Верным решением было бы написать совершенно новый серверный модуль — например, на Java или PHP — для загрузки и парсинга данных шрифта, и дальнейшего создания на его основе растрового изображения, но у нас просто не было ресурсов для этого.
Тем временем процесс портирования Fontstructor на Haxe продвигался очень медленно, и мы были далеки от релиза. Но мы продвинулись достаточно далеко, чтобы понять, что новый код на Haxe может решить наши проблемы с предварительным просмотром шрифтов в галерее.
У нас уже был Haxe код для загрузки и парсинга нашего формата шрифтов. У нас был код для отрисовки глифов (на Canvas в HTML5 или на спрайтах во Flash). У нас также были все компоненты, необходимые для решения проблемы на стороне клиента. Могли ли мы адаптировать этот код для использования на сервере?
Да! После того, как мы осознали это, процесс пошел очень быстро. Сначала мы думали задействовать C++ и библиотеку Cairo для отрисовки шрифтов (надеясь, что мы сможем написать расширение для PHP или какой-нибудь CGI-модуль), однако вместо этого было решено использовать Java. — Как замечательно иметь возможность сделать такой фундаментальный выбор с помощью нескольких строк кода и условной компиляции! Мы также могли использовать для этого NodeJS и node-canvas, но мы уже были знакомы с процессом создания и развертывания Java-сервлетов, и в Java есть все необходимые нам функции для отрисовки и манипулирования растровыми изображениями. Возможно, писать код на Java нам было не по нраву, но с Haxe такая необходимость отпадает.
Такой высокоуровневый код для отрисовки может быть скомпилирован и запущен как на клиенте (JavaScript), так и для сервере (в JVM)
В итоге на внесение изменений в нашей кодовой базе у нас ушло всего несколько дней, и новый рендерер был запущен. В одночасье мы значительно улучшили производительность галереи FontStruct, избавились от использования грязных хаков, и, самое главное, запустили наш самый первый компонент на Haxe на бой. Мы поняли, что Haxe способен удивлять и обладает гораздо большими возможностями, чем мы от него ожидали.
Прощание с Flash (и c ванильной Java)
В ноябре 2015 года, то есть спустя 7 месяцев, мы успешно запустили первый клиентский модуль на Haxe — HTML5-версию виджета для предпросмотра шрифтов. Я думаю, что наши пользователи практически не заметили изменений, что с одной стороны как-то разочаровывает, но, в конечном счете, является показателем успешности данного начинания.
Теперь у нас оставался только один Flash-модуль на сайте — редактор FontStructor, и нам понадобился еще один год, чтобы, наконец, запустить в ноябре 2016 года его HTML5-версию. Так настало время окончательно отказаться от нашего AS3-кода и объявить FontStruct свободным от Flash.
Новая HTML5-версия FontStructor, запущенная в 2016 году
В августе 2018 мы портировали на Haxe модуль генерации шрифтов «FontMortar». Этот последний порт позволил нам полностью отказаться от кода, написанного на Java.
Я не готов использовать Haxe для всего. Да, я знаю, что есть проекты, где он используется в качестве основного инструмента для всех аспектов веб-разработки, но меня в качестве основы нашего веб-приложения очень устраивает превосходный Symfony фреймворк. В FontStruct продолжает использоваться множество различных языков программирования, но внедрение Haxe позволило нам уменьшить размер и сложность наиболее важных частей нашего кода. Для нашей крошечной организации такое упрощение оказалось жизненно необходимым для поддержания и развития платформы.
Погружаясь глубже в Хакс
Если это первый раз, когда вы услышали о Haxe и мой рассказ заинтересовал вас, то я просто обязан перечислить еще несколько фактов о нем.
- Поддержка языка со стороны IDE вполне приличная и постоянно улучшается, активно развиваются модули под IntelliJ Idea и Visual Studio Code.
- Haxe предоставляет средства для работы с нативным кодом и библиотеками, таким образом, он не ограничивает ваши возможности, навязывая лишь небольшое подмножество доступных функций. Если вы ведете разработку под JavaScript, то у вас есть возможность использовать любую JavaScript-библиотеку, например, для работы с React доступны различные биндинги. Для FontStructor мы используем несколько npm-модулей, таких как interactive.js и opentip. Написание биндингов для них заняло всего пару минут.
- Haxe имеет надежную продвинутую систему типов с такими возможностями как параметризация типов, обобщенные классы и методы, абстрактные типы и выведение типов.
- В Haxe есть чрезвычайно мощные макросы, обеспечивающее доступ к AST во время компиляции, что позволяет разработчикам добавлять свои собственные языковые конструкции и генерировать код динамически.
- Несмотря на то, что по Haxe в интернете не так уж и много ресурсов, для него есть репозиторий библиотек, покрывающих общие потребности разработчиков за пределами стандартной библиотеки (кроме того на Github можно найти еще больше репозиториев, не представленных на haxelib — прим. переводчика).
- И последнее, но от этого не менее важное: у Haxe очень талантливое и отзывчивое сообщество.
Заключение
Большинство опытных разработчиков готовы к изменениям, но при этом они осторожны в выборе используемых технологий. Вероятно, что в какой-то момент большинство из Вас также испытывало желание сменить язык программирования в рамках проекта, особенно когда появляются новые, модные языки, с большим количеством интересных возможностей. Мы были осторожны насчет выбора Haxe, думая о том, как долго продлятся наши отношения. Примерно такими были наши мысли на его счет:
Звучит здорово, но у него такое маленькое сообщество. Что с ним будет через 5 лет? Сайт вроде нормальный, но почему-то он не выглядит современным. И это не внушает доверия.
Кажется, что он не так уж хорошо документирован.
Разве это не для инди-разработчиков игр?
Спустя пять лет использования Haxe, я искренне удивлен тем, что не сожалею о нашем выборе данной технологии. Несмотря на все недостатки и все сложности, вызванные ими, несмотря на относительно небольшое сообщество и отсутствие крупных корпоративных спонсоров, Haxe полностью справляется со своими задачами. С Haxe я чувствую свободу и независимость от какой-либо платформы. Теперь у нас единая кодовая база для основных компонентов FontStruct, в то время как раньше их было две. За прошедшие несколько месяцев работы новые версии сервлетов, отвечающих за генерацию шрифтов и изображений для предварительного просмотра, ни разу не давали сбоев. Новые HTML5-редактор и виджет для предварительного просмотра теперь работают во всех браузерах, включая мобильные, тогда как раньше мы были вынуждены работать с устаревшей и умирающей технологией.
И, если отбросить практическую выгоду, работа с Haxe приносит радость и чувство волшебства, радость Haxe!
Fengol
Лично мое мнение, что это препроцессор для множества языков. Ведь несмотря, что его синтаксис можно скомпилировать под множество языков, не зная их api, то есть не зная сам язык, написать ничего не получится. К тому же, его возможности можно считать доисторическими.
Zaphy Автор
А какой язык тогда вместо него предложите?
Fengol
В зависимости от задачи? мой выбор — ts, c#, f#. Просто когда говорят о подобных языках, то перечисляют только плюсы. Типа — один язык, все платформы. А минусы — нужно знать все языки под которые будешь компилировать. Невозможно написать одно приложение под все платформы будут просадки производительности, тем более трансляторе как haxe. Если на xamarin несмотря наприведение все к единому интерфейсу упорешся разграничивать все в зависимости от платформы, здесь логика и методы будут вообще разные, это рано или поздно сделает проект неповоротливым.
Поэтому в 2019 году нужно выбирать технологии в зависимости от задач. Почти у всех нормальных языков есть кроссплатформенные инструменты.
andrew911
Не обязательно знать и компилировать под все языки в конкретном случае.
Haxe намного раньше и в чем-то лучше заменяет TS для JS. Некоторые компании его используют в связке с Unity3D и Unreal используя одну кодовую базу для клиента и сервера.
AVL93
Идея в этом конечно очень крутая, но чтобы реально использовать один код на разных платформах — надо очень хорошо понимать, где язык гарантирует одинаковую работу кода, а где нет. А он может не гарантировать этого даже в самых, на первый взгляд, простых ситуациях.
Fengol
Мне не известны случаи, когда какой-либо язык не привносил чего-то уникального в тот мир, для которого он создавался. Поэтому унификация всех языков за счет собственных абстракций-языковых конструкций (как например реализация структур данных) сведет на нет все преимущества языков. Есть такое устоявшееся мнение, что писать нужно не на языке, а с использованием языка. Эта фраза была сказана очень давно и на сегодняшний день уже не актуальна, так как с тех времен, многое изменилось, появились огромные корпорации, коммунити, которые разрабатывают-затачивают языки под свои конкретные задачи. Поэтому сейчас к выбору языка подходят также, как к выбору инструмента. Если язык абстрагирует работу, скажем с самым слабым местом, с i\o или же структурами данных, с помощью собственных реализаций, то он он просто нивелирует все плюсы. Поэтому сейчас, если кто-то ещё и держит в голове вышеупомянутую фразу, то её нужно как минимум переместить на другой архитектурный слой, а именно инфраструктурный. То есть, унификация в современном мире должна реализовываться за счет провайдеров\плагинов, в АОП ключе. Но насколько мне известно в haxe, этого нет. К тому же до ts, ему как до звезды. ts на сегодня один из лучших языков. синтаксические возможности haxe, это прошлый-прошлый-прошлый век.
И мне сложно представить в чем смысл писать одну игру на unity и ue одновременно? Это очень похоже на глупость. Почему бы не писать сервер, на тех языках, на которых хочется и связывать это все при помощи микросервисов? Так делают все крутые игровые чуваки. А код на ue и unity разве может быть идентичным? Максимум какие-то утильки, из-за которых думать о выборе haxe, просто дольше, чем написать.
andrew911
В чем Haxe-у как до звезды TS и какие его возможности прошлый век?
Я не писал, что одна игра и на UE и на Unity3D, это разные игры и разные компании.
Слишком громкая и слишком абстрактная фраза.
dmitryhryppa
Вы о том, что какая-то операция на Си у вас займет 0.0003 секунды, а на Haxe 0.0008 секунд? Такого рода оптимизации не нужны для 95% софта. И тонны Electron приложений тому довозательство.
Более того, такие игры как Northgard, Papers please, Dead Cells, Rymdkapsel, Evoland и многие другие, которые написаны на Haxe и работают в том числе и на консолях с вами не согласны. А в играх-то производительность — не последнее слово, ведь так?
Более того, если уж говорить за возможности Haxe, то он позволяет писать платформозависимый код используя нативные конструкции конечного языка, в который он будет транслироваться, включая структуры данных. Включая вставки из этого языка (а-ка ассемблерные вставки в Си). Так что нет никакой проблемы написать узкое место «нативно».
Ну камон :) Крайне спорное заявляение.
Haxe компилируется в десяток раз быстрее, а JS код на выходе более оптимизированный, чем у TS за счет соответствующих возможностей: инлайн методы, конструкторы, статический анализатор кода и тд и тп.
У хакса действительно, не так много синтаксического сахара, по сравнению с котлином, например. Но вот про прошлый-прошлый-прошлый век — вообще с потолка :)
pecheny
Язык предоставляет абстракции для обобщенной работы с I/O и структурами данных на разных платформах. Как один из инструментов.
Кроме этого, он предоставляет много других инструментов: экстерны, абстракты и тайпдефы, которые позволяют писать обобщенный, и в то же время эффективный код.
Из недавнего: писал абстракцию над промисами для шарпов и js. При компиляции в js оно превращалось в работу со встроенными промисами, а на шарпах использовало библиотеку RSG.Promise. АПИ у них отличается принципиально, но в сгенерированном коде нет прослоек, один и тот же хаксовый код превращается на каждой платформе в прямые обращения к собственным платформенным классам.
Сам сервер можно писать на чем угодно, но есть большие куски логики, которые бывает очень удобно использовать независимо на клиенте и сервере.
Если их писать на haxe, то даже без микросервисной архитектуры можно использовать эту логику в совершенно разных стеках (В частности, менять стеки, не меняя логику). Мне известны успешные примеры использования haxe c серверной стороны, например, в стеках node.js, java, php.
Я верю, что можно написать такие прослойки, которые позволят запускать один и тот же код на обоих движках, хоть это будет и не самым эффективным/целесообразным.
Тем не менее, большая часть кода может быть написана с использованием абстракций, что позволит переносить наработки между платформами независимо.
Вот эти ребята использовали оба движка с хаксом
www.youtube.com/watch?v=WKs8QRuMC3k
и в каком-то из докладов я слышал, что чать наработок успешно перекочевала.
Кроме того, есть Kha, который предоставляет абстракции для работы с графическими АПИ, и позволяет писать достаточно низкоуровневые вещи типа шейдеров, корые можно компилировать под OpenGL, DirectX, Metal, Vulkan, WebGl, канвас и т.д (в том числе, для запуска внутри готовых движков типа анрила).
Ну тут я даже не знаю, как и возразить. Такая уверенность завораживает.
YuriHx
Всегда нужно выбирать технологии в зависимости от задач.
И в любом случае нужно знать API платформы, для которой выполняешь задачу, кэп.
Когда пишешь на том же C# под Unity или для другой платформы тебе нужно знать API или другой платформы, но это никак на влияет на оценку C#.
AVL93
andrew911
Haxe так умеет посредством фреймворков OpenFL, NME, Kha
AVL93
Фреймворки перекрывают API, однако остается тот факт что многие базовые конструкции языка могут вести себя по разному. Например, стандартный Int на некоторых таргетах будет переполняться через 32 бита, а на некоторых — вести себя как float.
dmitryhryppa
Нет, это не так. И никогда так не было. Int будет работать консистентно на всех платформах и уж тем более не будет вести себя как float.
AVL93
Как раз таки это даже документировано: haxe.org/manual/types-overflow.html
Где-то переполняется, где-то теряет точность на больших числах. И да, можно использовать Int32, но что делать если фреймворк который я использую, внутри использует Int? И опять же, это не единственный фактор, просто первый вспомнившийся пример.
dmitryhryppa
Я оспариваю не то, что документировано, а вашу риторику, что у вас все начнет переполняться, только от того, что Int, вдруг, везде разный. Речь идет за 32-ух битный Int. Он везде будет вести себя одинаково и до тех пор пока вы не запиеште туда значение больше, чем 32-ух битный Int может вмещать. То, как себя поведет переполнение уже и зависит от платформы, если не использовать специальные типы, которые это предотвращают.
Тот проект, который у вас работает отлично, скажем, на C++ таргете, будет 1 в 1 работать и на JS таргете, с таким же поведением Int'a и ничего у вас не переполнится. Аналогично вашему примеру с Юнити.
А если вы в Int пишете значение больше 2^32, не используя для этого Int64 — это уже вопрос к вам.
AVL93
Я могу спокойно работать с большими Int на js таргете, и наткнуться на проблемы при переносе кода на C++. И наоборот, я могу построить вполне себе нормальный алгоритм, опираясь на то что в C++ int гарантированно и предсказуемо переполняется, и получить проблемы при переносе на js. В приведенном примере с Unity — такого не наблюдается, код на C# скомпилированный в js будет работать именно так как описано в спецификации языка C#.
И, да, я понимаю что это осознанное решение разработчиков языка — пожертвовать универсальностью ради производительности. Но я считаю что это надо более явно и подробно описывать в документации, с большими флагами «ТУТ UNDEFINED BEHAVIOUR!!!».
dmitryhryppa
Вы с этим столкнетесь даже если вручную будете портировать код, потому что нельзя впихнуть больше, чем «обычный» int позволяет. Вам придется использовать для этого другой тип. Например, long в С++.
Я искрине не понимаю, в чем тут принципиальная проблема. Потому что там, где нужен большой инт, там используют для этого специальный тип, объявляющий большой инт.
Если железно нужен Int32, используйте Int32 и все будет консистентно.
AVL93
Любой кроссплатформенный язык имеет как строго описанное в спецификации поведение, так и UB определяемые целевой платформой. В haxe же таких UB очень много (да и спецификации языка как таковой нет).
В целом, я понимаю вашу точку зрения, и знаю что перечисленные мной недостатки (или то что я считаю недостатками) можно обойти. Но для этого надо понимать как сам Haxe, так и целевой язык на довольно высоком уровне.
rafuck
Я думаю, тут имеется в виду, что в JS вообще нет int, там есть только double.
dmitryhryppa
И да и нет, и не совсем. На TS->JS писать, ведь, ничего никому не мешает. В чем тогда проблема писать на Haxe->JS? Более того, Haxe имеет некоторые преимущества перед конкурентами:
github.com/damoebius/HaxeBench
А о каких именно возможностях речь? Синтаксис?
AVL93
С первого взгляда мне этот язык очень понравился, даже на работе агитировал за то чтобы некоторые новые проекты на нем делать… Но потом столкнулся с тем что:
— он на самом деле не является строго типизированным, строгость типизации полностью зависит от целевой платформы
— в стандартной библиотеке мало документации и много багов
Так что я перестал стремиться его использовать для хоть сколько-то важных проектов, разве что мелкие вспомогательные инструменты иногда на нем пишу.
YuriHx
Можете дополнить это утверждение ссылками на issue c багами в стандартной библиотеке, которые серьёзно осложняют вашу работу?
AVL93
В моем понимании, «строгая» типизация не должна такого позволять ни в коем случае. Если я не прав, то что же тогда понимается под словами «строго типизированный»?
Давно не писал на haxe ничего сложнее сотни строк, так что вспомню только например вот это — github.com/HaxeFoundation/neko/issues/167. Помню еще, что несколько раз сталкивался с тем что например поддержка ssl — согласно документации, есть, а в коде там одно большое TODO. А окончательно я разочаровался в языке, посмотрев на то как реализован HashMap.
andrew911
По поводу Dynamic, тут вопрос в качестве кода писавшего — не используйте его и будет все в порядке, а возможность отстрелить себе ногу даст любой язык.
AVL93
Ситуация была примерно такая: получаю JSON, использую стандартный парсер из стандартной библиотеки, получаю на выходе объект описанного мной класса, все поля строго типизированы — красота!!! (я надеялся что оно работает примерно так же как например gson). А потом внезапно (после пары часов отладки) обнаруживаю, что оказывается в Int поле спокойно можно записать строку. А что именно получится — либо просто строка, либо число получившееся после ее парсинга, либо ошибка выполнения — зависит от таргета.
P.S. В стандартном парсере json тоже баги были, но я сейчас точно не вспомню с чем именно связанные.
andrew911
Для строгой типизации есть другие варианты, помимо JSON, да и для него должны быть варианты.
pecheny
Зато haxe позволяет написать 30 строк макроса, который позволяет сгенерировать безопасный десериализатор для всех интовых полей классов, отмеченых метой.
Довольно изящно работая с узлами AST через pattern matching (к слову об устаревшем синтаксисе)
try-haxe.mrcdk.com/#4a425
Вот пример, сам макрос на закладке Source 2, можно посмотреть, как работает на js, и какой код генерирует.
bromzh
Типизация может быть строгой (сильной), но при этом динамической (как в Python). Может быть статической, но слабой (неявной), как в C++ и C.
Под строгой подразумевается типизция без неявного приведения типов. То есть, чтобы сложить число со строкой, нужно явно сделать из числа строку или наоборот. В питоне нельзя сделать так:
a = 1 + '2'
Под нестрогой (слабой) подразумевается типизация с неявным преобразованием типов (да и вообще, с преобразованием типов без создания нового объекта). Например, в С++ есть reinterpret_cast, а в си некоторые численные типы могут неявно быть преобразованы. В другом слаботипизируемом языке возможны вещи вроде
1 + '2'
,!!1
,const a = +'2'
YuriHx
Это же одно из важных свойств Haxe, что он даёт свободу выбора — нужна свобода и динамика — пожалуйста, использую Dynamic, untyped и т.д., нужна строгость — ок, не используй их и на этапе компиляции всё будет строго, включая выявленные типы.
Ну и конкретно о статической типизации, которую вы видимо имели имели в виду под строгой. Вот подробная таблица по целевым платформам: haxe.org/documentation/introduction/compiler-targets.html где указано, где она поддерживается платформой(!), а где нет. А в контексте нашей с вами дискуссии важна строка под таблицей, которая явно говорит нам что «Haxe code itself is statically typed no matter what target it is compiled to.»
Это ок, понятно, если вашей целевой платформой была neko, то я не удивлён вашим разочарованием. Хотя это уже про выбор платформы, а не язык. И одно из ключевых слов тут, возможно — «давно». Про neko ниже уже немного написали, уточню, что на момент эта платформа уже не актуальна, и будущего у неё нет, есть более современные решения, в том числе от того же разработчика. Последнее не имхо — те, кто следят за прогрессом Haxe, знают, что развивать neko дальше не будут.
AVL93
В общем, спасибо за ответ, кажется я понял в чем было мое заблуждение относительно работы с Dynamic. У меня было очень стойкое убеждение, что если компилятор не требует явного приведения типов при присвоении — значит, либо сработает неявное (или не сработает и гарантированно упадет при запуске), либо вообще все в порядке и оно не требуется.
dmitryhryppa
SSL уже везде и давненько есть. Там, где его раньше не было, там надо было библиотеку использовать.
Тип Dynamic — это ведь динамический тип, который может оказаться чем угодно в рантайме и риски с его использованием лежат исключительно на плечах программиста. Просто не стоит его использвать там, где ему не место и все. К слову, есть ведь typedef, который позволяет типизировать любой Dynamic, чтобы, случайно, не стрельнуть себе в ногу.
Справедливости ради, Neko — не совсем показатель, ибо через чур эзотерический и потому, даже рядом не стоит с Haxe -> JS, C++, PHP и другими.
AVL93
Cerberuser
TypeScript — строго типизированный или нет? Если да — то что там делает any, которое приводится в любой тип без явного каста?