Pull to refresh

Comments 36

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

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

Лично мое мнение, что это препроцессор для множества языков. Ведь несмотря, что его синтаксис можно скомпилировать под множество языков, не зная их api, то есть не зная сам язык, написать ничего не получится. К тому же, его возможности можно считать доисторическими.
А какой язык тогда вместо него предложите?
В зависимости от задачи? мой выбор — ts, c#, f#. Просто когда говорят о подобных языках, то перечисляют только плюсы. Типа — один язык, все платформы. А минусы — нужно знать все языки под которые будешь компилировать. Невозможно написать одно приложение под все платформы будут просадки производительности, тем более трансляторе как haxe. Если на xamarin несмотря наприведение все к единому интерфейсу упорешся разграничивать все в зависимости от платформы, здесь логика и методы будут вообще разные, это рано или поздно сделает проект неповоротливым.

Поэтому в 2019 году нужно выбирать технологии в зависимости от задач. Почти у всех нормальных языков есть кроссплатформенные инструменты.
Не обязательно знать и компилировать под все языки в конкретном случае.
Haxe намного раньше и в чем-то лучше заменяет TS для JS. Некоторые компании его используют в связке с Unity3D и Unreal используя одну кодовую базу для клиента и сервера.
Идея в этом конечно очень крутая, но чтобы реально использовать один код на разных платформах — надо очень хорошо понимать, где язык гарантирует одинаковую работу кода, а где нет. А он может не гарантировать этого даже в самых, на первый взгляд, простых ситуациях.
Мне не известны случаи, когда какой-либо язык не привносил чего-то уникального в тот мир, для которого он создавался. Поэтому унификация всех языков за счет собственных абстракций-языковых конструкций (как например реализация структур данных) сведет на нет все преимущества языков. Есть такое устоявшееся мнение, что писать нужно не на языке, а с использованием языка. Эта фраза была сказана очень давно и на сегодняшний день уже не актуальна, так как с тех времен, многое изменилось, появились огромные корпорации, коммунити, которые разрабатывают-затачивают языки под свои конкретные задачи. Поэтому сейчас к выбору языка подходят также, как к выбору инструмента. Если язык абстрагирует работу, скажем с самым слабым местом, с i\o или же структурами данных, с помощью собственных реализаций, то он он просто нивелирует все плюсы. Поэтому сейчас, если кто-то ещё и держит в голове вышеупомянутую фразу, то её нужно как минимум переместить на другой архитектурный слой, а именно инфраструктурный. То есть, унификация в современном мире должна реализовываться за счет провайдеров\плагинов, в АОП ключе. Но насколько мне известно в haxe, этого нет. К тому же до ts, ему как до звезды. ts на сегодня один из лучших языков. синтаксические возможности haxe, это прошлый-прошлый-прошлый век.

И мне сложно представить в чем смысл писать одну игру на unity и ue одновременно? Это очень похоже на глупость. Почему бы не писать сервер, на тех языках, на которых хочется и связывать это все при помощи микросервисов? Так делают все крутые игровые чуваки. А код на ue и unity разве может быть идентичным? Максимум какие-то утильки, из-за которых думать о выборе haxe, просто дольше, чем написать.
В чем Haxe-у как до звезды TS и какие его возможности прошлый век?
Я не писал, что одна игра и на UE и на Unity3D, это разные игры и разные компании.

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

Слишком громкая и слишком абстрактная фраза.
Если язык абстрагирует работу, скажем с самым слабым местом, с i\o или же структурами данных, с помощью собственных реализаций, то он он просто нивелирует все плюсы.

Вы о том, что какая-то операция на Си у вас займет 0.0003 секунды, а на Haxe 0.0008 секунд? Такого рода оптимизации не нужны для 95% софта. И тонны Electron приложений тому довозательство.
Более того, такие игры как Northgard, Papers please, Dead Cells, Rymdkapsel, Evoland и многие другие, которые написаны на Haxe и работают в том числе и на консолях с вами не согласны. А в играх-то производительность — не последнее слово, ведь так?
Более того, если уж говорить за возможности Haxe, то он позволяет писать платформозависимый код используя нативные конструкции конечного языка, в который он будет транслироваться, включая структуры данных. Включая вставки из этого языка (а-ка ассемблерные вставки в Си). Так что нет никакой проблемы написать узкое место «нативно».

ts на сегодня один из лучших языков

Ну камон :) Крайне спорное заявляение.

К тому же до ts, ему как до звезды.

Haxe компилируется в десяток раз быстрее, а JS код на выходе более оптимизированный, чем у TS за счет соответствующих возможностей: инлайн методы, конструкторы, статический анализатор кода и тд и тп.

синтаксические возможности haxe, это прошлый-прошлый-прошлый век.

