Пишем кроссплатформенный код с Haxe

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

Кто не читал предыдущую статью, предлагаю ознакомиться. Кому лень читать, вкратце Haxe — это инструментарий для разработки кросс-платформенного ПО. Язык Haxe транслируется в множество других языков и может быть выполнен на разных платформах.

Это первая практическая статья, с которой можно начать свое знакомство с инструментарием Haxe в целом и понять (я надеюсь), что он из себя представляет.

Установка и настройка Haxe

  1. Качаем установщик для своей ОС.
  2. Качаем VSCode:
    • Устанавливаем расширение для Haxe: marketplace.

Готово :) Но, чтобы полностью оценить все возможности, описанные в этой статье, вам также не помешает установленный NodeJS и VisualStudio (для компиляции C# и С++ кода под Windows), установленная Java 7+ в системе и Python 3.

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

Практика

После того, как вы установили сам Haxe, редактор и плагин к нему, давайте приступим сразу к практике.

Создайте файл MyApp.hx следующего содержания:

package;
class MyApp {
   public static function main():Void {
       trace("Hello world!");
   }
}

Откройте терминал и выполните следующую команду:

haxe -main MyApp --interp

В случае успешного выполнения эта команда выведет на экран строку:

MyApp.hx:4: Hello world!

Давайте остановимся на этом этапе подробнее.

Как и во многих других объектно-ориентированных языках, основная единица в Haxe — класс. Он же, в свою очередь, объявляется внутри пакета (package, подобно Java), который является своего рода пространством имен. Пакет также должен соответствовать пути к файлу. То есть если мы объявили пакет package com.cosmething, то наш класс должен лежать в папке com/something. Имя класса объявляется с большой буквы.

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

Вызвав команду компиляции, мы указали наш основной класс -main MyApp, который содержит main функцию и передали туда флаг --interp, который интерпретирует наш код на лету без его компиляции.

JavaScript

Теперь попробуем создать JavaScript-файл из нашего Haxe-класса. Поменяйте условия компиляции следующим образом:

haxe -main MyApp -js ./myapp.js

После успешного выполнения команды рядом с вашим классом должен появиться сгенерированный myapp.js файл. Его можно открыть через редактор и посмотреть, что же нам сгенерировал Haxe компилятор.

Если у вас уже установлен NodeJS, то вы можете выполнить полученный JS следующим образом:

node ./myapp.js

В случае успешного выполнения NodeJS выведет на экран строку:

MyApp.hx:4: Hello world!

Мы получили то же самое, что и в первом примере. Разница лишь в том, что в условиях компиляции мы заменили флаг интерпретации кода на компиляцию (по факту трансляцию) под JS, указав в качестве значения имя выходного файла.

Теперь везде, где вам доступен JavaScript, вы можете выполнять свой код.

Справка. Когда вы пишете на современном JS, вам приходится использовать дополнительные инструменты вроде Webpack’a для склеивания ваших JS классов и файлов. Haxe делает это автоматически, и вся программа будет слита в один монолитный JS файл. Стоит, однако, упомянуть, что если есть такая необходимость, то можно генерировать по JS файлу на класс при помощи сторонней библиотеки, описание которой выходит за рамки данной статьи.

Java

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

Справка. Библиотека hxjava будет установлена глобально и будет доступна во всех Haxe проектах. За ее установку отвечает библиотечный менеджер haxelib. По умолчанию установка библиотек производится в папку установки Haxe (а там в папку lib). Изменить эту директорию можно вызвав команду haxelib setup.

Итак, выполним установку зависимости:

haxelib install hxjava

А затем вызовем компиляцию:

haxe -main MyApp -java ./java

Вместо -js флага мы теперь используем -java. В отличие от предыдущего флага, в качестве значения целевого языка мы указали директорию, а не выходной файл. Таким образом, у нас должна была появиться директория java, в которой лежит скомпилированный MyApp.jar. Перейдите в эту директорию и выполните его:

java -jar ./MyApp.jar

И консоль снова выдаст вам:

MyApp.hx:4: Hello world!

Теперь везде, где вам доступна Java 7+, вы можете выполнять свой код.

C#

Для этого целевого языка, подобно Java, нам нужно установить библиотеку hxcs:

haxelib install hxcs

Далее, как мы уже делали это раньше, скомпилируем наш код под C#:

haxe -main MyApp -cs ./csharp

Перейдя в директорию csharp/bin, вы сможете обнаружить ваш MyApp.exe. Запустив его с консоли, вы увидите уже привычный ответ:

MyApp.hx:4: Hello world!

Теперь везде, где вам доступен C#, вы можете выполнять свой код.

Python

Очередь за Python. Компилируем:

haxe -main MyApp -python ./myapp.py

Выполняем:

python ./myapp.py

И получаем:

Hello world!

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

package;
class MyApp {
   public static function main():Void {
       haxe.Log.trace = haxe.Log.trace; //заставит Haxe использовать свою реализацию вывода отладочной информации, вместо нативного в python print
       trace("Hello world!");
   }
}

Теперь вы можете выполнять свой код везде, где вам доступен Python3.

Справка. Очень часто слышу вопрос по поводу Python таргета — зачем он нужен? Ответ на поверхности и в целом применим и к другим таргетам. Haxe — это ООП язык, с мощной системой типов и макросов, кросс-компиляцией и оптимизациями на уровне генерации кода. Таким образом, если вам по какой-то причине не нравится или не хочется учить Python, то Haxe можно рассмотреть как его замену. Так же, как и TypeScript является заменой чистого JS. Вы можете взять любую Python-библиотеку, например TensorFlow, подключить ее к Haxe и писать код на строго типизированном ООП языке.

C++

Далее для компиляции C++ нам понадобится установленный в системе VisualStudio (под линуксом — GCC, под маком — Xcode). Подробнее о компиляторах — Getting started with Haxe/C++.

По примеру с Java и C#, необходимо установить библиотеку hxcpp, которая позволит нам компилировать сгенерированный код:

haxelib install hxcpp

После успешной установки мы можем скомпилировать наш код:

haxe -main MyApp -cpp ./cpp

В папке cpp должен был появиться файлик MyApp.exe, выполнив который мы уже традиционно получим:

MyApp.hx:4: Hello world!

Теперь вы можете выполнять свой код (почти) везде, где вам доступен C++.

Справка. С++ — очень многогранный таргет. Код может быть сгенерирован по С++11 стандарту, скомпилирован под разные архитектуры (x86, x64, arm), под разные платформы: Linux, Mac, Windows, WinRT, Android, iOS, QNX, консоли, etc. Hxcpp поддерживает статическую и динамическую линковки, как внешних библиотек, так и кода, написанного на Haxe (для использования в качестве библиотеки в существующем проекте, например). Также полученный код может быть использован через emscripten.

Файл сборки

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

Удалите все, кроме MyApp.hx, и создайте файт build.hxml, а файл MyApp.hx перенесите в папку src так, чтобы у вас получилась следующая иерархия:

./build.hxml ./src/MyApp.hx

В файле build.hxml добавьте следующие команды:

# This is our build file
-cp src
-main MyApp.hx
-js ./deploy/out.js
-dce full
-D analyzer-optimize
-debug

Теперь нажмите Ctrl+Shift+B, и VSCode откроет вам в ниспадающем списке доступные файлы сборки:

Выберете наш build.hxml, и тогда VSCode скомпилирует вам Haxe код в JS с выходным файлом ./deploy/out.js. Теперь мы можем перестать использовать терминал для компиляции кода. Стоит сказать, что этот процесс можно еще больше упростить, добавив задачи для сборки через настройки VSCode. Подробнее — Build Tasks.

Вернемся к коду. Как вы, наверное, заметили, у нас появились какие-то новые, непонятные флаги компилятора в файле build.hxml. Давайте разберем их:

  • -dce full, — это исключение мертвого, неиспользуемого кода. Таким образом, все классы, которые не используются, не будут включены в сборку (в том числе стандартная библиотека Haxe). Это позволяет генерировать более компактный код. Подробнее.
  • -D analyzer-optimize, — включает статический анализатор кода и старается оптимизировать то, что мы получаем на выходе. Подробнее.
  • -debug, — включает дебаг-режим и генерирует карты кода (source map). Подробнее.

И напоследок давайте соберем еще один маленький пример.

Создайте в папке deploy файл index.hxml со следующим содержанием:

<!DOCTYPE html>
<html>
   <head>
       <meta charset="utf-8" />
       <title>Hello, Haxe!</title>
       <script src="out.js"></script>
   </head>
   <body>
   </body>
</html>

А MyApp класс измените следующим образом:

package;
import haxe.Json;
import js.Browser.console;
import js.Browser.document;
import js.Browser.window;

class MyApp {
   public static function main():Void {
       //парсим JSON, типизировав объект (типизировать необязательно):
       var o:{my_value:Int} = Json.parse('{"my_value": 100}');
       //так что, поле my_value доступно через автодополнение кода:
       console.log(o.my_value);
      
       //выведем сообщением значение my_value:
       window.alert('my_value is ${o.my_value}');
      
       //подождем загрузки страницы
       //используем анонимную функцию
       document.addEventListener('DOMContentLoaded', function():Void {
           //приведем к строке распарсенный ранее объект:
           document.body.innerHTML = Json.stringify(o);
           throw "Something happened.";
       });
   }
}

Скомпилируйте Haxe код (Ctrl+Shift+B) и откройте index.html в браузере. Если вы сделали все верно, то браузер должен показать вам сообщение со значением переменной my_value. Если вы откроете консоль разработчика в браузере, то увидите, что console.log также отработал успешно. И напоследок, чтобы показать интеграцию с целевыми платформами, мы изменяем HTML код прямо из Haxe.

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

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

#js
trace("This code is available on JavaScript target.");
#elseif java
trace("This code is available on Java target.");
#else
trace(“This code is available on all (other) targets”);
#end

Haxe поддерживает и другие языки, например PHP, Lua, ActionScript. Рассматривать мы их уже не станем, чтобы не повторяться, так как кодогенерация происходит идентично вышеописанным языкам.

Также у Haxe есть хорошо оптимизированная виртуальная машина HashLink, которая может либо выполнять сгенерированный байткод aka JIT, либо конвертироваться в Си с последующей компиляцией и выполнением без виртуальной машины.

Выводы

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

Интересные факты:

  • Все целевые платформы Haxe могут использовать нативные библиотеки.
  • Компилировать сгенерированный код в исполняемый файл необязательно (Java, C#, C++). Вы можете генерировать только код, а компиляцию производить своими инструментами (либо включать сгенерированный код в уже существующие проекты). В этом вам поможет флаг -D no-compilation.
  • Генерируя JavaScript, вы можете использовать его как самостоятельное приложение, так и включать его в свои JS проекты и использовать напрямую из JS кода. Для того, чтобы Haxe классы стали доступны из JS кода, используйте для них @:expose мету. Подробнее.
  • Сгенерированный код Haxe может быть использован напрямую в целевых языках. Так же, как и эти языки могут быть использованы прямо в коде Haxe.

Ссылки

Чат: gitter.im/HaxeFoundation/haxe
Форум: community.haxe.org
Мануал: haxe.org/...​duction-what-is-haxe.html
Сравнение Haxe, TypeScript, Dart и Wasm: github.com/damoebius/HaxeBench


Связь со мной: [email protected]

В следующей статье мы посмотрим на интересные конструкции языка, его особенности и преимущества.

Все про українське ІТ в телеграмі — підписуйтеся на канал DOU

👍ПодобаєтьсяСподобалось0
До обраногоВ обраному1
LinkedIn



39 коментарів

Підписатись на коментаріВідписатись від коментарів Коментарі можуть залишати тільки користувачі з підтвердженими акаунтами.

Как раз сейчас сижу, несколько дней, занимаюсь тем что пытаюсь найти проблему в GC (IMMIX), С++.
Может Вы сможете что то подсказать.
Суть в том что когда идет запрос на память и этот запрос с сумароно выделлной памятью привышет некий порог (mLargeAllocForceRefresh) происходит собрка мусора CollectFromThisThread().
Далее происходит обход все статических мемберов всех типов о которых знает Haxe (из компиляции), и размечаются соотвествующие флаги «строк» в блоках.
Потом такой же обход происходит уже на уровне тогог на что ссылаются эти статические переменные и т.д.
После разметки флагов, происходит reclaim() который пересчитывает регионы в блоках, тем самым неиспользуемая под объекты память в эти регионы не попадает.
Проблема в том что в эти регионы попадает память память с «живыми» объектами, естественно она перетирается и потом уже при обращении к ней получается лажа.
У меня сейчас вот возникло подозрение что дело в неправильной интеграции кода С++ и кода С++ который сгенерировал Haxe.
Что нелзя вот так вот в классе который написал пользователь создавать объект класса который сгенерировал Haxe.
class MyClass {   com::flow::Converter converter;   MyClass()   {     converter = com::flow::Converter_obj::__new();   } }

По сути мы создаем объек средствами GC. На который никто никогда не ссылается.
Это ведь неправильная интеграция?

Привет! Ух... :) Путь этот плохо документирован, к сожалению. Конкретику какую-то я сейчас не подскажу, много лет прошло и что-то менялось с тех пор, когда я подобную задачу делал.
------------
Но вообще, чтобы использовать Haxe в качестве библиотеки, удобнее всего делать это через @:nativeGen мету и интерфейсы (nativeGen для С++ таргета работает только на них, не на классах, имей ввиду). Так же, обрати внимание на @:unreflective мету

Пример такого создания API можно глянуть тут:
github.com/...​ee/master/test/extern-lib
Пример использования тут:
github.com/...​ee/master/test/extern-use

Так же, вот это может пригодится почитать:
haxe.org/...​cpp-ThreadsAndStacks.html

----------
Ещё, есть библиотека (я лично не пробовал её), которая генерит макросами готовые хедеры для последующего использования Haxe библиотеки в С++ проекте:
github.com/haxiomic/haxe-c-bridge
Можно попробовать её поковырять, посмотреть как там решаются проблемы с GC\тредами.

Ну и в целом, глянь тесты, может чего интересного еще найдешь:
github.com/...​on/hxcpp/tree/master/test

Удачи ))

