LEARN X · ЗА 15 МИН
Haxe
Экспресс-тур по Haxe за 15 минут: типы, switch с pattern matching, enum, null-safety, дженерики и кросс-компиляция в JS/C++/Python.
Haxe — строго типизированный кроссплатформенный язык, который компилируется в JavaScript, C++, Python, PHP, Java, C#, Lua и другие таргеты. Весь язык — на одной странице через закомментированный код.
Структура программы
Точка входа — статический метод main в классе. Запуск: haxe -main Main --interp или компиляция в нужный таргет.
// Однострочный комментарий
/* Многострочный
комментарий */
/**
* Документирующий комментарий (для генерации документации)
*/
// Каждая программа — это класс с методом main
class Main {
// static — метод принадлежит классу, а не объекту
static function main() {
// trace — встроенный вывод в консоль (с номером строки)
trace("Привет, Haxe!");
// На JS-таргете trace превращается в console.log
}
}
Переменные и типы
Haxe статически типизирован, но умеет выводить типы (type inference).
// var — изменяемая переменная
var age:Int = 25; // целое число
var pi:Float = 3.14; // число с плавающей точкой
var active:Bool = true; // логический тип
var name:String = "Аня"; // строка
// Тип можно не писать — компилятор выведет его сам
var count = 10; // выведется Int
var ratio = 0.5; // выведется Float
// final — неизменяемая переменная (константа)
final MAX = 100;
// MAX = 200; // ошибка компиляции
// Dynamic — отключает проверку типов (использовать редко)
var anything:Dynamic = "строка";
anything = 42; // допустимо
// null допустим только для Null<T> или ссылочных типов
var maybe:Null<Int> = null;
Строки
Одинарные кавычки поддерживают интерполяцию через $var и ${выражение}.
var who = "мир";
var n = 3;
// Интерполяция работает ТОЛЬКО в одинарных кавычках
var greeting = 'Привет, $who!'; // Привет, мир!
var math = 'Сумма: ${1 + 2}'; // Сумма: 3
var repeated = 'n = $n, n*2 = ${n * 2}'; // n = 3, n*2 = 6
// Двойные кавычки НЕ интерполируют
var literal = "Цена: $price"; // так и останется: $price
// Методы строк
var s = "Haxe";
trace(s.length); // 4
trace(s.toUpperCase()); // HAXE
trace(s.toLowerCase()); // haxe
trace(s.charAt(0)); // H
trace(s.indexOf("x")); // 2
trace(s.substr(1, 2)); // ax
trace(s.split("a")); // [H, xe]
trace(" пробел ".split("")); // массив символов
Операторы и условия
switch в Haxe — это полноценный pattern matching, а не просто сравнение.
var a = 10, b = 3;
trace(a + b); // 13
trace(a % b); // 1 (остаток)
trace(a / b); // 3.333... (деление всегда Float)
trace(Std.int(a / b)); // 3 (явное приведение к Int)
// Сравнение и логика: == != < > <= >= && || !
if (a > b) {
trace("a больше");
} else if (a == b) {
trace("равны");
} else {
trace("b больше");
}
// Тернарный оператор
var max = a > b ? a : b;
// switch с pattern matching
var x = 5;
var label = switch (x) {
case 0: "ноль";
case 1 | 2 | 3: "мало"; // несколько значений
case n if (n > 100): "огромно"; // guard-условие
case _: "обычное"; // _ — любое (default)
};
trace(label); // обычное
Циклы
// for всегда работает с итератором или диапазоном
// Диапазон a...b — от a до b-1
for (i in 0...5) {
trace(i); // 0 1 2 3 4
}
// Итерация по массиву (по значениям)
var fruits = ["яблоко", "груша", "слива"];
for (fruit in fruits) {
trace(fruit);
}
// Если нужен индекс — используем key => value
for (index => value in fruits) {
trace('$index: $value');
}
// while и do-while
var i = 0;
while (i < 3) {
trace(i);
i++;
}
do {
trace("выполнится хотя бы раз");
} while (false);
// break и continue работают как обычно
for (i in 0...10) {
if (i == 3) continue;
if (i == 5) break;
trace(i); // 0 1 2 4
}
Массивы и мапы
// Array<T> — типизированный динамический массив
var nums:Array<Int> = [1, 2, 3];
nums.push(4); // [1,2,3,4]
nums.pop(); // удаляет последний -> 4
trace(nums.length); // 3
trace(nums[0]); // 1
nums.unshift(0); // в начало -> [0,1,2,3]
trace(nums.indexOf(2)); // 2
trace(nums.contains(3)); // true
// Функции высшего порядка
var doubled = nums.map(n -> n * 2); // [0,2,4,6]
var evens = nums.filter(n -> n % 2 == 0); // [0,2]
// Map<K, V> — ассоциативный массив
var ages:Map<String, Int> = ["Аня" => 25, "Боб" => 30];
ages["Вера"] = 28; // добавление
trace(ages["Аня"]); // 25
trace(ages.exists("Боб")); // true
ages.remove("Боб");
for (key => val in ages) {
trace('$key -> $val');
}
Функции
// Обычная функция с типами аргументов и возврата
function add(a:Int, b:Int):Int {
return a + b;
}
// Аргументы по умолчанию (помечаются ?)
function greet(name:String, ?greeting:String = "Привет"):String {
return '$greeting, $name!';
}
trace(greet("Аня")); // Привет, Аня!
trace(greet("Боб", "Хей")); // Хей, Боб!
// Стрелочные функции (короткий синтаксис)
var square = (x:Int) -> x * x;
trace(square(5)); // 25
// Замыкание — функция захватывает переменные окружения
function makeCounter() {
var count = 0;
return () -> ++count;
}
var next = makeCounter();
trace(next()); // 1
trace(next()); // 2
// Функции — значения первого класса
var op:Int->Int->Int = add;
trace(op(2, 3)); // 5
Классы и ООП
class Animal {
public var name:String; // публичное свойство
var energy:Int; // приватное по умолчанию
// Конструктор всегда называется new
public function new(name:String) {
this.name = name;
this.energy = 100;
}
public function speak():String {
return '$name издаёт звук';
}
}
// Наследование через extends
class Dog extends Animal {
public function new(name:String) {
super(name); // вызов конструктора родителя
}
// override обязателен при переопределении
override public function speak():String {
return '$name говорит: Гав!';
}
}
var d = new Dog("Рекс");
trace(d.speak()); // Рекс говорит: Гав!
trace(d.name); // Рекс
// Свойства с геттером/сеттером
class Circle {
public var radius:Float;
// (get, never) — только чтение через геттер
public var area(get, never):Float;
public function new(r:Float) { radius = r; }
function get_area():Float return Math.PI * radius * radius;
}
Перечисления (enum)
Haxe-enum — это алгебраические типы: значения могут нести параметры.
// Простой enum
enum Color {
Red;
Green;
Blue;
}
var c = Color.Green;
var hex = switch (c) {
case Red: "#FF0000";
case Green: "#00FF00";
case Blue: "#0000FF";
};
// Параметризованный enum (несёт данные)
enum Shape {
Circle(radius:Float);
Rectangle(w:Float, h:Float);
}
function square(s:Shape):Float {
return switch (s) {
case Circle(r): Math.PI * r * r;
case Rectangle(w, h): w * h; // деструктуризация
};
}
trace(square(Circle(2))); // 12.566...
trace(square(Rectangle(3, 4))); // 12
// Абстрактный enum поверх базового типа (компилируется в Int/String)
enum abstract Direction(Int) {
var North = 0;
var East = 1;
var South = 2;
var West = 3;
}
Null-safety и Option
// Null<T> явно помечает значение, которое может быть null
var maybe:Null<String> = null;
// Безопасная навигация: ?. вернёт null, не упав
var len = maybe?.length; // null, а не ошибка
// Оператор ?? — значение по умолчанию (null-coalescing)
var safe = maybe ?? "по умолчанию";
trace(safe); // по умолчанию
// haxe.ds.Option — типобезопасная альтернатива null
import haxe.ds.Option;
function find(arr:Array<Int>, x:Int):Option<Int> {
var idx = arr.indexOf(x);
return idx >= 0 ? Some(idx) : None;
}
switch (find([1, 2, 3], 2)) {
case Some(i): trace('Найдено на индексе $i');
case None: trace("Не найдено");
}
// Компилятор заставит обработать оба случая
Дженерики (generics)
// Параметр типа <T> делает функцию универсальной
function first<T>(arr:Array<T>):T {
return arr[0];
}
trace(first([1, 2, 3])); // 1 (T = Int)
trace(first(["a", "b"])); // a (T = String)
// Обобщённый класс — пара значений
class Pair<A, B> {
public var left:A;
public var right:B;
public function new(a:A, b:B) {
left = a;
right = b;
}
}
var p = new Pair<String, Int>("возраст", 25);
trace('${p.left} = ${p.right}'); // возраст = 25
// Ограничение типа: T должен быть подтипом
function maxOf<T:Float>(a:T, b:T):T {
return a > b ? a : b;
}
trace(maxOf(3, 7)); // 7
Особенности: кросс-компиляция и abstract
Один и тот же код Haxe компилируется в десятки таргетов.
// Компиляция в разные языки:
// haxe -main Main -js out.js -> JavaScript
// haxe -main Main -cpp out -> C++ (нативный бинарник)
// haxe -main Main -python out.py -> Python
// haxe -main Main -php out -> PHP
// haxe -main Main -java out -> Java
// haxe -main Main -cs out -> C#
// haxe -main Main -lua out.lua -> Lua
// Условная компиляция под конкретный таргет
class Main {
static function main() {
#if js
trace("Это JavaScript-сборка");
#elseif cpp
trace("Это C++-сборка");
#else
trace("Другой таргет");
#end
}
}
// Abstract type — обёртка над существующим типом БЕЗ накладных расходов
// На рантайме это просто Float, но с собственным API
abstract Meters(Float) from Float to Float {
public function new(v:Float) this = v;
// оператор + для сложения метров
@:op(A + B) public function add(other:Meters):Meters;
}
var distance:Meters = 5.0; // from Float — неявное приведение