У хакса действительно, не так много синтаксического сахара, по сравнению с котлином, например. Но вот про прошлый-прошлый-прошлый век — вообще с потолка :)
Если язык абстрагирует работу, скажем с самым слабым местом, с i\o или же структурами данных, с помощью собственных реализаций, то он он просто нивелирует все плюсы.

Язык предоставляет абстракции для обобщенной работы с I/O и структурами данных на разных платформах. Как один из инструментов.
Кроме этого, он предоставляет много других инструментов: экстерны, абстракты и тайпдефы, которые позволяют писать обобщенный, и в то же время эффективный код.
Из недавнего: писал абстракцию над промисами для шарпов и js. При компиляции в js оно превращалось в работу со встроенными промисами, а на шарпах использовало библиотеку RSG.Promise. АПИ у них отличается принципиально, но в сгенерированном коде нет прослоек, один и тот же хаксовый код превращается на каждой платформе в прямые обращения к собственным платформенным классам.
Почему бы не писать сервер, на тех языках, на которых хочется и связывать это все при помощи микросервисов?

Сам сервер можно писать на чем угодно, но есть большие куски логики, которые бывает очень удобно использовать независимо на клиенте и сервере.
Если их писать на haxe, то даже без микросервисной архитектуры можно использовать эту логику в совершенно разных стеках (В частности, менять стеки, не меняя логику). Мне известны успешные примеры использования haxe c серверной стороны, например, в стеках node.js, java, php.
А код на ue и unity разве может быть идентичным?

Я верю, что можно написать такие прослойки, которые позволят запускать один и тот же код на обоих движках, хоть это будет и не самым эффективным/целесообразным.
Тем не менее, большая часть кода может быть написана с использованием абстракций, что позволит переносить наработки между платформами независимо.
Вот эти ребята использовали оба движка с хаксом
www.youtube.com/watch?v=WKs8QRuMC3k
и в каком-то из докладов я слышал, что чать наработок успешно перекочевала.

Кроме того, есть Kha, который предоставляет абстракции для работы с графическими АПИ, и позволяет писать достаточно низкоуровневые вещи типа шейдеров, корые можно компилировать под OpenGL, DirectX, Metal, Vulkan, WebGl, канвас и т.д (в том числе, для запуска внутри готовых движков типа анрила).
Так делают все крутые игровые чуваки.

Ну тут я даже не знаю, как и возразить. Такая уверенность завораживает.
Всегда нужно выбирать технологии в зависимости от задач.
И в любом случае нужно знать API платформы, для которой выполняешь задачу, кэп.
Когда пишешь на том же C# под Unity или для другой платформы тебе нужно знать API или другой платформы, но это никак на влияет на оценку C#.
на том же C# под Unity
Я могу взять довольно крупный проект на Unity, который сейчас таргетирован на windows, закомментировать платформозависимые куски кода, и скомпилировать под WebGL (asm.js). И работать он будет точно так же как и win-версия, несмотря на то что целевая платформа javascript и я не внес ни строки кода, зависящего от специфичного для платформы api.
Заголовок спойлера
А вот Haxe так не умеет, в чем и было мое самое большое разочарование относительно этого языка.
Haxe так умеет посредством фреймворков OpenFL, NME, Kha
Фреймворки перекрывают API, однако остается тот факт что многие базовые конструкции языка могут вести себя по разному. Например, стандартный Int на некоторых таргетах будет переполняться через 32 бита, а на некоторых — вести себя как float.
Например, стандартный Int на некоторых таргетах будет переполняться через 32 бита, а на некоторых — вести себя как float

Нет, это не так. И никогда так не было. Int будет работать консистентно на всех платформах и уж тем более не будет вести себя как float.
Как раз таки это даже документировано: haxe.org/manual/types-overflow.html
Где-то переполняется, где-то теряет точность на больших числах. И да, можно использовать Int32, но что делать если фреймворк который я использую, внутри использует Int? И опять же, это не единственный фактор, просто первый вспомнившийся пример.
Как раз таки это даже документировано: haxe.org/manual/types-overflow.html

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

Тот проект, который у вас работает отлично, скажем, на C++ таргете, будет 1 в 1 работать и на JS таргете, с таким же поведением Int'a и ничего у вас не переполнится. Аналогично вашему примеру с Юнити.

А если вы в Int пишете значение больше 2^32, не используя для этого Int64 — это уже вопрос к вам.

Я могу спокойно работать с большими Int на js таргете, и наткнуться на проблемы при переносе кода на C++. И наоборот, я могу построить вполне себе нормальный алгоритм, опираясь на то что в C++ int гарантированно и предсказуемо переполняется, и получить проблемы при переносе на js. В приведенном примере с Unity — такого не наблюдается, код на C# скомпилированный в js будет работать именно так как описано в спецификации языка C#.
И, да, я понимаю что это осознанное решение разработчиков языка — пожертвовать универсальностью ради производительности. Но я считаю что это надо более явно и подробно описывать в документации, с большими флагами «ТУТ UNDEFINED BEHAVIOUR!!!».
Я могу спокойно работать с большими Int на js таргете, и наткнуться на проблемы при переносе кода на C++

