I started with Haxe a little less than 2 years ago, and was confused at first about how to get a project going. Most tutorials are based around making a game in OpenFL or similar - which is great but when you want to make a bare-bones console app to test things out it’s a bit overkill.
I wish I’d found a tutorial like yours back then, so as a fellow newcomer to Haxe here’s a few things that would have helped me on my way when first starting to learn Haxe. Some are language basics, others are specific things that have helped me work better with Haxe.
Feel free to add, reword, ignore!
VSCode has a great Haxe extension, and one of the commands is Initialize Project
. It builds out a ‘Hello World’ app with the Build task wired up to run it. It is a great way to get straight to trying something out. Just a quick way to do what your ‘getting-started’ page explains.
For more complex projects the HXP library is a fantastic build tool, it does many things that are difficult to do with standard HXML build files. It can be thought of as a pre-build step that can run any Haxe code and compiles HXML files that are then used for the build step. It is an OpenFL project, but OpenFL is not required to use it. I built with HXML for quite a while but eventually ran into trouble handling larger projects - HXP saves some headaches when batching builds to client+server or changing targets/etc.
Haxe does almost everything at compile time instead of run time. This threw me off a bit at first. I noticed private methods becoming public in the output, and methods that required a specific parameter type not being type-checked. Essentially the Haxe compiler says ‘hey, I checked that at compile time, run time checking is slow and I already did the work!’ Which totally makes sense and works great, but only when all the code was compiled from Haxe! Building a library for a target language from Haxe means you have to validate all incoming data, be careful! Some targets (C#/Java) have specific compiler meta flags that make building libraries better/easier, Built-in Compiler Metadata - Haxe - The Cross-platform Toolkit (@:private for example).
Functions can be defined within another scope, this is very similar to how Javascript does things. Coming from a ‘pure’ object oriented (C#/Java) world I love it and hate it at the same time, but it’s just so handy I would never give it up. Quick example:
class Main {
static function main() {
var random:Float = 0;
// Function within a function
function addRandom(limit:Float) {
random+=Math.random()*limit;
}
for(i in 1 ... 5)
addRandom(i);
trace(random);
}
}
For array and map creation the []
init is handy:
var a = new Array<String>();
var a:Array<String> = [];
var m:Map<Int,String> = [];
If you’re trying to incorporate Haxe into an existing project in a different language, it is difficult to get clean libraries on some targets. Class
es and interface
s come through great on most OO pure languages, and simple class
es and anonymous structures come through great on dynamic languages. A quick way to see this is create an anonymous struct, do a few operations on it, and compile out to various targets. They’ll turn out quite different:
// javascript - easy to read!
var t = { obj : "test"};
t.obj = "test2";
console.log("src/Main.hx:5:",t);
//C# - that's not so much fun anymore
object t = new global::haxe.lang.DynamicObject(new int[]{5541879}, new object[]{"test"}, new int[]{}, new double[]{});
string __temp_expr1 = global::haxe.lang.Runtime.toString(global::haxe.lang.Runtime.setField(t, "obj", 5541879, "test2"));
global::haxe.Log.trace.__hx_invoke2_o(default(double), t, default(double), new global::haxe.lang.DynamicObject(new int[]{302979532, 1547539107, 1648581351}, new object[]{"main", "Main", "src/Main.hx"}, new int[]{1981972957}, new double[]{((double) (5) )}));
This is Haxe doing the work at compile time again, it is always consistent internally but making use of the output can be a bit odd. It totally works, and creating a library from Haxe to begin a migration from a previous project is great, but you have to build some kind of interface and validation layer to be clean and safe about it.
Function binding allows new functions from existing ones (Function Bindings - Haxe - The Cross-platform Toolkit). This comes in handy in many situations, just remember functions are first-class. I’ve found this especially handy when dealing with javascript onclick
events: it’s quick job to create some helper methods, bind
them to a specific object, then pass them around or set them to onclick
.
enum
with switch
together are very useful, helping to avoid bugs (Enum matching - Haxe - The Cross-platform Toolkit). Together they force you to capture all combinations of the enum
at compile time. I often use a central ‘enum’ for messaging within the code, by adding or changing the enum
you can get the compiler to find all the places in the code which would break saving quite a bit of searching.
There’s plenty more tips and tricks, but this is getting too long already! Most of the super cool stuff Haxe does are part of other languages as well so I’m sure lots of people out there are saying ‘well MY favorite language already does that!’, but I’ve realized Haxe includes the best parts of every language I’ve used and it can output to many different targets as well, making it much more flexible. With this complexity comes a somewhat steep learning curve, but it has been very worthwhile.