Note
Source does not longer available. Also, some things can be outdated.
- Brief Haxe Tutorial
- Language Overview
- Pros and Cons of Haxe
- Languages Comparable to Haxe
- Setup for this Tutorial
- The Language
- Maps
- Libraries, Packages, and Modules
- Using the Standard Library
- Static Extension
- Using Libraries from Haxelib
- Example: CSV files
- Example 2: Set Type
--class-path
vs--library
- Documenting Your Library
- Recommended Libraries
- Links
- What’s Next?
- TODO
This is a concise, fast-paced, limited tutorial covering just enough Haxe to get you productive right away.
Haxe is a modern, general-purpose, high-level, statically-typed, object-oriented programming language with a familiar syntax. It can be compiled to native, to VM bytecode (its own HashLink VM, or to the JVM), to numerous target programming languages1, or can run code using its own built-in interpreter.
For a short sample of some Haxe code, visit haxe.org and scroll down.
It’s assumed here that you’re already familiar with some other JavaScript-/Java-/ActionScript3-style language.
In the text below where I write, “${feature} works just as you’d expect it ought to”, that’s intended to mean that Haxe does just what you’d think a sensible, block-structured, lexically-scoped, modern language would do. :)
This document covers Haxe version 4. If you don’t already have it installed, and you’re on GNU/Linux, you might have a look at my short getting-started guide.
Some characteristics of Haxe:
- statically-typed, with type inferencing
- garbage-collected
- block-structured, lexically-scoped
- syntax is your garden-variety C-style (JavaScript-style) curlies and semis
- OOP support (functions go in classes)
- first-class functions
- everything is an expression (ex.
if
/else
,switch
/case
, etc. — more about this below) - consistent, sensible, modern programming language without excessive syntactic sugar
- supports advanced features (not (yet) covered in this tutorial) such as abstracts, pattern matching, and macros
See also the official Language Introduction.
- Haxe can target a lot of different platforms, including its very own HashLink VM.
- compiled, and statically-typed, so Haxe helps you find your mistakes as soon as possible (preferably at compile-time)
- You can use Haxe within ${other-language} projects, as long as Haxe can target that other language or platform — which it usually can.
- You can easily make use of libraries from the language your Haxe project targets2.
- The Haxe library repository provides a nice catalog of community-provided Haxe libraries installable via the handy and standard
haxelib
tool. - active and supportive community (see Haxe community forum)
- nice manual, API docs, code cookbook, website (with Haxe videos section), and blog
- good support for a handful of IDE’s3 via plug-ins
- lots of support for game development, if that’s your interest
- free-software licensed (GPLv2+ for compiler, MIT for std lib and HashLink), community-focused, and not beholden to any particular corporate interests
- fast and efficient executables for natively compiled targets
- the compiler is very quick
- probably the easiest migration path from Flash / ActionScript 3
- Support plans are available from the Haxe Foundation if your company desires paid support.
- docs could use more examples, and in some areas, more content
- would like to see more users blogging about their use of Haxe
Haxe first appeared in 2006. The following are a handful languages (roughly in order by date-first-appeared) that are comparable to Haxe (that is, being statically-typed, GC’d, OO, “application-level”, modern languages with familiar syntax):
Language | First Appeared | License | Comments |
---|---|---|---|
Dart | 2011 | BSD | From Google |
Kotlin | 2011 | Apache 2.0 | From JetBrains |
TypeScript | 2012 | Apache 2.0 | From Microsoft |
Swift | 2014 | Apache 2.0 | From Apple |
ReasonML | 2016 | MIT | From Facebook |
Note
Two observations:
- The above list of languages are all open source, while the Haxe compiler is free software. Read more about the differences between open source and free software.
- The evolution of each of the above languages is determined by its owner’s corporate interests. I’m delighted to note that Haxe is free from such influence.
This tutorial was written using Haxe 4 on GNU/Linux, utilizing haxe
’s built-in interpreter (“interp”, aka “eval”). To install Haxe, see my getting started doc.
Note
To get set up targetting Haxe’s own HashLink VM, see my getting started with HashLink doc. If you’d like to target some other environment, visit the Haxe introduction and follow the link buttons under “Setup your development environment”.
Create and populate your project directory:
mkdir my-proj
cd my-proj
touch interp.hxml
mkdir src
touch src/Main.hx
with src/Main.hx containing:
class Main {
public static function main() {
trace("Hello, World!");
}
}
and interp.hxml containing:
-p src
-m Main
--interp
Run your code on the command line from the project directory like so:
haxe interp.hxml
Note
Note that when not using interp (Haxe’s built-in interpreter) — that is, when compiling to some target platform — you’d usually have to type two separate commands:
- a
haxe
invocation to compile/build your program, and then - a target-specific command to run or otherwise use the output that
haxe
just produced.
Using interp (aka eval), it’s just the one step though.
For invoking the haxe
command, rather than type out the necessary command line options every time, you can instead put those options into a .hxml (“hax em ell”) file. For example, instead of typing:
haxe -p src -m Main --some-target whatev
you can instead create a target-specific some-target.hxml file containing those options,
-p src
-m Main
--some-target whatev
and then type haxe some-target.hxml
to get the same result.
If you build your project for multiple different targets, you’ll likely have multiple hxml files — one for each target (ex., interp.hxml, hl.hxml, lua.hxml, etc.), or maybe one for each domain (ex., desktop.hxml, mobile.hxml, web.hxml).
hxml files can contain comments, which start with #
.
// single-line comment
/*
A multi-line
comment.
*/
/* Some people like to write
* them with asterisks on the
* left-hand-side.
*/
/**
A multi-line _doc_ comment.
Indent however you like.
*/
Haxe is not whitespace-sensitive.
Haxe is case-sensitive. Variable names are often written camelCase, with types capitalized LikeThis.
null
is used to indicate when a variable refers to no object.
Void
is used to indicate the absence of type (ex. when used as the return type of a function).
The so-called “basic types” are Bool, Int, and Float, with literals like:
true, false
1, 0xffff
1.2, -3.4, 1e3
Note
On HashLink, Ints are 32-bit signed (so, -2,147,483,648 → 2,147,483,647, and they wrap), and Floats are 64-bit.
Strings are immutable and Unicode. Not a “basic type”, but there’s a built-in literal syntax for strings, and string interpolation is supported:
"a string" // double-quoted (no interpolation)
'allows ${foo} interpolation' // single-quoted
"concat" + "enate strings"
"both single- and double-quoted strings
can be written across multiple lines and
both can contain escapes like \t and \n."
See the String API docs for full list of String fields (ex. length
, charAt()
, charCodeAt()
, indexOf()
, split()
, …).
You use var
or final
to define variables. Variables are typed. Thanks to type inferencing, you don’t often need to explicitly specify the type of a variable:
var x = 3.2;
var a = 5, b = 6, c = 7;
final n = 160; // A constant.
final
variables can’t be reassigned to another object, but they can refer to a mutable object (which could itself be modified).
On HashLink (and other statically-typed targets):
var x = 5;
x = null; // ERROR, `null` can't be used as a basic type.
When types cannot be inferred you must include them explicitly. Type names come after the variable name and colon, for example:
// (The type can be inferred here, but to show the syntax...)
var x:Int = 5;
// You could put in extra spaces if you want, but that's not customary.
var x : Int = 5;
To see the type of any variable, use $type(x);
which will print out a warning line to the console at compile-time indicating x
’s type.
A class defines a new type, and typically goes into its own like-named file.
Classes have fields which may be variable fields or function fields (aka methods). Fields also may be member (aka, non-static, aka instance) or static. Summarizing:
instance | static | |
---|---|---|
variable: | member variable | static variable |
function: | (member) method | static method |
Note
Terminology: class fields that are functions (that is, functions belonging to a class) are referred to as “methods”. In Haxe you can create local functions as well, but those aren’t considered methods.
By default, all fields are private and member.
As an example, from your project directory make a src/my_pkg/MyClass.hx file:
package my_pkg;
// Classes are public by default.
class MyClass {
var memberVariable = 3; // private member variable
static var staticVariable = "yo";
// Constructor must be public.
public function new(n) {
trace("Hi from MyClass ctor!", n);
this.memberVariable = n;
}
public function incr() {
trace("Incrementing our member variable.");
this.memberVariable++;
}
public function show() {
trace("member variable's value:", this.memberVariable);
trace("static variable's value:", staticVariable);
}
public static function staticMethod() {
trace("Hi from MyClass.staticMethod()!");
}
}
and use it from Main.hx:
import my_pkg.MyClass;
class Main {
public static function main() {
MyClass.staticMethod();
var mc = new MyClass(9);
mc.incr();
mc.show();
}
}
In Haxe, functions are defined within classes (or within other functions). You’ve seen examples above.
Function args can have default values.
// Here, `x` is an Int. On static platforms (like HashLink),
// you may not pass in `null` as a value for basic types.
public function foo(x = 7) {...}
public function foo(x:Int = 7) {...} // same
// Function args can be made optional. If omitted, gets assigned
// `null` (`Null<Int>` on static platforms).
public function foo(?x:Int) {...}
// Optional function arg with default value. You can pass in `null`
// on static platforms (`x` is `Null<Int>` "nullable").
public function foo(?x = 7) {...}
When necessary you can explicitly specify return type:
function foo() : String {...}
function bar(x:Int) : Array<Int> {...}
You return values with a return
statement.
var x = 5;
var y = x;
y++; // `y` is now 6
trace(x); // `x` is still 5
// Similarly for Strings, as they're immutable.
// But for other objects:
var w = new Whatev();
var v = w; // Both `w` and `v` refer to the same instance of Whatev.
w.mutateIt();
// The object to which `v` refers is mutated too, of course.
var a = [5, 66, 7];
a[1]; //=> 66
a[1] = 6; // [5, 6, 7]
a.push(8); // [5, 6, 7, 8]
var x = a.pop(); //=> 8, and it's removed from `a`
a.unshift(4); // prepends the 4: [4, 5, 6, 7, 8]
a.shift(); //=> 4, and leaves `a` as `[5, 6, 7, 8]`.
a.insert(idx, 99); // Inserts 99 into `a` at `a[idx]`.
a.remove(99); // Removes first occurrence of 99.
// Removes `len` elements from an array, starting at position `pos`.
var x = a.splice(pos, len);
// So,
// * add stuff with: push, unshift, and insert:
// "push/unshift onto", "insert into"
// * remove stuff with: pop, shift, splice, and remove:
// "pop/shift off", "splice out of", "remove from"
a.length; // the length of the array
a[0]; // first elem of the array
a[a.length-1]; // last elem of the array
// Print an array:
trace(a);
// Another way:
Sys.println(a);
// Another way, to see how the sausage is made:
trace(haxe.Json.stringify(a));
// Array comprehension (creates an array)
var a = [for (i in 5...10) i];
// sort an array, in-place
a.sort(Reflect.compare);
// or, if you want to write your own comparison function:
a.sort((a, b) -> a - b) // (works for sorting numbers only)
// or, more generally:
a.sort(
(a, b) -> {
if (a < b) return -1;
else if (a > b) return 1;
else return 0;
}
);
// Note, Haxe doesn't do array index bounds checking:
a = [5, 6, 7];
a[3]; //=> 0 (same result for an array of floats)
a = [true, true, true];
a[3]; //=> false
a = ["i", "j", "k"];
a[3]; //=> null
See the arrays article of the code cookbook for more examples.
See also the Array API docs.
var m = ["a" => 1, "b" => 2];
// Note though how it's formatted with curlies when printed out (as of Haxe 4.0.5):
trace(m); // {b => 2, a => 1}
m["a"]; //=> 1
m["c"] = 3; // sets a key/value pair.
// Note, not bounds-checked:
m["d"]; //=> null
// Map comprehension.
var m = [for (i in 5...8) i => '*${i}*'];
// If you really need to know the length of one...
Lambda.count(m);
See the maps article in the code cookbook for more examples.
See also the Map API docs.
Haxe doesn’t come with a Set type out of the box, but you can use the thx.Set type from the thx.core library, as described later in the section about using libraries from the Haxe library repo.
Usage looks like:
var s = Set.createInt(); // a set of Ints
s.add(5); // returns `true` if `s` is changed
s.add(12);
s.add(5); // no effect (returns `false`)
s.add(7);
s.length; // 3
See the thx.Set API docs.
If you want to create a new empty Array or Map, you can use the new
keyword and also specify what type the Array or Map will contain. The syntax for that is:
// A new empty array of Strings.
var a = new Array<String>(); // though, you can also do:
var a:Array<String> = []; // and get the same thing.
a.push("aa");
a.push("bb");
// A new Map where the keys are Strings and the values are Ints.
var m = new Map<String, Int>(); // though, you can also do:
var m:Map<String, Int> = []; // and get the same thing.
m["a"] = 1;
m["b"] = 2;
The types written in the angle brackets are the type parameters.
Built-in support for enums:
enum Size {
Small;
Medium;
Large;
}
var s = Size.Small; // Ok
var s = Small; // Ok too
var s = Size.XLarge; // Error
var s = XLarge; // Error
But they can do more than that. See:
If you just need a simple structure to group heterogeneous data (recall, Maps’ values must be all of the same type), and you don’t need a class, you might use an anonymous structure:
var p1 = {n: 10, name: "Ron"};
var p2 = {n: 12, name: "Tammy 1"};
var p3 = {n: 15, name: "Tammy 2"};
p1.n; //=> 10
p2.name; //=> Tammy 1
They can contain and be nested in other data structures, as you’d expect.
Don’t confuse anonymous structures with maps even though they print out similarly in the terminal:
var m = ["a" => 1, "b" => 2];
// prints as {b => 2, a => 1} <-- curlies!
// type is haxe.ds.Map<String, Int>
var x = {a: 1, b: 2}; // This is an anon struct, not a map!
// prints as {a : 1, b : 2}
// type is {b : Int, a : Int}
(Well, and if you’re familiar with Python: Haxe anon structs look very similar to Python maps/dicts.)
Note
Note that usually in Haxe when you see a :
there’s a type after it — not so for anon structs though. My advice: when writing types after a variable, omit any extra spaces. When writing anon structs, put that space after the colon. I believe the Haxe code formatter does this as well.
To avoid repetition you can use typedef
:
typedef SomeType = {n:Int, name:String};
// or
typedef SomeType = {
var n:Int;
var name:String;
};
Note, that typedef cannot be declared inside a class.
For more info, see the anon structs manual chapter.
Haxe has the usual operators you’d expect.
It also supports both pre- and post- increment/decrement, and they work like you’d expect.
Haxe supports the ternary operator (foo ? bar : baz
), but you can often nearly as easily use if
/else
as an expression instead:
var x = someCond ? "this" : "that";
// or
var x = if (someCond) "this" else "that";
Division of numbers always gets you a Float.
Logical “and”, “or”, and “not” are spelled &&
, ||
, !
. These and also the comparison operators (<
, >
, etc.) result in a Bool.
&&
and ||
short-circuit, like you’d expect.
All the usual compound assignment operators (like +=
, *=
, etc.) are supported.
Haxe also supports the range operator, 5 ... 8
, which gets you 5, 6, 7.
There’s no operator for exponents; explicitly use Math.pow()
.
Haxe supports the usual flow control statements, most (all?) of which are actually expressions in Haxe:
// Loop over an array.
for (e in someArray) { /*...*/ }
for (i in 0...someArray.length) { /*... someArray[i] ...*/}
for (i in 0...5) { /* 0 to 4 */ } // or
for (_ in 0...5) { /* if you don't care about that idx value */ }
// You can use `break` and `continue` while looping.
// Loop over a map.
for (k => v in someMap) { /*...*/ }
for (v in someMap) { /* just the values */ }
for (k in someMap.keys()) { /* just the keys */ }
while (someCond) { /*...*/ }
do {
//...
} while (someCond);
Flow control statements that expect a Bool must get a Bool (for example, as returned by the equality and comparison operators); there’s no notion of “truthiness” and “falsiness” like in scripting languages.
if (someCond) someExpr;
if (someCond) { /*...*/ }
else if (otherCond) { /*...*/ }
else if (yetAnother) { /*...*/ }
else { /*...*/ }
Note, since blocks evaluate to an expression, if you only have one expression in the if
body anyway, you can do:
if (someCond) someThing
else if (otherCond) somethingElse
else if (yetAnother) somethingOther
else thatsIt;
Switch:
switch x {
case 1: trace("x was 1!");
case 2: trace("x was 2!");
default: trace("Hm...");
}
There’s much more to the switch statement though, since it supports pattern matching.
Scoping is lexical, and works just as you’d expect it ought to.
Further, you can create a block with its own local scope, and it works like you’d expect:
var x = 3;
{
var x = 18; // new local, shadows outer `x`
var y = 5; // new local
trace(x); // 18
trace(y); // 5
}
trace(x); // 3
trace(y); // ERROR, `y` out of scope here
The value of the block is the value of its last expression.
var x = {
trace("hi");
trace("bye");
7;
};
trace(x); // 7
Loop variables are scoped to inside the loop body:
for (i in 5 ... 8) {
trace(i);
}
trace(i); // ERROR, `i` is out of scope here
Haxe has built-in support for and literal syntax for regexes (ex. var rx = ~/[a-f]+/;
). For more info, see:
Haxe has try/catch and throw for exception handling. See the try/catch manual chapter for more info.
Haxe packages correspond to directories, and Haxe modules correspond to .hx files. Package names (and their corresponding directory names) are lowercase and may contain underscores. Module names (and their corresponding filenames) are capitalized and CamelCase. Modules contain types, and type names are also written CamelCase.
Take for example the file foo/bar/Baz.hx containing class Baz. The module Baz resides in package foo.bar (and at the top of Baz.hx it says package foo.bar;
). The fully-qualified module name is foo.bar.Baz. The fully-qualified typename for class Baz is foo.bar.Baz.Baz. In general, you access the types in a module like so:
{pkg-name}.{module-name}.{type-name}
However, for that special case where module name == type name, Haxe allows you the shortcut of writing foo.bar.Baz
to mean foo.bar.Baz.Baz
.
Haxe finds modules by searching its classpath (see the --class-path|-p
compiler option), and also by looking where --library|-L
specifies.
Importing a module allows you to use its unqualified module name (without its package name) in your code. You could go ahead and use modules without importing them, but then you’d have to fully-qualify them with their package name every time you use them.
import foo.bar.Baz;
// then access `foo.bar.Baz` as just `Baz`.
If your module contains more than one type, any types named differently than the module name are called “sub-types”. If you import a module containing subtypes, they’ll be available unqualified just like the type named after the module. That is to say, when you import a module, you get all of its types.
Libraries are archive files you download from the online Haxelib library repository using the haxelib
tool. They contain modules residing within packages.
Alas, because many types of archive files are sometimes referred to as “packages”, the term “package” is sometimes casually used as a synonym for “library”.
Using the standard library (documented at https://api.haxe.org/) doesn’t require any special imports or compiler arguments.
See the Haxe code cookbook for examples using the standard libary, though here’s a couple of choice tidbits anyway.
var s = Std.string(whatev); // Whatev to String, or
var s = '${whatev}'; // this. There's also a
var s = '$whatev'; // shorthand for simple case.
var n = Std.int(5.8); // Float to Int, but truncates (so, 5)
var n = Math.round(5.8); // Float to Int (so, 6)
var x = Math.fround(5.8); // Float to Float (so, 6.0)
var x = n * 1.0; // Int to Float :)
var n = Std.parseInt("5"); // String to Int
var n = Std.parseInt("05"); // Same. Leading zeros are ignored.
var n = Std.parseInt("5.7"); // String to Int, and truncates
var x = Std.parseFloat("5.2"); // String to Float
// Note that `Std.parseInt("")` ==> null
// and `Std.parseFloat("")` ==> NaN
var r1 = Math.random(); // Float, 0 to < 1
var r2 = Std.random(n); // Int, 0 to < n
Reading from stdin and writing to stdout and stderr.
You can read from stdin interactively from the command line. You can also pipe input to your Haxe program just as you would with any other command line utility.
To read in one line:
Sys.println("Enter your name:");
var ans = Sys.stdin().readLine();
// `ans` is just the text --- no newline
If you want to iteratively read in lines:
var line:String;
var lines:Array<String> = [];
try {
while (true) {
line = Sys.stdin().readLine(); // a String, no newline
lines.push(line);
}
}
catch (e:haxe.io.Eof) {
trace("done!");
}
You could also read in all the input in one shot:
var content = Sys.stdin().readAll().toString();
There’s a few ways to write to stdout:
trace("Hello, trace!"); // Provides filename, line number, and a newline.
Sys.println("Hello, println!"); // Provides a newline.
Sys.print("Hello, print!"); // No added newline.
You can also use Sys.stdout()
to grab the stdout object and call its write methods (see haxe.io.Output).
To write to stderr:
Sys.stderr().writeString("Yow!\n");
Sys.args(); //=> array of args passed (Strings)
Sys.exit(0);
Haxe can do a special trick, strictly for convenience: If there’s a Type (typically not one you’ve written yourself; call it, say, SomeClass
) to which you’d like to add member functions, you can:
- Create a SomeStaticExtClass containing static methods which you wish SomeClass had as member functions. Those static methods should take an instance of SomeClass as their first argument.
- Back in your Main.hx, instead of
import some_pkg.SomeStaticExtClass
, dousing some_pkg.SomeStaticExtClass
. This makes the magic happen. - Now when you make these wished-for method calls on instances of SomeClass, Haxe will behind-the-scenes translate them into calls to those static methods with the instance passed in as the first argument.
A few modules in the standard library were written with this usage in mind. See the static extension chapter of the manual.
The online repository of Haxe libraries and also the library management command line tool are both called “haxelib”. When you install the binary release of Haxe onto GNU/Linux, it comes with both haxe
(the compiler) and haxelib
(the library management tool). See also haxelib --help
.
Libraries you install via haxelib are often (though not always) named lowercase with dashes where necessary. They are sometimes named using dots instead of dashes.
Note
Note that the name of a library does not necessarily have anything to do with the packages and modules it contains. For example, the “thx.core” library contains modules in the thx package (there’s no “thx.core” package).
Unfortunately, if you name libraries with dots they tend to look like package names, which can be confusing. I think using dashes makes more sense.
A library archive file itself is a .zip file, and its haxelib.json file is the library’s config file.
By default, haxelib
installs and unpacks libraries into your ~/haxelib directory. This is your local haxelib-installed libraries repo. Haxelib can also manage project-specific repos.
To use a library from the haxelib library repository:
- Install the library you want, ex.:
haxelib install cool-stuff
- Into your .hxml build file add a line like
--library cool-stuff
- In your code, you can now use that lib’s fully-qualified typenames (or, if you’d rather not have to fully-qualify names, import modules from the library).
Examples:
haxelib list # to see the list of all packages installed
haxelib update # to update all packages
It’s often very useful to install packages directly from their git repo, rather than waiting for their lib.haxelib.org package to be updated. To do that, for example, for the thx.core library:
haxelib git thx.core https://github.com/fponticelli/thx.core.git
(see docs for haxelib git)
If you’re working on a library on your own system, which you’d like to install (locally, into your ~/haxelib) straight from its project directory and without having to first publish it to lib.haxe.org, you can do:
haxelib dev my-proj ~/path/to/my-proj
For more info, see the haxelib docs.
To work with csv files, try the thx.csv module. Install the lib:
haxelib install thx.csv # or,
haxelib git thx.csv https://github.com/fponticelli/thx.csv.git
To your .hxml file, add the line --library thx.csv
In your code, import thx.csv.Csv
and use this module like so:
// Read in the csv content as a string.
var s = "Size,Color,Shape
3,green,round
5,red,square";
// Gets you an array of array of strings.
var aoa = Csv.decode(s);
trace(aoa);
Install haxelib install thx.core
, and import thx.Set
. See Sets above for usage.
Note the difference between the two following haxe
compiler options:
-
--class-path|-p
This specifies any additional directories where the compiler is to look for classes (the classpath).
-
--library|-L
This specifies a particular library (typically in your ~/haxelib) that you want included into your build.
Note, you don’t add the path to the Haxe standard library to your classpath — that’s what the $HAXE_STD_PATH environment variable is for. For example, with my installation the Haxe standard library is located in ~/opt/haxe/std, and so in my ~/.bashrc I have:
export HAXE_STD_PATH="$HOME/opt/haxe/std"
To generate HTML API docs, use dox.
It works like this:
-
document your libraries with doc comments like
/** ... */
. -
to be continued …
For coloring output in the terminal, try Console.hx. Super easy to use.
todo
One popular testing library is munit.
See also discussion on other options.
Docs:
- the Haxe manual.
- for example code, see the Haxe Code Cookbook.
- for the standard library docs, see the Haxe API docs.
Misc:
- Haxe Roundups — current Haxe news
- Haxe at StackOverflow
- HashLink, the Haxe VM, aka “HL”
- Haxe weekly roundups
- Try Haxe online
- Handy dependency info tool
HaxeUI (GUI toolkit):
More links:
- See travix, for automated building on multi and varied targets.
- hxnew for quickly creating a new empty starter project.
- tink_web
- thx.core
- format — read and write different file formats
- Start a fun project.
- Go read the Haxe Manual to learn about all the more advanced stuff not covered in this tutorial!
- Peruse the Haxe code examples cookbook.
- Keep the Haxe API docs handy.
- Enjoy, and see you at the forum!
- externs
- abstracts
- metadata (
@foo
and@:bar
) - more about enum, ADT, and pattern matching
- class properties
Footnotes
-
See the full list of Haxe compiler targets. ↩
-
Via externs, which are currently out of the scope of this tutorial. ↩
-
See Editors and IDEs. ↩