Architecture
editCompiler
editThe Haxe compiler is separated into one frontend and multiple backends. The frontend is responsible for parsing and type-checking the input language, applying macros, general optimizations, various transformations, and for producing the intermediate representation of the code, the typed abstract syntax tree (AST). Each of the backends is responsible for translating that AST into either sourcecode or bytecode for the respective target.
The compiler is written in Ocaml and has exceptional performance. It can be run in server-mode (command-line: haxe --wait [host:]port ) to provide code completion for IDEs and maintain a cache, to speed up compilation even more.
Targets
editA target, in the context of Haxe, is an ambiguous term. It is used for platforms that provide access to core-APIs (language and bytecode targets), for the compiler-backends that are responsible for generating the respective code, and for runtimes with specific APIs that go beyond the core language support (platform-targets).
Language and Bytecode Targets
editThere are targets that produce bytecode (Neko, SWF, SWF8), sourcecode, that has to be compiled by a third-party compiler (C++, C#, Java), and source-code, that is being passed as-is to the respective runtime (JavaScript, PHP). Each provides specific mechanisms to interact with low-level aspects of the target-language to ease development. Targets that produce source-code e.g. offer a mechanism to embed code written in the target-language directly into haxe code.
Platform Targets
editFor most of the targets, multiple runtimes with different purposes exist. JavaScript, for instance, is in wide-spread use as a scripting language in browsers, game-engines, office-applications, as server-side language for runtimes like NodeJs, and much more. While Haxe supports all these runtimes with a single JavaScript target, the respective runtime APIs differ. Extern type definitions ("extern class" in Haxe) allow to describe the types of platform-native APIs, as well as those of runtimes and libraries written in the target language, to the Haxe compiler, so that static type-checking can be applied.
Since many of these platform targets run on multiple platforms themselves, there are very many options to deploy code written in haxe. Proper separation of platform independent code from the platform specific parts (e.g. the different filesystem APIs in AIR and NodeJS) ...
Code Generator | Platform Target | Common usage | Since Haxe Version | Comments |
---|---|---|---|---|
AVM | Flashplayer | Desktop/Browser | 1.0 (Nov. 2005) | |
AVM2 | Flashplayer | Desktop/Browser | 2.0 (Aug. 2006) | |
" | AIR | Desktop, Mobile | ||
" | Tamarin | Server,Desktop | ||
Javascript | Browser/HTML5 | Desktop, Mobile | 1.0 (Nov. 2005) | |
" | NodeJS | Server,Desktop | ||
" | PhoneGap | Mobile | ||
" | Sencha | Mobile | ||
Neko | NekoVM | Server,Desktop | 1.0 (Nov. 2005) | |
C++ | hxcpp | Server,Desktop,Mobile | 2.04 (April 2009) | |
Java | JVM | Server, Desktop | 2.10 (April 2012) | experimental |
C# | .NET | Server, Desktop, Mobile | 2.10 (April 2012) | experimental |
PHP | PHP | Server | 2.0 (July 2008) |
Language
editType System
editHaxe is a statically typed language. It has a rich type system that offers classes, interfaces, function/method types, anonymous types, algebraic data types (ATDs, called "enum" in Haxe), abstract types, and generalized algebraic datatypes (GADTs). Classes, ADTs, GADTs and function types allow parametric polymorphism based on type erasure, sometimes also called "Generics" in object oriented programming languages.
Bounded quantification is also part of the feature set: type parameters can be constrained to a set of zero or more types.
Haxe doesn't offer variance annotations for type parameters, the type constructors are always invariant in their parameter types.
Subtype polymorphism is supported via standard, single-inheritance.
Further, haxe supports both structural typing and nominal typing. To ease the burden on the programmer, without sacrificing type safety, Haxe supports type-inference, which in many cases alleviates the need to write out types explicitly.
Classes
editClasses (keyword "class") in Haxe are similar to those in e.g. Java or AS3. Their fields can be either methods, variables or properties, each static or per instance respectively. Haxe supports the accessors "public" and "private". as well as more advanced methods for access control (ACL, link, whatever), that are denoted using annotations. Methods and static variables of constant values can be inlined using the "inline" keyword.
class Person {
var birth:Date;
var name:String;
public function age():Int return Date.now().getFullYear()-birth.getFullYear()
}
Interfaces
editInterfaces in Haxe are very similar to those in e.g. Java.
interface ICreature {
function age():Int;
}
class Fly implements ICreature {
public function age():Int;
}
Algebraic Data Types
editEnums (keyword "enum") in Haxe are commonly referred to as algebraic data types (short: ADT) in functional languages. Haxe also supports generalized algebraic data types (GADT).
Prominent uses for ADTs are e.g. Option, Either and List:
enum Option<T> {
Some(v:T);
None;
}
enum Either<T,U> {
Left(v:T);
Right(v:U);
}
enum ConsList<T> {
Nil;
Cons(head:T,tail:ConsList<T>);
}
Anonymous Types
editAnonymous types are defined by denoting their structure explicitly, they can be given an alias by using a type definition (keyword "typedef"):
typedef Anon = { a:Int, b:String, c:Float->Void };
Function Types
editFunctions are first-class values in Haxe. Their type is denoted by using arrows between argument types, and the argument type(s) and return type respectively, as common in many functional languages. However, unlike in prominent examples like Haskell or the ML-family of languages, not all functions are unary functions (functions with one argument only), and in Haxe, functions can't be partially applied per default. Therefore the following type signatures have different semantics than in the aforementioned languages. The type F is a function that takes an Int and a String as arguments, and returns a value of type Float.
The same notation in a language with unary functions only, would refer to a function that takes an Int as argument, and returns a function of Type String->Float.
Types F2 and F3 denote the same type. Both are binary functions that return a binary function of type F. For F3 the syntax to declare a function type within a function type is used.
typedef F = Int->String->Float;
typedef F2 = Int->String->F;
typedef F3 = Int->String->(Int->String->Float);
Abstract Types
editAbstract Types, along with GADTs are the latest addition to the Haxe type system. They allow to reuse existing types for specific purposes, like implementing types for units of measurement, to greatly reduce the risk of mixing up values of the same underlying type, but with different meanings (e.g. miles vs. km).
Let's assume a library that uses the metric system per default, but has to deal with legacy data, that still uses miles for distances in certain places. Whenever we encounter miles, we want an automatic conversion to kilometers, but not in the opposite direction.
So we define two abstracts, that capture those requirements for us:
abstract Kilometer(Float) {
public function new(v:Float) this = v
}
abstract Mile(Float) {
public function new(v:Float) this = v
@:to public inline function toKilometer():Kilometer return (new Kilometer(this/0.62137))
}
class Test {
static var km:Kilometer;
static function main(){
var one100Miles = new Mile(100);
km = one100Miles;
trace(km); // et voila! 160.935
}
}
As the example shows, no explicit conversion is required for the assignment "km = one100Miles;" to do the right thing.
Structural Typing
editStructural typing plays a major role in many functional programming languages, and only to a much lesser extent in common OOP languages. Unlike in (exclusively) nominative type systems, the equality of two types isn't established by some kind of name tag, but rather by the structure of a type. Structural types can be thought of as implicit interfaces:
class FooBar {
public function new(){ foo=1; bar="2";}
public var foo:Int;
public var bar:String;
function anyFooBar(v:{foo:Int,bar:String}) trace(v.foo)
static function test(){
var fb = new FooBar();
fb.anyFooBar(fb);
fb.anyFooBar({foo:123,bar:"456"});
}
}
Dynamic
editIt is unfortunate, but as a language that has to interface with all sorts of platforms, sometimes the need arises to eschew the safety net, that the strong static type system provides the Haxe developer with. For those situations, there is Dynamic. It unifies with any other type, completely circumventing the type system.
Features
editMetaprogramming
editExtension Methods
editDead Code Elimination
editPattern Matching
editRemoting
editHistory
editmtasc, AS3 blabla