Comments 13
Манипуляция с DOM, работа с файлами, drag&drop, какие-то специфичные js библиотеки.
Ситуации, когда проще написать на js. Ситуация, когда сторонний проект не с нуля и там js.
Это очень здоровский ход(конём): «Упрощается процесс отслеживания совместимости API между клиентом и сервером». Сам столкнулся с подобным, используются вычисления как на клиенте, так и на сервере, в итоге нужно изменения применять в 2х местах. А данный подход мог бы упростить работу.
Среди примеров можно было бы упомянуть Reason и Kotlin.
Отдельного внимания заслуживает haxe, который тоже обладает полноценной системы типов, хорошим апи для метапрограммирования, но транспилируется не только в js, а еще и в C++, C#, Java, JVM, Python, Lua, PHP, SWF.
пилили на HaXe игрухи, которые потом переваривали в JS ES5, было круто, но дебаг и оптимизация производительности периодически были крайне непростыми. В зависимости от происходящего твой класс на HaXe в 20 срок могло переварить в 50 — 300 строк кода на джаваскрипте-пятёрке
И обратное тоже возможно: благодаря dce из 100 строк получить 20.
и когда после языка высокого уровня нужно было лазить в прототипно-ориентированной каше,
Как раз для js там очень прозрачные трансформации, структурно код очень близкий, просто блок может назначаться через свойство prototype. Основное отличие, которое бросалось в глаза – это for трансформирующийся в while с вынесением объявления счетчика, мне кажется это могло быть основной причиной раздувания.
ES6 на классах сейчас тоже поддерживается через флаг компиляции.
разбираясь, что где течёт или много жрёт — иногда морочились днями-неделями, пока полировали игру перед релизом.
Если искать протечки в своем коде, а не в движке, то обилие таргетов может помочь. Искать безудержно аллоцирующие места можно на любой платформе, выбирая по наличию удобных инструментов. Flash в этом весьма хорош, кстати. Быстрая компиляция, быстрый запуск, отличный дебаггер. HxScout есть, работает и для swf, и для c++.
Ещё нравилась сильная система кастования, возможность делать дженерики
Дженериками сегодня никого не удивишь, ADT это уже интереснее, а аналогов абстрактам в популярных языках я и не назову.
и отдельно запомнилось, что у языка 2 null — для скриптовых языков и для статически-типизируемых и динамически-типизируемых языков.
Я бы сказал немного по-другому: haxe старается не вносить оверхэд на ровном месте, поэтому null на haxe превращается в null на целевом языке, а вот его поведение уже определяется природой целевой платформы.
Жаль, что язык представлен в разработках очень нишево
Вот с этим соглашусь, пиарить его особо не кому, кто знает – тихо использует. Кто не знает – думает, «если он такой хороший, что ж я про него не знал до сих пор» и проходит мимо. Поэтому сложнее набирать команду – это риск для бизнеса, замкнутый круг.
а аналогов абстрактам в популярных языках я и не назову.
Newtype в haskell.
Наследники AnyVal в scala, но есть ограничения. При определенных условиях значение все же может быть завернуто в класс.
1. haxe – мультипарадигменный язык с уклоном в ООП, большая часть прикладного кода пишется в императивном ООП-стиле
2. haxe – мультитаргетный транспилятор, одна из широко используемых возможностей – шаринг кодовой базы между целевыми платформами. Написание прикладного кода так, чтобы он работал на всех (нужных) платформах одинаково и эффективно – задача специфическая, решать ее не всегда требуется. Но если все же – то аналогов я не знаю.
Сейчас попробую пояснить. Действительно, один из способов применения абстрактов в haxe – zero-overhead описание алгебры предметной области. То есть, можно описать, что метры*метры = метры квадратные, складывать их между собой можно, а друг с другом – нельзя.
Scala – пожалуй, единственный язык из известных мне, который имеет похожую абстракцию и позволяет писать в императивном стиле. И то – вопрос еще, что получится при транспиляции в js. Как минимум, раньше она тащила с собой жирнющий рантайм в несколько мегабайт.
А при транспиляции хаскеля в js newtype превращается просто в underlying type?
Как бы то ни было, абстракты в хакси могут быть над любым типом, а не только примитивным, у них явно описывается конструктор с произвольным телом. Кроме этого, можно явно управлять способом транспиляции для каждого метода или перегруженного оператора: если в объявлении не указан инлайн, то сгенерируется хэлпер со статическим методом.
У Haxe есть экстерны для описания типов целевой платформы, абстракты – это отличный способ изолировать особенности «нативных» сущностей с разных платформ, добавить к ним некоторое поведение, не внося новых сущностей в целевой код.
То есть в некотором смысле абстракты еще могут быть и альтернативой миксинам/трейтам.
Хороший пример – реализация сигналов. Абстракт над массивом, этакий аналог (почти) invokation list в пару строк и без внесения лишних сущностей в язык.
Есть, но кодогенерация оставляет желать лучшего. В Scala аналог выглядит так:
import scala.scalajs.js.Array
object types {
type Handler[T] = T => Unit
class Signal[T](val handlers: Array[Handler[T]]) extends AnyVal {
def +=(h: Handler[T]): Unit = handlers += h
def dispatch(t: T): Unit = handlers.foreach(_(t))
}
object Signal {
def apply[T](): Signal[T] = new Signal[T](Array[Handler[T]]())
}
}
object Main {
def main(args: scala.Array[String]): Unit = {
val s = types.Signal[String]()
s += { s => println(s) }
s.dispatch("Hello")
}
}
Параметр handlers
у класса-обертки должен быть val
т.е. неизменяемый. Но в данном случае это ссылка, сам то объект по ссылке может быть мутабельным.
Родной scala/java массив, который мог бы быть транслирован в js массив, использовать нельзя. Потому что в jvm массивы размеры динамически менять не умеют, а выделить новый массив и поменять ссылку мы не можем, потому что ссылка константная.
Если использовать коллекцию какую-нибудь из стандартной библиотеки, тогда ее реализация сгенерируется в js коде. У меня получилось 11к строк. Если же использовать прокси тип к нативном js массиву (как в моем примере), тогда уже 2к строк. Но в таком случае код уже не совместим в jvm.
На самом деле это не проблема, Scala поощряет использование неизменяемых типов, и +=
следовало бы заменить на +
который бы возвращал новый объект Signal
, но я ваш код повторял.
Нагенерировалось вкратце это:
$c_Lcom_habr_Main$.prototype.main__AT__V = (function(args) {
var s = $m_Lcom_habr_types$Signal$().apply__sjs_js_Array();
$m_Lcom_habr_types$Signal$().$plus$eq$extension__sjs_js_Array__F1__V(s, new $c_sjsr_AnonFunction1(((this$1) => ((s$2$2) => {
var s$2 = $as_T(s$2$2);
var this$3 = $m_s_Console$();
var this$4 = this$3.out__Ljava_io_PrintStream();
this$4.java$lang$JSConsoleBasedPrintStream$$printString__T__V((s$2 + "\n"))
}))(this)));
$m_Lcom_habr_types$Signal$().dispatch$extension__sjs_js_Array__O__V(s, "Hello")
});
...
$c_Lcom_habr_types$Signal$.prototype.apply__sjs_js_Array = (function() {
return []
});
Последняя строчка это конструктор, который на самом деле возвращает голый массив. var s
инициализируются массивом, дальше работа идет с голым массивом, про Signal есть лишь упоминание в именах функций.
Если не использовать прокси класс к js массиву, то Signal обертки все равно не будет. Просто вместо голого массива будет использоваться выбранная коллекция.
Учтите, что я ни на scalajs и на js никогда не писал, это в первый раз. Возможно транслятор настройки какие имеет. Глазами я конечно не хотел бы это читать, но source map он генерирует. Но у haxe и в правду js код получился хороший.
Если по-честному, то js-таргет у haxe немного в более привилегированном положении, так как его Sdt (в т.ч. стандартный массив) схож с js-ными по апи, так как исторически был альтернативой as3, который тоже является ECMAScript. Для других таргетов код массива тоже будет включен в сгенерированный.
Родной scala/java массив, который мог бы быть транслирован в js массив, использовать нельзя. Потому что в jvm массивы размеры динамически менять не умеют, а выделить новый массив и поменять ссылку мы не можем, потому что ссылка константная.
Но прелесть haxe как мультитранспилера выражается в том, что мы можем написать
typedef Signal<T> = #if (java||jvm) AbstractOnJLinkedList #else AbstractOnArray #end
а код в абстрактах для подгонки апи будет инлайниться.
+=следовало бы заменить на + который бы возвращал новый объект Signal
Ненене. Haxe изначально очень практичный и прикладной инструмент, а массовый прикладной код сейчас все-таки пишется императивный-ООП-стайл, и я заходил именно с этой стороны. То есть, ожидается, что я могу отдать куда-то ссылку на сигнал, потом в него что-то напихать, а кто-то по ссылке до напиханного достучится.
Хотя в то же время на haxe можно написать и OCaml-стайл портянку паттернматчина по AST, представленного в виде ADT. Собственно, macro апи для метапрограммирования тоже является отличительной фишкой haxe.
Зачем мы транспилируем Haskell в JavaScript