Есть два типа языков, которые являются хорошими целями для компиляторов:
- Языки, семантика которых близко соответствует семантике исходного языка.
- Языки, которые имеют очень низкоуровневую и, следовательно, очень общую семантику (или можно возразить: семантики вообще нет).
Примеры для № 1 включают: компиляцию ECMAScript 2015 в ECMAScript 5 (большинство языковых дополнений были специально разработаны как синтаксический сахар для существующих функций, вам просто нужно их), компиляция CoffeeScript в ECMAScript, компиляция TypeScript в ECMAScript (в основном, после проверки типов просто сотрите типы, и все готово), компиляция байт-код Java в JVM, компиляция C♯ в байт-код CLI CIL, компиляция Python в байт-код CPython, компиляция Python в байт-код PyPy, компиляция Ruby в байт-код YARV, компиляция Ruby в байт-код Rubinius, компиляция ECMAScript в SpiderMonkey байт-код.
Примеры для № 2 включают: машинный код для ЦП общего назначения (RISC тем более), C--, LLVM.
Компиляция Scala to Go не подходит ни для того, ни для другого. Их семантика очень отличается.
Вам нужен либо язык с мощной низкоуровневой семантикой в качестве целевого языка, чтобы вы могли построить свою собственную семантику поверх, либо вам нужен язык с близкой семантикой , чтобы вы могли преобразовать свою собственную семантику в целевой язык.
На самом деле, даже байт-код JVM уже слишком высокоуровневый! У него есть такие конструкции, как классы, которые не соответствуют таким конструкциям, как трейты Scala, поэтому должно быть довольно сложное кодирование трейтов в классы и интерфейсы. Точно так же до invokedynamic
было практически невозможно представить динамическую диспетчеризацию структурных типов в байт-коде JVM. Компилятору Scala пришлось прибегнуть к отражению или, другими словами, преднамеренно выйти за пределы семантики байт-кода JVM (что привело к ужасным потерям производительности при диспетчеризации методов для структурных типов по сравнению с диспетчеризацией методов для других типов). типы классов, хотя оба они одно и то же).
Другим примером являются правильные хвостовые вызовы: мы хотели бы иметь их в Scala, но поскольку байт-код JVM недостаточно мощен, чтобы выражать их без очень сложного сопоставления (по сути, вы должны отказаться от использования вызова JVM стек и управлять своим собственным стеком, что разрушает как производительность, так и совместимость с Java), было решено не иметь их в языке.
У Go есть некоторые из тех же проблем: чтобы реализовать выразительные нелокальные конструкции потока управления Scala, такие как исключения или потоки, нам нужна столь же выразительная нелокальная конструкция потока управления для сопоставления. Для типичных целевых языков эта "выразительная нелокальная конструкция потока управления" является либо продолжением, либо почтенным GOTO
. Go имеет GOTO
, но намеренно ограничивает свою "нелокальность". Для написания кода людьми ограничение выразительных возможностей GOTO
— это хорошо, но для целевого языка компилятора не так уж и много.
Очень вероятно, что можно создать мощный поток управления с помощью горутин и каналов, но сейчас мы уже покидаем удобные рамки простого сопоставления семантики Scala с семантикой Go и начинаем строить высокоуровневую семантику Scala поверх высокоуровневой семантики Go. семантика, не предназначенная для такого использования. Горутины не были разработаны как общая конструкция потока управления для создания других видов потока управления поверх него. Это не то, в чем они хороши!
Так почему же scala-native решили использовать LLVM на таком низком уровне?
Потому что это именно то, для чего LLVM был разработан и в чем он хорош.
В чем была бы загвоздка с транспайлером golang?
Семантика двух языков слишком различна для прямого сопоставления, а семантика Go не предназначена для построения поверх семантики разных языков.
их ГК становится все лучше и лучше
То же самое можно сказать и о Scala-native. Насколько я понимаю, выбор для текущего использования Бёма-Демерса-Вайзера в основном связан с ленью: он есть, он работает, вы можете добавить его в свой код, и он просто сделает свое дело.
Обратите внимание, что изменение GC обсуждается. Существуют и другие GC, разработанные как встраиваемые, а не тесно связанные с макетом объекта виртуальной машины хоста. Например. IBM в настоящее время находится в процессе реструктуризации J9, своей высокопроизводительной JVM, в набор слабо связанных, независимо повторно используемых компонентов «строительных блоков среды выполнения» и выпускает их под разрешительной лицензией с открытым исходным кодом.
Проект называется "Eclipse OMR" (исходный код на GitHub), и он уже готов к работе: реализация IBM J9 на Java 8 была полностью построена из компонентов OMR. Существует проект Ruby + OMR, который демонстрирует, как легко интегрировать компоненты в существующую языковую среду выполнения, поскольку сами компоненты предполагают нет языковой семантики и нет конкретной памяти или расположения объектов. коммит, который заменяет сборщик мусора и добавляет JIT, а профилировщик обрабатывает чуть более 10 000 строк. Он не готов к производству, но загружает и запускает Rails. У них также есть аналогичный проект для CPython (пока не общедоступный).
почему бы просто не транспилировать Scala to Go (а-ля Scala.js)?
Обратите внимание, что у Scala.JS много тех проблем, о которых я упоминал выше. Но они все равно это делают, потому что выгода огромная: вы получаете доступ ко всем веб-браузерам на планете. Для гипотетического Scala.go нет сопоставимого выигрыша.
Есть причина, по которой существуют инициативы по добавлению низкоуровневой семантики в браузер, такие как asm.js и WebAssembly именно потому, что при компиляции языка высокого уровня в другой язык высокого уровня всегда возникает этот "семантический разрыв", который необходимо преодолеть.
На самом деле обратите внимание, что даже для низкоуровневых языков, которые были специально разработаны как цели компиляции для определенного языка, вы все равно можете столкнуться с проблемами. Например. У Java есть дженерики, у байт-кода JVM нет. В Java есть внутренние классы, а в байт-коде JVM — нет. В Java есть анонимные классы, а в байт-коде JVM — нет. Все это должно быть как-то закодировано, и конкретно кодирование (или, скорее, некодирование) дженериков вызвало все виды боли.
person
Jörg W Mittag
schedule
27.05.2016