From AS3 to Haxe

2013-01-02

I recently converted a codebase of about 5000 lines from ActionScript 3 to [Haxe](http://haxe.org). Here's what I learned. ## Initial impressions: 1. Haxe compiles *really* fast. I see compile times from 0.1 to 1 second - usually 0.1. As a comparison, the same project used to see compile times from 2-15 seconds in AS3. This is great when you're testing out lots of small changes rapidly. 3. Autocompletion is built in. Haxe was designed with autocompletion in mind. This means almost any Haxe editor supports it, since the API is so simple. I've personally been using [Sublime Text 2](http://www.sublimetext.com), which handles Haxe like a dream, and runs on Windows, OSX and Linux (I run OSX). If you run Windows, you'd be a fool not to use [FlashDevelop](http://flashdevelop.org/), which is rock solid. AS3 autocomplete was a shaky proposition outside of FlashDevelop, so having it available everywhere (and being able to even autocomplete the flash API) is a boon. 2. The AS3 target is solid. Through the entire translation phase, I didn't encounter a single Haxe bug. Debugging was a breeze because the backtrace given was relative to the Haxe files (as you should expect). ## Language differences 1. ###Stronger type system. Generics This is a huge, *huge* win for Haxe. If you've used AS3, you might be familiar with how they have a parameterized `Vector.<T>`. You're probably familiar with how you got your hopes up for properly generic types and functions, only to have them dashed when it turned out `Vector.<T>` is an Adobe hardcode and you can't do anything like it. Haxe, on the other hand, has generics built into the language, so you can make both functions and objects generic. Function types In AS3, functions have one type: `Function`. In Haxe, they have many. For instance, a function that takes an `Int` and converts it to a `String` would be `Int -> String`. This catches many bugs. 2. ###No more `Object`. The problem with `Object` from AS3 is that it's not type safe. In AS3 you can do something like this: <pre> var myObject:Object = {};<br> myObject[1] = "hi"; myObject["somekey"] = 4.3; </pre> Obviously if you do a loop through that object, you couldn't specify the type of the key. Haxe gets around this by splitting `Object` into two types that encompass all of its expected functionality. The first is `TypedDictionary<Key, Value>`. `TypedDictionary` is your typical key-value store - put in a key of one type, get out a value of another. The second is `typedef`. `typedef` is really similar to `struct` from C. If you're not familiar with `struct`, you can also think of it as an AS3 `Object` that you can't add any more properties to. Here's an example. <pre> typedef User = { var age : Int; var name : String; } var u : User = { age : 26, name : "Tom" }; u.age = 32; trace(u.name); u.xyz = 557; //Error! </pre> Both of these have the advantage of type safety. Also notice how they really do separate the two use cases of AS3's Object. In AS3 you shouldn't be using `Object` for `struct`s - you should be making classes - but I'd bet that you do anyways because `Objects` are so much more lightweight. There's a nice interplay between typedefs and classes. For instance, the `Iterator` typedef defines two methods: `next` and `hasNext`. You can make an `Iterator` like so: <pre> var a:ArrayTINT = [1,2,3,4,5,6]; var loc:Int = 0; var i:IteratorTINT = { hasNext = function() return loc != a.length, next = function() return a[loc++] } </pre> But you can also make an iterator like this: <pre> class MyIteratorTINT { function hasNext(): Bool { // do some stuff } function next(): T { // do other stuff } } </pre> Both these two iterators are interchangeable. Nice! Strictly speaking, you can simulate an AS3 object by declaring a var `Dynamic` and using `Reflect.setField` and `Reflect.getField`. I encourage you not to do this, though, because the built-in `Object` replacements are far superior in terms of type-safety. 3. ### Improved for loops. This is a nice advantage over AS3. Haxe's loops *only* use iterators. A traditional for loop looks like this: <pre> for (x in 0...10) trace(x) </pre> If you want to loop through an `Array`, it looks like this: <pre> var myArray:ArrayTINT = [1,2,3,4,5];<br> for (val in myArray) trace(val) </pre> This means that you can make any user-defined object loopable simply by defining an `iterator()` method on the object. 4. ### Different setter/getter syntax. `public var someInt(getSomeInt, setSomeInt): Int;` This indicates that the variable `someInt` has a setter and getter method named `getSomeInt` and `setSomeInt`, respectively. 5. ### Enumerations <pre> enum Color { Red; Green; Blue; } </pre> They type check - no mismatching enumerations because you did something like `var Red:Int = 1`. Enumerations in Haxe are a bit more powerful than, say, those found in Java or C++. If you're familiar with Haskell, you'll see that they take influence from algebraic datatypes - they can have values and be recursive. (And if you're not familiar with Haskell, don't be scared away! It's quite simple.) Nicolas Cannasse wrote Haxe in OCaml, so the influence is obvious. Here's your basic binary tree, where each node in the tree is either a leaf or a node with two trees beneath: <pre> enum Tree { Leaf(val: Int); Node(left:Tree, right:Tree); } </pre> Of course, we don't need to be showing favoritism to `Int` - we can templatize! <pre> enum TreeTTT { Leaf(val: T); Node(left:TreeTTT, right:TreeTTT); } </pre> Let's see AS3 typecheck that :-) 6. ### Using The `using` keyword allows you to add additional methods onto existing types. The classic example of `using` is the `Lambda` class. The `Lambda` class has a bunch of static methods on it. We'll use `Lambda.exists` as an example. The definition looks like this: <pre>static function existsTTT(it: IterableTTT, F: T -> Bool);</pre> For example, you could use the function like this: <pre> var myArray:ArrayTINT = [1,2,3,4]; var is3:Int -> Bool = function(x: Int) return x == 3;<br> if (Lambda.exists(myArray, is3)) { trace("I found a 3 in the array!"); } </pre> The `using` keyword lets you drop `exists` right onto the Array object itself - or any other object that implements `Iterable<T>`, for that matter. Check it out: <pre> using Lambda;<br> var myArray:ArrayTINT = [1,2,3,4]; var is3:Int -> Bool = function(x: Int) return x == 3;<br> if (myArray.exists(is3)) { trace("I found a 3 in the array!"); } </pre> Nice, huh? ## Problems 1. ### No cross-platform Dictionary type. The AS3 target has `TypedDictionary`, but sadly it doesn't exist on all platforms. The NME target has `ObjectHash`, but the problem with `ObjectHash` is that it can't have primitive types (`Int`, `String`, `Float`, `Bool`) as keys. To solve this problem, I wrote [SuperObjectHash.hx](https://github.com/johnfn/FathomHaxe/blob/master/hx/SuperObjectHash.hx) which combines `ObjectHash` and `Hash` into a single interface that you can use without having to worry about having primitive typed values. (It was pointed out on #haxe that ObjectHash [is planned to be introduced to Haxe](http://code.google.com/p/haxe/issues/detail?id=961), and will make it in by Haxe 3. Then my SuperObjectHash won't even be necessary.) 2. ### Overriding setters/getters is tricky. Essentially, you can override a variable setter and getter, but only if you know the name of the functions you're overriding (which rules out extending some builtins), and you're not permitted to use `super` to access the parent's property. From what I understand, these limitations stem from problems with target languages, primarily PHP. This essentially means that enhancing old behavior is impossible. The good news is that the `super` limitation is going away in Haxe 3 too. The first Haxe 3 release candidate is coming out in late February, and I'm definitely looking forward to it. ## Closing thoughts My overall impression? As a suffering AS3 developer, Haxe is a dream come true. It has all the features I wished AS3 would have - and a few more. It compiles faster than AS3 and it has better autocompletion than AS3. It optimizes code better than AS3 (which is to say not at all - AS3 optimizes absolutely nothing). It even has macros. Yep, a language with macros that doesn't have parenthesis all over the place (not to speak bad of Lisp, of course). Haxe is impressive. Even better, Haxe doesn't feel like a dead end language. I can cross-compile to any number of platforms with NME, which is exciting. I've been experimenting with using NME, which is admittedly a bit more shaky than using the AS3 libraries, but it's there, and it's exciting. I no longer feel nervous about the world moving to HTML5. Nicolas Cannasse and the Haxe team move incredibly fast - just the other day I noticed they were writing a [Haxe shader language](http://haxe.org/manual/hxsl) and a set of [generic 3D bindings](https://groups.google.com/forum/?fromgroups=#!topic/haxelang/zzupV6qL4_E) that will interoperate between Flash's Stage3D, HTML5's WebGL, and more. Wow. I have to feel like one of the big reasons that Haxe hasn't seen more widespread attention is that it's not English - the documentation is full of imprecise wording that feels amateur. (In fact, I spent some time cleaning it up the other day.) It's easy to draw the conclusion that the language is like the docs - mismatched and awkward - but it's not. Check it out. The possibilities are wild. _Thanks to Simn, Jan\_Flanders, PSvils, oreth, and everyone else on #haxe for proofreading this article and pointing out lots of things I didn't know. :-)_