Пишем кроссплатформенный код на HAXE
0. выбираем фреймворк, заточенный под кроссплатформенную разработку
1. PROFIT!!!

Очень интересно.
А как будет выглядеть работа с указателями?

Фактичеси, с указателями работать не надо, т.к. у Haxe есть GC, который за всем смотрит.

Однако, если мы миксуем С++ код с хаксом, то да, возможность работы с указателями есть.
Вот такая запись: Some *p может быть получена через объявление в хаксе var p:Star<Some>. Либо же можно работать через класс Pointer, который предоаставляет дополнительный функционал для работы с указателями. У такого класса есть поля ptr и ref, которые возвращают указатель и ссылку, есть destroy и destroyArray. Есть еще всякие ConstPointer, ConstStar, ConstCharStar и тд и тп.
Вот тут, в целом, можно глянуть апи по C++ таргету: api.haxe.org/cpp/index.html

Нюансов много на самом деле и надо конкретный пример разбирать. Я С++ тему еще обязательно затрону в грядущих статьях.

Вот практический пример:
Нужно написать мультикастовый парсер.

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

Я обычно делаю с помощью смещения указателя на sizeof(datatype), в С этот код выглядит так

// интерпретируем интовое значение
char * buffer // указатель на начало буфера, в котором хранятся наши считанные данные