Вы с этим столкнетесь даже если вручную будете портировать код, потому что нельзя впихнуть больше, чем «обычный» int позволяет. Вам придется использовать для этого другой тип. Например, long в С++.
Я искрине не понимаю, в чем тут принципиальная проблема. Потому что там, где нужен большой инт, там используют для этого специальный тип, объявляющий большой инт.

И наоборот, я могу построить вполне себе нормальный алгоритм, опираясь на то что в C++ int гарантированно и предсказуемо переполняется, и получить проблемы при переносе на js.

Если железно нужен Int32, используйте Int32 и все будет консистентно.
в чем тут принципиальная проблема
Напомню, что эта ветка обсуждения началась с того, что при использовании C# в Unity и компиляции его в js мне ровным счетом ничего не нужно знать про особенности js. А при использовании Haxe — нужно. Неконсистентность Int — всего лишь один из таких факторов.
Любой кроссплатформенный язык имеет как строго описанное в спецификации поведение, так и UB определяемые целевой платформой. В haxe же таких UB очень много (да и спецификации языка как таковой нет).
В целом, я понимаю вашу точку зрения, и знаю что перечисленные мной недостатки (или то что я считаю недостатками) можно обойти. Но для этого надо понимать как сам Haxe, так и целевой язык на довольно высоком уровне.
Я думаю, тут имеется в виду, что в JS вообще нет int, там есть только double.
Пробовал и ts, и haxe. TS для меня проигрывает в большинстве случаев. Если изначально закладываться на определённые платформы для клиента и сервера (а не расчитывать на то что он соберётся под любую платформу), то можно избежать трудностей с несколькими кодовыми базами. А если распробуете макросы в хакс, то наверное уже сложно будет его на что-то променять.
… не зная сам язык, написать ничего не получится.

И да и нет, и не совсем. На TS->JS писать, ведь, ничего никому не мешает. В чем тогда проблема писать на Haxe->JS? Более того, Haxe имеет некоторые преимущества перед конкурентами:
github.com/damoebius/HaxeBench

К тому же, его возможности можно считать доисторическими.

А о каких именно возможностях речь? Синтаксис?
С первого взгляда мне этот язык очень понравился, даже на работе агитировал за то чтобы некоторые новые проекты на нем делать… Но потом столкнулся с тем что:
— он на самом деле не является строго типизированным, строгость типизации полностью зависит от целевой платформы
— в стандартной библиотеке мало документации и много багов
Так что я перестал стремиться его использовать для хоть сколько-то важных проектов, разве что мелкие вспомогательные инструменты иногда на нем пишу.
— он на самом деле не является строго типизированным, строгость типизации полностью зависит от целевой платформы
Полностью? Вы серьёзно? Не могли бы вы раскрыть эту мысль? Как, например, влияет платформа на типизацию на этапе компиляции?
в стандартной библиотеке мало документации и много багов
Можете дополнить это утверждение ссылками на issue c багами в стандартной библиотеке, которые серьёзно осложняют вашу работу?
Как, например, влияет платформа на типизацию на этапе компиляции?
На этапе компиляции есть какая-то проверка типов. Однако никто не запрещает присвоить например Dynamic в Int, даже явный каст не потребуется. На статических таргетах — ошибка выполнения, на динамических — как будто все хорошо. И получается что один и тот же простой код на разных таргетах может дать 3 разных результата.
В моем понимании, «строгая» типизация не должна такого позволять ни в коем случае. Если я не прав, то что же тогда понимается под словами «строго типизированный»?
дополнить это утверждение ссылками на issue c багами в стандартной библиотеке
Давно не писал на haxe ничего сложнее сотни строк, так что вспомню только например вот это — github.com/HaxeFoundation/neko/issues/167. Помню еще, что несколько раз сталкивался с тем что например поддержка ssl — согласно документации, есть, а в коде там одно большое TODO. А окончательно я разочаровался в языке, посмотрев на то как реализован HashMap.
По поводу Dynamic, тут вопрос в качестве кода писавшего — не используйте его и будет все в порядке, а возможность отстрелить себе ногу даст любой язык.
Ситуация была примерно такая: получаю JSON, использую стандартный парсер из стандартной библиотеки, получаю на выходе объект описанного мной класса, все поля строго типизированы — красота!!! (я надеялся что оно работает примерно так же как например gson). А потом внезапно (после пары часов отладки) обнаруживаю, что оказывается в Int поле спокойно можно записать строку. А что именно получится — либо просто строка, либо число получившееся после ее парсинга, либо ошибка выполнения — зависит от таргета.
P.S. В стандартном парсере json тоже баги были, но я сейчас точно не вспомню с чем именно связанные.
Для строгой типизации есть другие варианты, помимо JSON, да и для него должны быть варианты.
Зато haxe позволяет написать 30 строк макроса, который позволяет сгенерировать безопасный десериализатор для всех интовых полей классов, отмеченых метой.
Довольно изящно работая с узлами AST через pattern matching (к слову об устаревшем синтаксисе)
try-haxe.mrcdk.com/#4a425
Вот пример, сам макрос на закладке Source 2, можно посмотреть, как работает на js, и какой код генерирует.
В моем понимании, «строгая» типизация не должна такого позволять ни в коем случае. Если я не прав, то что же тогда понимается под словами «строго типизированный»?

