nadako's rants

Вышел Haxe 3.1!

(This is a translation of my original overview to Russian, as requested by Russian Haxe Community. Это перевод моего оригинального обзора на английский по просьбе русского сообщества Haxe).

Новый релиз компилятора Haxe наконец-то вышел в свет, спасибо разработчикам, контрибьюторам и всем сочувствующим (даже я немного помог, ура-ура!)

Итак, давайте посмотрим, что появилось нового, кроме горы багфиксов (особенно по abstract'ам, введенным в 3.0)...

Enum abstracts

Об этой фиче я писал почти сразу как она появилась в гит-репозитории Haxe. Соответствуя своему названию, enum abstract'ы, являются комбинацией enum'ов и abstract'ов и используются для определения набора констант определенного типа, с compile-time поддержкой проверок правильности и полности. Подробнее, вы можете почитать в моем оригинальном посте на английском, но выглядят они как-то так:

@:enum abstract State(Int)
{
    var Idle = 0;
    var Walk = 1;
    var Fire = 2;
}

Переменные, заданные в abstract'е становятся public static inline переменными типа State, который является abstract'ом над Int, как мы указали выше, их можно использовать как State.Idle, или даже просто Idle, если компилятор значет, что выражение должно быть нашего типа State, так же как с enum'ами в Haxe. При этом в run-time, эти значения будут обычными целыми числами, потому что так устроены abstract'ы.

Extractors

Экстракторы (extractor) - интересное нововведение для паттерн-матчинга, они позволяют обработать значение перед сопоставлением с шаблоном внутри case-выражения. Синтаксис следующий: expr => pattern, где expr - произвольное выражение экстрактора, в котором можно использовать оргинальное сопоставляемое значение через идентификатор _. Например:

var s = "hello";
switch (s)
{
    case _.toUpperCase() => "HELLO":
        trace(1);
    case StringTools.urlDecode(_) => "hello":
        trace(2);
    case "a" + _ => "ahello":
        trace(3);
}

Одно из мест, в которых удобно использовать экстракторы - работа с haxe.macro.Type, поскольку эти значения часто имеют объекты-ссылки Ref<T>, которые нужно разыменовать перед дальнейшим сопоставлением, например проверка на то, является ли тип строкой (String) теперь можно записать так:

var a = Context.getLocalType();
switch (a)
{
    case TInst(_.get() => {pack: [], name: "String"}, []):
    case _:
}

EnumValue.match

Это небольшое, но полезное дополнение к enum'ам, позволяющее проверять, соотвествует ли указанное значение enum шаблону. Теперь, вместо такого:

var matches = switch (a) { case A(1): true; default: false; };

Можно просто писать:

var matches = a.match(A(1));

Подключение .NET библиотек напрямую для C#

Теперь вы можете напрямую подключать .NET-библиотеки при компиляции в C#-таргет, так же как это уже делается для Flash и Java. Компилятор парсит файл .NET assembly (DLL-ку), вытаскивая из неё и конвертируя типы в Haxe. Это избавляет от необходимости писать extern-объявления для .NET-библиотек. Делается это через аргумент командной строки -net-lib MyLib.dll. Вот картинка для привлечения внимания:

В дополнение к этой фиче, C#-таргет теперь поддерживает аттрибуты, делегаты и события .NET, а все это нововведения вместе взятые должны сильно улучшить возможности взаимодействия Haxe и C#.

Кроме этих мощнейших нововведений, C#-таргет получил большую долю любви, связанной с тестированием, исправления багов и общей стабилизации.

Generic build

Об этом я так же недавно уже писал. Это новое средство генерации типов через макросы и лично я думаю, что это очень большая фича и что она найдет множество применений (в статье я описываю одно, которое я уже нашел).

Основная идея в том, что вы можете изменить или даже построить новый тип для каждого экземпляра (специфицкации) шаблонного (параметризованного) типа. Вы указываете функцию построения с помощью меты @:genericBuild, так же как с @:build-макросами, но вместо возвращения списка полей для строящегося типа, build-функция возвращает целый тип. И самое главное то, что макрос @:genericBuild будет выполнен для каждой комбинации параметров типа, поэтому вы можете генерировать тип на основе указанных параметров.

Например, это позволяет реализовать @:generic через макросы, как показал в оригинальном пулл-реквесте Simon Krajewski, или заменить MacroType гораздо более красивым синтаксисом, или реализовать константные (read-only) типы, как делаю я. Для подробностей, читайте мой оригинальный пост.

Синтаксис для ECheckType

Те, кто работают с макросами вероятно знают про ECheckType - выражение-обертка, позволяющее проверить, подходит ли данное выражение под указанный тип. Однако, несмотря на то что это выражение, в Haxe не было синтаксиса для него, и его можно было сгенерировать только через макрс. Теперь, для этого появился новый синтаксис: (expr : MyType), где expr - любое выражение, а MyType - указанный тип. Результат такого выражения будет указанного типа, поэтому его можно использовать для типизации Dynamic или конвертации abtract'ов через @:to. Например:

static function getData():Dynamic return "hello";

static function main()
{
    trace((getData() : String).toLowerCase());
}

Типизированное AST

Это дополнение API макросов Haxe. Теперь появился доступ к синтаксическому дереву (AST) после того как оно полностью обработано и типизировано, но до непосредственного вывода сгенерированного (байт)кода. Структура данных для этого - TypedExpr из модуля haxe.macro.Type, который был просто ссылкой 3.0, теперь это структура, похожая на haxe.macro.Expr, но содержащая информацию о типе выражения и имеющая больше смысла с точки зрения структуры программы. Например, вместо работы с идентификаторами, вы работаете с локальными переменными и полями. Это имеет множество применений, например в статическом анализе или генерации новых таргетов, как показал нам Heinz Hölzer в своем проекте hx2python.

Context.getExpectedType

Продолжая тему API макросов, замечу еще одно небольшое дополнение: Context.getExpectedType - функция, возвращающая тип выражения, ожидаемый в месте вызова макроса. Например, она возвращает Int для вот такого вызова макроса: var a:Int = myMacroCall();

Итак...

Релиз Haxe 3.1 выглядит более стабильным чем 3.0, принимая во внимание колиество исправленных багов, а новые фичи безусловно делают язык более выразительным. Хотелось бы выразить особую благодарность Andy Li, который настроил запуск юнит-тестов Haxe на Travis CI, тем самым обеспечив стабильность всему проекту Haxe.