int32_t testInt = *(int32_t *)buffer;
buffer += 4;

Есть еще вариант со структурой (когда создается структурная маска и буфер интерпретирует его как тип данных структуры), но он менее быстрый (хотя и код гораздо меньше по объему) и есть ньюансы (связанные с особенностью дефолтной упаковки данных в структурах).

Еще встречал вариант с битовыми сдвигами, но пока еще не познал дзен этого способа :)

Ага, со структурами проходили уже)
Юзаем #pragma(pack, 1) ( код объявления структуры) #pragma(pop)
Вот только парсинг со сдвигом получается гораздо (до 3 раз) быстрее :(

Проще всего (если я правильно понял задачу), вообще опустить работу с указателями и воспользоваться апи сокетов, которые есть в хаксе. Тогда, можно будет читать информацию из сокета примерно таким образом:
var type = socket.input.readInt8(); //получаем, например, тип сообщения.
var size = socket.input.readInt32(); //длину сообщения
var bytes = socket.input.read(size); //забираем по длине оставшиеся байты
Ну и дальше уже конвертируем байты во что нам надо, например в строку.

Если вопрос стоит в том, чтобы переписать приведенный выше код 1 в 1, то это можно сделать разными способами:
— переписать тоже самое, только на Haxe, с использованием классов для работы с c++
— писать на С++ и заинжектить в Haxe (подобно ассемблерным вставкам в си)
— написать С++ классы и потом к ним экстерны (типа биндинги, ака внешняя библиотека).

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

Один из примеров где такой многопортируемый язык может пригодится: Компания пишет проект на С++, продукт набирает популярность и пользуется спросом. Но через годы возникает необходимость перейти на другой язык программирования. Причины могут быть разные: новый директор, новые платформы, новый функционал, или приходят новички которые не понимают разницы между delete и delete[] и херачат баги с утечкой памяти. В общем, принимается решение переписать продукт на ActionScript. А через пару лет приходит указание переписать часть кода на Java, для Android разработки. В подобных случаях Haxe значительно упростит разработку: код уже написан, портируется на разные языки и позволяет собираться под разные платформы, одну и ту же библиотеку можно применить для Android / Windows / Apple / Linux и др. платформ.

Работает: после переписывания кода С++ -> ActionScript было решено переписать на Haxe и остаться на нем.

haxe.Log.trace = haxe.Log.trace;

спасибо, достаточно

Название случайно ассоциируется с «нах»?

Реальная востребованность такого решения выглядит очень сомнительно

Писать сразу под все? Haxe не обязательно использовать как кроссплатформенный язык.
Насколько сомнительно выглядит написание кода на TypeScript под JS?
Haxe точно так же можно использовать как альтернативу другим языкам, в том числе (и особенно) JS, где он делает кодогенерацию практически всегда лучше чем конкуренты.
Вот тут еще есть интересный бенчмарк по JS таргету:
github.com/damoebius/HaxeBench

Ну допустим. А смысл?

Я имею в виду не идеалистический смысл, а практический.
Я не очень верю в то, что этот язык одинаково адекватно переведет нюансы различных языков.
Допустим, джава у нас синхронная, джс асинхронный. Допустим, мне надо нафурычить какойто очень многопоточный тредсейф код для обработки миллиона чегонибудь. В джс это будет один код, в джаве — совершенно другой. Они будут принципиально различаться. Что мне писать на этом «кроссплатформенном» хаксе? В реальных языках содержится миллион инструментов, специфических именно для этого языка. Функционал «кроссязыка» либо А) должен поддерживать все библиотеки всех языков, либо Б) подходит реально только для хеловорлдов типа как в примерах этой статьи.