Типизация может быть строгой (сильной), но при этом динамической (как в Python). Может быть статической, но слабой (неявной), как в C++ и C.
Под строгой подразумевается типизция без неявного приведения типов. То есть, чтобы сложить число со строкой, нужно явно сделать из числа строку или наоборот. В питоне нельзя сделать так: a = 1 + '2'
Под нестрогой (слабой) подразумевается типизация с неявным преобразованием типов (да и вообще, с преобразованием типов без создания нового объекта). Например, в С++ есть reinterpret_cast, а в си некоторые численные типы могут неявно быть преобразованы. В другом слаботипизируемом языке возможны вещи вроде 1 + '2', !!1, const a = +'2'

никто не запрещает присвоить например Dynamic в Int, даже явный каст не потребуется
Это очень плохой пример. Мягко говоря. А строго говоря (если уж вы за строгость типизации) это вообще не пример. Интересно, почему для подтверждения слов о строгой типизации вы выбрали именно пример с Dynamic? Это ведь тип специально для тех, кому не нужна типизация, кто хочет от неё отказаться, в конкретном месте, явно.
Это же одно из важных свойств Haxe, что он даёт свободу выбора — нужна свобода и динамика — пожалуйста, использую Dynamic, untyped и т.д., нужна строгость — ок, не используй их и на этапе компиляции всё будет строго, включая выявленные типы.
Ну и конкретно о статической типизации, которую вы видимо имели имели в виду под строгой. Вот подробная таблица по целевым платформам: haxe.org/documentation/introduction/compiler-targets.html где указано, где она поддерживается платформой(!), а где нет. А в контексте нашей с вами дискуссии важна строка под таблицей, которая явно говорит нам что «Haxe code itself is statically typed no matter what target it is compiled to.»
Давно не писал на haxe ничего сложнее сотни строк, так что вспомню только например вот это — github.com/HaxeFoundation/neko/issues/167.
Это ок, понятно, если вашей целевой платформой была neko, то я не удивлён вашим разочарованием. Хотя это уже про выбор платформы, а не язык. И одно из ключевых слов тут, возможно — «давно». Про neko ниже уже немного написали, уточню, что на момент эта платформа уже не актуальна, и будущего у неё нет, есть более современные решения, в том числе от того же разработчика. Последнее не имхо — те, кто следят за прогрессом Haxe, знают, что развивать neko дальше не будут.
почему для подтверждения слов о строгой типизации вы выбрали именно пример с Dynamic?
Просто потому что это самый простой пример который можно привести. Наткнулся я на это, ни разу не используя Dynamic в своем коде (чуть выше комментарий про json).

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

В C# тоже есть dynamic тип и там точно так же можно себе в ногу выстрелить с его помощью.

Помню еще, что несколько раз сталкивался с тем что например поддержка ssl — согласно документации, есть, а в коде там одно большое TODO.

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

Dynamic в Int, даже явный каст не потребуется

Тип Dynamic — это ведь динамический тип, который может оказаться чем угодно в рантайме и риски с его использованием лежат исключительно на плечах программиста. Просто не стоит его использвать там, где ему не место и все. К слову, есть ведь typedef, который позволяет типизировать, на момент компиляции, любой Dynamic, чтобы, случайно, не стрельнуть себе в ногу.

Давно не писал на haxe ничего сложнее сотни строк, так что вспомню только например вот это — github.com/HaxeFoundation/neko/issues/167.

Справедливости ради, Neko — не совсем показатель, ибо через чур эзотерический и потому, даже рядом не стоит с Haxe -> JS, C++, PHP и другими.
риски с его использованием лежат исключительно на плечах программиста
Все знакомые мне строго типизированные языки в таких случаях требуют делать явный каст (и я как программист прописывая явный каст, понимаю что тут есть риск падения). И если тип в рантайме не соответствует ожидаемому — вот он сработавший риск, ошибка выполнения.
TypeScript — строго типизированный или нет? Если да — то что там делает any, которое приводится в любой тип без явного каста?
Sign up to leave a comment.

Articles

Change theme settings