Даниил Коростелёв (@nadako)
Что такое Haxe?
Haxe - язык программирования общего назначения с простым синтаксисом, мощной системой типов и богатыми возможностями метапрограммирования.
class Greeter {
var who:String;
public function new(who) {
this.who = who;
}
public function greet() {
trace('Hello, $who!');
}
}
Главная особенность
Компилятор Haxe ориентирован на генерацию кода для разных рантаймов
Не магия
Парсинг исходника в AST
Типизация AST (проверка и вывод типов)
Анализ и оптимизация AST
Генерация таргет-кода
(js,c++,c#,etc)
Общее для всех целевых платформ
Haxe-way
- Haxе генерирует код максимально близкий к родному для платформы, без лишних врапперов
- Все платформы разные и имеют свои особенности
- Haxe позволяет абстрагироваться над платформами
Чтобы это работало, Haxe минимально специфицирует поведение в рантайме и при этом предоставляет мощные средства для того, чтобы перенести максимум кода в этап компиляции. Дополнительный плюс этого подхода в том что мы получаем на выходе более компактный и производительный код для рантайма.
Отсюда принцип: минимизируйте использование рантайм-рефлекшена и динамической работы с типами.
Киллер-фичи языка
- Переменные!
- Условия!
- Циклы!
- Функции!
- Классы!
- ...и даже интерфейсы!
Алгебраические типы
- Создается ключевым словом enum
- Описывает набор конструкторов значений с параметрами (или без)
- Удобны для описания составных данных, содержащих разный набор данных в зависимости от конструктора
// простейший вид алгебраического типа - привычное многим перечисление
enum ResourceType {
Gold;
Wood;
Iron;
}
// полноценный алгебраический тип
enum Reward {
Experience(amount:Int); // конструктор с одним аргументом
Resource(type:ResourceType, amount:Int); // ... с несколькими
Chest(rewards:Array<Reward>); // с собой же в качестве аргумента
PremiumStatus; // без аргументов
}
Паттерн-матчинг
aka сопоставление по образцу
- Используется в операторе switch
- Гораздо мощнее switch в Си-подобных языках (и не требует break)
- Каждый case описывает шаблон, в котором можно проверить одно или несколько значений из составных объектов
- Кроме проверки, также можно захватывать значения в локальные переменные
- Является основным способом работы с enum, но также работает с любыми объектами и даже массивами.
Механизм для компактного выражения условий ветвления по значению как простых так и сложных данных.
Паттерн-матчинг
switch (httpCode) {
case 200:
processData();
case 404:
handleNotFound();
}
// свитч по полям объекта
switch (player) {
// поздравляем Татьян 15-го уровня
case {level: 15, name: "Tanya"}:
sendCongratulations();
// отправляем подарок игрокам 10-го уровня, причем передавая
// значение isPremium, захваченное в локальную переменную `p`
case {level: 10, isPremium: p}:
giveLevelTenReward(p);
}
Простой switch по константам
Switch по полям объекта
Паттерн-матчинг
function getSellCost(reward:Reward):Int {
switch (reward) {
// премиум статус и опыт не продаётся
// (игнорируем значение Experience с помощью _)
case PremiumStatus | Experience(_):
return 0;
// золото стоит дороже других ресурсов
case Resource(Gold, amount):
return amount * 10;
// другие ресурсы продаются по монете за единицу
case Resource(other, amount):
return amount;
// сундук стоит столько, на сколько в нём лежит наград
case Chest(rewards):
var sum = 0;
for (reward in rewards)
sum += getSellCost(reward);
return sum;
}
}
Switch по enum с захватом значений
Абстрактные типы
- Никак не связаны с понятием абстрактного класса
- Полноценно существуюут только во время компиляции, а в рантайме представлены другим ("подлежащим") типом
- Позволяет создавать абстракции над данными без объектов-врапперов, тем самым избегая лишних аллокаций
- Позволяет определить конкретные типы для конкретных вещей, а так же настроить правила преобразования между ними и даже добавить к ним методы и вычисляемые свойства
Абстрактные типы
abstract UserId(Int) {
function new(value) {
this = value;
}
public static function generate():UserId {
return new UserId(42);
}
}
abstract Money(Int) {
public function new(value) {
this = value;
}
public function format():String {
return 'USD $this';
}
}
class Main {
static function giveMoney(user:UserId, money:Money) {
trace('Giving ${money.format()} to user $user');
}
static function main() {
var user = UserId.generate();
var money = new Money(50);
giveMoney(user, money);
// Ошибка компиляции: Money should be UserId
// giveMoney(money, user);
}
}
пример
Структурная типизация
- Представлена особым видом типов - "анонимными структурами", описывающими только набор полей объекта
- Предоставляет возможность легко создать анонимный объект, даже не описывая его тип
- Как объекты классов так и анонимные объекты могут быть присвоены переменной структурного типа, если их структура (набор полей и их типов) подходит под требования типа
- Хорошо подходит для описания JSON-данных и для быстрого прототипирования
- На статических платформах доступ к полям структур имеет оверхед по сравнению с классами из-за необходимости динамического lookup'а. Это стоит учитывать.
Структурная типизация
пример
// создаём анонимный объект, его тип будет выведен автоматичеки
var person = {name: "Dan", age: 29};
// тип анонимной структуры можно указать и явно
function greet(p:{name:String, age:Int}) {
trace('Hello ${p.name}, I hope you are much younger than ${p.age}!');
}
// необходимые поля присутствуют и их типы соответствуют,
// а значит такой вызов будет валиден
greet(person);
Typedef
- Просто "псевдоним" для типа
- Часто используется с анонимными структурами, чтобы дать им имя и повторно использовать
- Поддерживает параметризацию типа
- Может быть использован для любого типа
// почему бы и нет
typedef Name = String
// имя для анонимной структуры
typedef Person = {
var name:String;
var age:Int;
}
// для тех кто не любит ASCII art
typedef AsyncResult<T> = Promise<Either<Error<String>,Result<T>>>
Extern
- Описывается как haxe-класс с модификатором extern
- Позволяет взаимодействовать с нативным (для целевой платформы) кодом
- Не требует дополнительного кода-клея
- Не требует особого структурирования нативного кода и имеет средства для описания практически любого внешнего кода
- Для некоторых платформ, таких как Flash, C# и Java, компилятор Haxe может сам загружать extern'ы напрямую из байткода (swf, dll, jar)
- Для популярных библиотек и фреймворков есть готовые наборы extern'ов, но даже если для вашей любимой нет - не беда, просто опишите минимальный extern сами и дополняйте по мере надобности
Extern
extern class Buffer {
var length:Int;
function readInt8(offset:Int):Int;
function writeInt8(value:Int, offset:Int):Void;
static function alloc(size:Int):Buffer;
}
@:include("SDL.h")
extern class Sdl {
@:native("SDL_Init")
static function init(flags:Int):Int;
@:native("SDL_CreateWindow")
static function createWindow(title:cpp.ConstCharStar, x:Int, y:Int, w:Int, h:Int, flags:Int):cpp.RawPointer<SdlWindow>;
}
extern class SdlWindow {}
примеры
JavaScript (node.js)
C++ (SDL)
Метапрограммирование
aka макросы
- Haxe-код интерпретируемый во время компиляции
- Имеют доступ к типам и выражениям языка через API компилятора
- Позволяет генерировать код и выполнять дополнительные проверки во время компиляции
Как результат: потенциально более простой и безопасный исходный код, а так же более компактный и быстрый сгенерированный код.
Метапрограммирование
Три вида макросов
- Макро-методы (aka expression macro) - получают на вход выражения, возвращащают выражения, подставляемое на место вызова. Удобно для синтаксического сахара.
- Build-макросы - программно генерируют целые новые типы (class, abstract, enum, typedef). Удобно для создания разного рода врапперов и прокси, а так же генерации типов на основе внешних данных (из файла, из сети и т.п.)
- Init/onGenerate-макросы - макросы, запускаемые в начале или вконце компиляции. Удобно для подготовительных или дополнительных работ (доп.проверки, сбор данных).
Метапрограммирование
В общем...
Макросы - крайне мощная штука, позволяющая автоматизировать много задач и исходный сделать код компактнее и безопаснее.
Однако чтобы её эффективно использовать, нужно хорошо понимать как компилятор работает с кодом.
Кроме того, обилие макросов может наоборот усложнить вашу кодовую базу, ведь макро-код находится в другой области понимания, нежели код который вы пишете непосредственно для вашего приложения. Поэтому, при изучении макросов важно научиться не злоупотреблять ими и применять только там где они действительно нужны.
Оптимизации
- Haxe - оптимизирующий компилятор
- Информация о типах во время компиляции позволяет проводить мощный статический анализ и оптимизацию кода на этапе сборки
Совместно с оптимизациями, производимыми компиляторами и JIT-анализаторами целевых платформ получаем хорошее быстродействие и небольшой размер даже для "тяжелого" кода.
(актуально только если ваш код хорошо типизирован и не злоупотребляет рефлекшеном)
Оптимизации
Что конкретно:
- Инлайнинг функций
- Инлайнинг объектов
- Удаление неиспользуемых полей и классов (dead code elimination)
- Анализ и оптимизация выражений
- const propagation
- copy propagation
- expression fusion
- local dead code elimination
- purity inference
- code motion
Оптимизации
пример-упражнение
class Life {
public var value:Int;
public inline function new(v) {
this.value = v;
}
}
class Main {
inline static function getQuestion(life) {
return {
life: life.value,
universe: life.value + 6,
everything: life.value * 2
};
}
inline static function getAnswer(question, callback) {
var realQuestion = question.everything;
if (realQuestion > 9)
callback(realQuestion + 12);
else
callback(13);
}
static function main() {
var life = new Life(15);
var question = getQuestion(life);
getAnswer(question, function(answer) {
trace('The answer is $answer');
});
}
}
(function () { "use strict";
var Main = function() { };
Main.main = function() {
console.log("The answer is " + 42);
};
Main.main();
})();
Исходный код
Генерируемый код (JS)
Сообщество
- Команда Haxe - люди из разных стран и отраслей IT-отрасли
- Haxe - открытый проект, разрабатываемый через GitHub
- Работают принципы любого open-source проекта:
- нашел проблему - заведи issue
- знаешь как улучшить - создай pull request
- хочешь помочь - добро пожаловать, работа найдётся для всех :)
- Доброжелательное и знающее сообщество - не стесняйтесь задавать вопросы в Google Groups
- Haxe Foundation - организация для коммерческого и стратегического партнёрства с фирмами
Ближайшие планы
- Язык и компилятор Haxe в хорошем состоянии
- Большинство обсуждаемых изменений в языке - ломают совместимость, поэтому до версии 4 вводиться не будут
- Багфиксы, оптимизации и дополнения стандартной библиотеки идут своим чередом
Текущий фокус:
- Инфраструктура
- менеджер библиотек haxelib
- документация (manual, cookbook)
- упрощение поддержки для IDE
- Формализация процессов (contributor guidelines, extension proposals, community voting)
- PR: блоги, конференции, ресурс для поиска работы и людей
Ресурсы
- http://haxe.org/
- http://haxe.org/manual/
- http://haxe.org/blog/
- http://haxe.org/foundation/people.html
- http://code.haxe.org/
- http://try.haxe.org/
- https://github.com/HaxeFoundation/haxe
- https://haxe.io/
- http://groups.google.com/group/haxelang
- #haxe at freenode IRC
официальные и общепризнанные
Интересные либы
Спасибо за внимание!
вопросы?
Про Haxe
By Dan Korostelev
Про Haxe
- 5,169