На джаве мне нужен вебсервис на спрингбуте с джерсей и дата джпа, а на ноде этот же функционал будет реализован ваще по-другому, что здесь может сделать «кроссязык»? Ничего.

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

а с чего бы вам использовать столь разные target? выглядит сферическим конем в вакууме.
<минутка петросянства>
Допустим, у меня есть текст повести на русском. Я хочу перевести его на английский и на Pascal. Как мне translate.google.com может помочь в таком? Откуда он поймет нюансы языка программирования?
< /минутка петросянства>

Что мне писать на этом «кроссплатформенном» хаксе? В реальных языках содержится миллион инструментов, специфических именно для этого языка. Функционал «кроссязыка» либо А) должен поддерживать все библиотеки всех языков, либо Б) подходит реально только для хеловорлдов типа как в примерах этой статьи.

да. #if java помогает.
проблема та же, что и в случае кросс платформенной разработки в рамках даже одного языка.
например, как в Qt реализовано написание кода под Linux и Windows?

Так вот я именно об этом, вы по сути продолжаете мою мысль.
Сам концепт «кроссязыка», учитывая разность целевых платформ, видится мне сферическом конем в вакууме, который вроде как де-юре конь, но ездить на нем практически нельзя.

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

С++ компилируется в разный бинарный код в зависимости от платформы.
bytecode Java компилируется в разный бинарный код.
могу продолжить.

