August 21st, 2013

Book

Требования к языкам, компилирующимся в JS

По просьбе общественности, детализирую требования

Вот мне интересно, есть ли языки, компилирующихся в JS и удовлетворяющие требованиям:

1. человекочитаемый выходной код

- красиво отформатированный (pretty printed, не в одну строчку и с отступами)

- с понятными идентификаторами, передающими смысл программы (в противоположность состоящим из фиксированного префикса и sequence number, вроде a$42, vBA0EF). Эти идентификаторы можно синтезировать из идентификаторов входной заведомо читаемой программы и выполняемых компилятором трансформаций. Например, если у нас была функция fold c локальной переменной х, и компилятор заинлайнил - то x или fold_x в выходном файле всяко лучше x1 или v0233 в том месте, где раньше был х.

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

- не замутненный бойлерплейтом - т.е. если мы компилируем hello world из 1 строчки и на выходе получаем 500 строк, из которых 499 строк - инициализация рантайм-системы, то эта инициализация должна быть отделена от смыслового кода и вынесена в отдельный генерируемый модуль или функцию выходного языка.

2. старый дизайн выходного кода (никаких остающихся на выходе ленивостей, алгебраиков, гигантских говнорантаймов, идиотского кода вроде f.call(null) и хуй-эр-пэ)

Нельзя использовать неидиоматичные конструкции, которые человек никогда бы не написал. Напишу на примере Си т.к. при генерации Си нарушить требование 2 очень просто. В Си могут быть goto и longjump в отдельных местах, но если компилятор использует эти конструкции в генерируемом им Сишном коде повсеместно, то на выходе получаем кашу, с которой человек работать (профилировать, оптимизировать, ловить баги во входном коде путем анализа стектрейсов в выходном) не может. Если я пишу в Хаскеле main = print "foo" >> print "bar", а компилятор из Хаскеля в Си выдает мне что-то отличное от int main (void) { puts("foo"); puts("bar"); return 0; } - например, копипастит в выходной файл библиотеку редукции графов, сборщик мусора, реализацию регулярных выражений и другой "мусор" - то требование 2 не выполняется. Если у нас во входном языке списки - это рекурсивный алгебраический тип, а выходной язык Си - то списки должны транслироваться в struct foo { int data; struct foo *next}; а не во фреймворк симуляции рекурсивных алгебраических типов в Си. Понятно, что при трансляции в JS списки - это массивы, и т.п.

3. новая система типов

Языки, не добавляющие ничего к системе типов Javascript, не интересны. Должна быть или хорошая (как в Эрланге) сильная динамическая типизация, или статическая система типов. Желательно не система типов 50-х годов, как в Обероне, а что-то более совершенное, но это опционально.

4. новый синтаксис

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

5. компактный новый язык

Языки типа Typescript и Coffeescript, сохраняющие тот же развесистый набор примитивов, что в ECMAScript, неинтересны. Язык должен отличаться от джаваскрипта малым размером спецификации и продуманным набором базовых конструкций, которые, с одной стороны, можно идиоматически (с выполнением требований 1-2) транслировать в джаваскрипт, а с другой стороны, сокращают когнитивную нагрузку на программиста при изучении языка (надо учить 2 способа написания цикла вместо 10).

Вот таблица языков из предыдущего поста:

Coffeescript, LiveScript - [1, 2, 4]
TypeScript - [1, 2, 3]
Ur, Fay, Roy, Elm, Haxe, Agda, Idris, Kotlin, F*, FunScript, Websharper - [3, 4, 5]
ClosureScript - [1, 4, 5]
MetaJS - [2, 4, 5]
Erlang JavaScript compiler, Wisp - [1, 2, 4, 5]
OberonJS - [1, 3, 4, 5]
JScala.org - [1, 2, 3, 4, 5]