Pull to refresh

Comments 13

UFO landed and left these words here

Я думаю вам стоит ознакомиться с разделом "Зачем нужна транспиляция в JavaScript". Его, конечно, очень легко было не заметить

Такой подход имеет какой-то процент покрытия по сравнению с чистым JS?
Манипуляция с DOM, работа с файлами, drag&drop, какие-то специфичные js библиотеки.
Ситуации, когда проще написать на js. Ситуация, когда сторонний проект не с нуля и там js.
Это очень здоровский ход(конём): «Упрощается процесс отслеживания совместимости API между клиентом и сервером». Сам столкнулся с подобным, используются вычисления как на клиенте, так и на сервере, в итоге нужно изменения применять в 2х местах. А данный подход мог бы упростить работу.
Если я правильно понял вопрос про покрытие, то все основное уже обтянуто биндингами, а даже если и чего-то нет, то можно сделать тонкую обёртку через JS FFI в GHCJS. В целом нам не приходилось этого делать ни для чего вышеупомянутого — только для подключения библиотек и внешних компонентов. outline.js, например, в каждом первом проекте используется.
Можно было упомянуть F# и Fable — его компилятор в JavaScript. Целый стек для разработки на них: SAFE, Elmish и новоиспеченный Sutil (аналог Svelte). Бандлы JS получаются вполне приемлемых размеров и даже остаются читаемы, есть приличный FFI
К плюсам транспиляции из статически-типизированных языков можно добавить гораздо большие возможности compile-time оптимизаций, в том числе dead code elimination.
Среди примеров можно было бы упомянуть Reason и Kotlin.
Отдельного внимания заслуживает haxe, который тоже обладает полноценной системы типов, хорошим апи для метапрограммирования, но транспилируется не только в js, а еще и в C++, C#, Java, JVM, Python, Lua, PHP, SWF.
пилили на HaXe игрухи, которые потом переваривали в JS ES5, было круто, но дебаг и оптимизация производительности периодически были крайне непростыми. В зависимости от происходящего твой класс на HaXe в 20 строк могло переварить в 50 — 300 строк кода на джаваскрипте-пятёрке и когда после языка высокого уровня нужно было лазить в прототипно-ориентированной каше, разбираясь, что где течёт или много жрёт — иногда морочились днями-неделями, пока полировали игру перед релизом. Ещё нравилась сильная система кастования, возможность делать дженерики и отдельно запомнилось, что у языка 2 null — для скриптовых языков и для статически-типизируемых и динамически-типизируемых языков. Жаль, что язык представлен в разработках очень нишево, хотя всё-таки поддерживать этот слоёный пирог — нетривиальная задача
пилили на 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 в пару строк и без внесения лишних сущностей в язык.
Решил приложить пример в качестве дополнения, там сразу можно посмотреть сгенерированный js. Такому вот есть аналоги?

Есть, но кодогенерация оставляет желать лучшего. В 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.
Only those users with full accounts are able to leave comments. Log in, please.

Information

Founded
Location
Россия
Website
typeable.io
Employees
11–30 employees
Registered