Сам концепт «кроссязыка», учитывая разность целевых платформ

...переносит сложность поддержки разных платформ на разработчиков кроссплатформенных фреймворков и библиотек.

В кроссплатформенной разработке практически всегда есть платформозависимые вещи. Но такие вещи это, навреное, 5-10-20% проекта. Все остальное — логика, которая полностью может быть написана один раз и использоваться везде.
И да, хакс поддерживает бибилиотеки всех языков и может общаться с нативным кодом напрямую.
Я постараюсь раскрыть это в следующей статье.

логика, которая полностью может быть написана один раз

Это так только в простых проектах уровня Cat cat = new Cat(); return cat; Пример — применение хибернейта и декларативного управления транзакциями в спринге непосредственно влияет на структурирование логики, которую вы пишете, если она сложная. Поэтому логика тоже платформозависима.

Вот например, в джаве есть две аннотации @Transactional. Причем нужна именно спринговая, а не ЕЕ. Как мне написать это в хаксе?

Еще вопрос — как дебажить код на хаксе, если он, как таковой, не выполняется?

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

Аннотации, если это то, о чем был вопрос:

Этот код:
@:strict(MyAnnotation({ value :"lock" }))
public static function main():Void {

}

Будет преобразован в такой на Java:
@com.path.to.the.MyAnnotation(value = «lock»)
public static void main()
{
}

Скриншот:
image.ibb.co/dHP25J/image.png

По-поводу дебага Java. Haxe может генерировать исходники, без автоматической компиляции. Открываете их своей IDE и дебажите.

А вот что мешает потом эту логику использовать на JS клиенте

Отсутствие чегото типа hibernate4js например? Существованием транзакций и персистентного контекста в js я вообще никогда не интересовался, но сильно сомневаюсь в их наличии. А даже если они и есть, то я уверен, что работа их совершенно другая.

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

К сожалению, не могу ответить как-то конкретнее, без конкретного примера.

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

Вот код на джаве, который делает то, что нужно:

public Ownership establishOwnership(long personId, long catId)
   Person p = this.personService.getById(personId);
   Cat c = this.catService.getById(catId);
   Ownership ownership = null;
   synchronized( this.lock ) {
         ownership = this.ownershipService.establishBetween(person, cat);
   }   
   runAsync(() -> this.notificationService.notifyOnEstablish(ownership));
   return ownership;
}

напишите, пожалуйста, код на хаксе, который
1) переведется на джаву ровно как в примере,
2) родит код на JS который будет делать ровно тоже самое (с учетом синхронности джавы и асинхронности JS).

Когда вы пишете кроссплатформенное приложение, то архитектура строится так, чтобы логика знала как можно меньше о платформе, а лучше, чтобы не знала совсем. Такие вещи, как synchronized оборачиваются в абстрактный код, который является мостом между логикой и платформой.
Если вы хотите, чтобы Java-specific вещи работали на JS, то это в корне неверный подход к кроссплатформенной разработке. Неважно, Haxe это или нет.

Впрочем, разве это проблема принести синхронность в JS?
Если вы хотите получить async\await на всех платформах, то это можно сделать при помощи сторонней Haxe библиотеки, например tink_await. Либо подождать релиза Haxe 4, где это запланировано из коробки. Либо написать самому. Либо использовать нативные средства.

Но, кстати, я могу написать Haxe код, который сгенерирует точно такой же Java код, как вы попросили, с этим проблем нет. И собирает его под JS (без synchronized, конечно, который java-specific). Я стрелочками там подчеркнул, чтобы легче было найти, куда смотреть:
image.ibb.co/cccTvJ/image.png

И собирает его под JS

я примерно этого и ожидал.
i.imgur.com/J6qOaW1.png
Скажите, с какой вероятность переменные p (line 16) и с (line 17) будут содержать undefined к тому моменту как начнется выполнение line 19, т.к. функции в сервисах еще не успеют выполнится, т.к. эти вызовы асинхронны?
К моменту выполнения строки 19 не гарантируется, что переменные c и р, которые должны быть проинцилизированы в изначальной логике кода, написанного на хаксе, будут проинициализированы возвратом значений из вызванных функций.

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

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

Такие вещи, как synchronized оборачиваются в абстрактный код, который является мостом между логикой и платформой.

Вы совершенно правы. И это подводит нас к мысли о принципиальной абсурдности инструмента которые делает операцию
Java <— %lang% —> JS
Т.к. код на этих платформах пишется принципиально по-разному.
Что я и хочу показать.

Скажите, с какой вероятность переменные p (line 16) и с (line 17) будут содержать undefined к тому моменту как начнется выполнение line 19, т.к. функции в сервисах еще не успеют выполнится, т.к. эти вызовы асинхронны?

Если эти методы асинхронные, то надо просто добавить async\await чтобы стали синхронными. Из условий задачи было неясно. Это совсем не проблема. Синхронный вызов асинхронных методов на всех платформах возможен.

И это подводит нас к мысли о принципиальной абсурдности инструмента которые делает операцию
Java <— %lang% —> JS
Т.к. код на этих платформах пишется принципиально по-разному.

Как я уже писал выше, такие вещи — это 5-10, максимум 20% проекта. Если больше, то это явная проблема в архитектуре.

Более того, Haxe не обязательно должен выступать как кроссплатформенное решение. Можно смело писать под одну платформу и не париться, выбрав его как замену одного из поддерживающих языков.

К моменту выполнения строки 19 не гарантируется, что переменные c и р, которые должны быть проинцилизированы в изначальной логике кода, написанного на хаксе, будут проинициализированы возвратом значений из вызванных функций.

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

Это в следующей статье. Она будет большая и там очень много всего. Постараюсь не тянуть и закончить ее в скором времени.

C++ и С используют там, где для реализаций на других языках или медленно программа работает или памяти не хватает. В других местах он нафиг не упал.

Ещё это может быть удобно для генерации кросс-платформенного кода под мобильные платформы. C/C++ можно собрать как под iOS, так и под Android, особенно если это логика или алгоритмы без привязки к конкретной мобильной платформе.

Підписатись на коментарі