Haxe 3 Manual
Haxe 3 Manual
Haxe Foundation
August 3, 2017
Todo list
Could we have a big Haxe logo in the First Manual Page (Introduction) under the menu (a
bit like a book cover ?) It looks a bit empty now and is a landing page for ”Manual” . 8
This generates the following output: too many ’this’. You may like a passive sentence: the
following output will be generated...though this is to be avoided, generally . . . . . . 10
make sure the types are right for inc, dec, negate, and bitwise negate . . . . . . . . . . . . . 14
While introducing the different operations, we should include that information as well, in-
cluding how they differ with the ”C” standard, see http://haxe.org/manual/operators 14
please review, doubled content . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
review please, sounds weird . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
for starters...please review . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
Is there a difference between ?y : Int and y : Null<Int> or can you even do the
latter? Some more explanation and examples with native optional and Haxe optional
arguments and how they relate to nullability would be nice. . . . . . . . . . . . . . . . 18
please review future tense . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
Same as in 2.2, what is Enum<T> syntax? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
list arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
please reformat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 24
It seems a bit convoluted explanations. Should we maybe start by ”decoding” the meaning
of Void -¿ Void, then Int -¿ Bool -¿ Float, then maybe have samples using $type . . . . 26
please review your use of “this” and try to vary somewhat to avoid too much word repetition 33
please review for correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
You have marked “Map” for some reason . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Mention toString()/String conversion somewhere in this chapter. . . . . . . . . . . . . . . . 48
”parent class” should probably be used here, but I have no idea what it means, so I will
refrain from changing it myself. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
proper label and caption + code/identifier styling for diagram . . . . . . . . . . . . . . . . . 55
Figure out wtf our rules are now for when this is checked. . . . . . . . . . . . . . . . . . . . 96
Comprehensions are only listing Arrays, not Maps . . . . . . . . . . . . . . . . . . . . . . . . 97
what to use for listing of non-haxe code like hxml? . . . . . . . . . . . . . . . . . . . . . . . . 122
Check if we can build GADTs this way. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
I think C++ can use integer operatins, but I don’t know about any other targets. Only saw
this mentioned in an old discussion thread, still true? . . . . . . . . . . . . . . . . . . . 153
1
Contents
Todo list 1
1 Introduction 8
1.1 What is Haxe? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
1.2 About this Document . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.1 Authors and contributions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9
1.2.2 License . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.3 Hello World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
1.4 History . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
I Language Reference 12
2 Types 13
2.1 Basic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.1 Numeric types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.2 Overflow . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.3 Numeric Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14
2.1.4 Bool . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.1.5 Void . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
2.2 Nullability . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.2.1 Optional Arguments and Nullability . . . . . . . . . . . . . . . . . . . . . . . 18
2.3 Class Instance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.3.1 Class constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3.2 Inheritance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
2.3.3 Interfaces . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20
2.4 Enum Instance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
2.4.1 Enum Constructor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 22
2.4.2 Using enums . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.5 Anonymous Structure . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23
2.5.1 JSON for Structure Values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.5.2 Class Notation for Structure Types . . . . . . . . . . . . . . . . . . . . . . . . 25
2.5.3 Optional Fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.5.4 Impact on Performance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25
2.5.5 Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.6 Function Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
2.6.1 Optional Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27
2.6.2 Default values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.7 Dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
2.7.1 Dynamic with Type Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2
2.7.2 Implementing Dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
2.8 Abstract . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
2.8.1 Implicit Casts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32
2.8.2 Operator Overloading . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 35
2.8.3 Array Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
2.8.4 Enum abstracts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
2.8.5 Forwarding abstract fields . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.8.6 Core-type abstracts . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
2.9 Monomorph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
3 Type System 41
3.1 Typedef . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41
3.2 Type Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
3.2.1 Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
3.3 Generic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44
3.3.1 Construction of generic type parameters . . . . . . . . . . . . . . . . . . . . 45
3.4 Variance . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
3.5 Unification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.5.1 Between Class/Interface . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
3.5.2 Structural Subtyping . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.5.3 Monomorphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.5.4 Function Return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
3.5.5 Common Base Type . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.6 Type Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
3.6.1 Top-down Inference . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51
3.6.2 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.7 Modules and Paths . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
3.7.1 Module Sub-Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.7.2 Import . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53
3.7.3 Resolution Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
4 Class Fields 58
4.1 Variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 58
4.2 Property . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59
4.2.1 Common accessor identifier combinations . . . . . . . . . . . . . . . . . . . 61
4.2.2 Impact on the type system . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 62
4.2.3 Rules for getter and setter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
4.3 Method . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64
4.3.1 Overriding Methods . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
4.3.2 Effects of variance and access modifiers . . . . . . . . . . . . . . . . . . . . . 66
4.4 Access Modifier . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.1 Visibility . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 67
4.4.2 Inline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 68
4.4.3 Dynamic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 69
4.4.4 Override . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
4.4.5 Static . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 70
3
5 Expressions 71
5.1 Blocks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 73
5.2 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.3 Binary Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.4 Unary Operators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.5 Array Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
5.6 Object Declaration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.7 Field Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.8 Array Access . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75
5.9 Function Call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.10 var . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.11 Local functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
5.12 new . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.13 for . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 77
5.14 while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78
5.15 do-while . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.16 if . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.17 switch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
5.18 try/catch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.19 return . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 80
5.20 break . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.21 continue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.22 throw . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.23 cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.23.1 unsafe cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
5.23.2 safe cast . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82
5.24 type check . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
6 Language Features 84
6.1 Conditional Compilation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
6.2 Externs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 88
6.3 Static Extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
6.3.1 In the Haxe Standard Library . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
6.4 Pattern Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4.2 Enum matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
6.4.3 Variable capture . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92
6.4.4 Structure matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.4.5 Array matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.4.6 Or patterns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
6.4.7 Guards . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.4.8 Match on multiple values . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.4.9 Extractors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
6.4.10 Exhaustiveness checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.4.11 Useless pattern checks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.5 String Interpolation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
6.6 Array Comprehension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
6.7 Iterators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98
6.8 Function Bindings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
6.9 Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 100
6.10 Access Control . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 102
4
6.11 Inline constructors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
9 Macros 128
9.1 Macro Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
9.2 Arguments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
9.2.1 ExprOf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130
9.2.2 Constant Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
9.2.3 Rest Argument . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131
9.3 Reification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
9.3.1 Expression Reification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
9.3.2 Type Reification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132
9.3.3 Class Reification . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
9.4 Tools . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
9.5 Type Building . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
9.5.1 Enum building . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135
9.5.2 @:autoBuild . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136
9.5.3 @:genericBuild . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
9.6 Limitations . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9.6.1 Macro-in-Macro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9.6.2 Static extension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139
9.6.3 Build Order . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
9.6.4 Type Parameters . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
9.7 Initialization macros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 141
5
III Standard Library 142
10 Standard Library 143
10.1 String . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
10.2 Data Structures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
10.2.1 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143
10.2.2 Vector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
10.2.3 List . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
10.2.4 GenericStack . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
10.2.5 Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 147
10.2.6 Option . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148
10.3 Regular Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149
10.3.1 Matching . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
10.3.2 Groups . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
10.3.3 Replace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.3.4 Split . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.3.5 Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
10.3.6 Implementation Details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
10.4 Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
10.4.1 Special Numbers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.4.2 Mathematical Errors . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.4.3 Integer Math . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.4.4 Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153
10.5 Lambda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 154
10.6 Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155
10.7 Reflection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 158
10.8 Serialization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 159
10.8.1 Serialization format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 162
10.9 Xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
10.9.1 Getting started with Xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
10.9.2 Parsing Xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
10.9.3 Encoding Xml . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
10.10Json . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 164
10.10.1 Parsing JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
10.10.2 Encoding JSON . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 165
10.10.3 Implementation details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10.11Input/Output . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10.12Sys/sys . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10.13Remoting . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10.13.1 Remoting Connection . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 166
10.13.2 Implementation details . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168
10.14Unit testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 169
IV Miscellaneous 171
11 Haxelib 172
6
12 Target Details 173
12.1 JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173
12.1.1 Getting started with Haxe/JavaScript . . . . . . . . . . . . . . . . . . . . . . 173
12.1.2 Using external JavaScript libraries . . . . . . . . . . . . . . . . . . . . . . . . 174
12.1.3 Inject raw JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
12.1.4 JavaScript untyped functions . . . . . . . . . . . . . . . . . . . . . . . . . . . 176
12.1.5 JavaScript target Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177
12.1.6 Exposing Haxe classes for JavaScript . . . . . . . . . . . . . . . . . . . . . . . 178
12.1.7 Loading extern classes using ”require” function . . . . . . . . . . . . . . . . 179
12.2 Flash . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179
12.2.1 Getting started with Haxe/Flash . . . . . . . . . . . . . . . . . . . . . . . . . 179
12.2.2 Embedding resources . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 180
12.2.3 Using external Flash libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.2.4 Flash target Metadata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.3 Neko . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.4 PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.4.1 Getting started with Haxe/PHP . . . . . . . . . . . . . . . . . . . . . . . . . 181
12.4.2 PHP untyped functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182
12.5 C++ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 183
12.5.1 Getting started with Haxe/C++ . . . . . . . . . . . . . . . . . . . . . . . . . 183
12.5.2 The Hxcpp Build Environment . . . . . . . . . . . . . . . . . . . . . . . . . . 183
12.5.3 build.xml File Format . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 185
12.5.4 Using C++ Defines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 186
12.5.5 Using C++ Pointers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.6 Cppia . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.7 Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.8 C# . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.9 Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.10Lua . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.10.1 Getting started with Haxe/Lua . . . . . . . . . . . . . . . . . . . . . . . . . . 189
12.10.2 Using external Lua libraries . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
12.10.3 Version flags . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
12.10.4 Multireturns . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 190
12.11HashLink . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 191
13 Debugging 192
13.1 Logging and Trace . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 192
13.2 Position Information Parameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.3 Tracing types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.4 Debugging in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 193
13.5 Source Maps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
13.5.1 Source Maps in JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
13.5.2 Source Maps in Php7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194
7
Chapter 1
Introduction
8
We continue with the Haxe compiler reference, which first handles the basics in Compiler
Usage (Chapter 7) before getting into the advanced features in Compiler Features (Chapter 8).
Finally, we will venture into the exciting land of haxe macros in Macros (Chapter 9) to see how
some common tasks can be greatly simplified.
In the following chapter, Standard Library (Chapter 10), we explore important types and
concepts from the Haxe Standard Library. We then learn about Haxe’s package manager Haxelib
in Haxelib (Chapter 11).
Haxe abstracts away many target differences, but sometimes it is important to interact with a
target directly, which is the subject of Target Details (Chapter 12).
In a few places, this document has trivia-boxes. These include off-the-record information such
as why certain decisions were made during Haxe’s development or how a particular feature has
been changed in past Haxe versions. This information is generally not important and can be
skipped as it is only meant to convey trivia:
9
• Miha Lunar: Editing
• Nicolas Cannasse: Haxe creator
1.2.2 License
The Haxe Manual by Haxe Foundation is licensed under a Creative Commons Attribution 4.0
International License.
Based on a work at https://github.com/HaxeFoundation/HaxeManual.
Related content
• Beginner tutorials and examples in the Haxe Code Cookbook.
1.4 History
The Haxe project was started on 22 October 2005 by French developer Nicolas Cannasse as a suc-
cessor to the popular open-source ActionScript 2 compiler MTASC (Motion-Twin Action Script
Compiler) and the in-house MTypes language, which experimented with the application of type
inference to an object oriented language. Nicolas’ long-time passion for programming language
design and the rise of new opportunities to mix different technologies as part of his game devel-
oper work at Motion-Twin led to the creation of a whole new language.
Being spelled haXe back then, its beta version was released in February 2006 with the first
supported targets being AVM2 -bytecode and Nicolas’ own Neko virtual machine3 .
Nicolas Cannasse, who remains leader of the Haxe project to this date, kept on designing
Haxe with a clear vision, subsequently leading to the Haxe 1.0 release in May 2006. This first
2 Adobe Virtual Machine
3 http://nekovm.org
10
major release came with support for JavaScript code generation and already had some of the
features that define Haxe today such as type inference and structural sub-typing.
Haxe 1 saw several minor releases over the course of two years, adding the Flash AVM2 target
along with the haxelib-tool in August 2006 and the ActionScript 3 target in March 2007. During
these months, there was a strong focus on improving stability, which resulted in several minor
bug-fix releases.
Haxe 2.0 was released in July 2008, including the PHP target, courtesy of Franco Ponticelli. A
similar effort by Hugh Sanderson lead to the addition of the C++ target in July 2009 with the Haxe
2.04 release.
Just as with Haxe 1, what followed were several months of stability releases. In January 2011,
Haxe 2.07 was released with the support of macros. Around that time, Bruno Garcia joined the
team as maintainer of the JavaScript target, which saw vast improvements in the subsequent
2.08 and 2.09 releases.
After the release of 2.09, Simon Krajewski joined the team and work towards Haxe 3 began.
Furthermore, Cauê Waneck’s Java and C# targets found their way into the Haxe builds. It was
then decided to make one final Haxe 2 release, which happened in July 2012 with the release of
Haxe 2.10.
In late 2012, the Haxe 3 switch was flipped and the Haxe Compiler team, now backed by the
newly established Haxe Foundation4 , focused on this next major version. Haxe 3 was subsequently
released in May 2013.
4 http://haxe-foundation.org
11
Part I
Language Reference
12
Chapter 2
Types
The Haxe Compiler employs a rich type system which helps detecting type-related errors in a
program at compile-time. A type error is an invalid operation on a given type such as dividing
by a String, trying to access a field of an Integer or calling a function with not enough (or too
many) arguments.
In some languages this additional safety comes at a price because programmers are forced to
explicitly assign types to syntactic constructs:
1 var myButton:MySpecialButton = new MySpecialButton(); // As3
2 MySpecialButton* myButton = new MySpecialButton(); // C++
The explicit type annotations are not required in Haxe, because the compiler can infer the type:
1 var myButton = new MySpecialButton(); // Haxe
We will explore type inference in detail later in Type Inference (Section 3.6). For now, it is suf-
ficient to say that the variable myButton in the above code is known to be an instance of class
MySpecialButton.
The Haxe type system knows seven type groups:
We will describe each of these type groups and how they relate to each other in the next
chapters.
13
2.1 Basic Types
Basic types are Bool, Float and Int. They can easily be identified in the syntax by values such
as
Basic types are not classes (2.3) in Haxe. They are implemented as abstract types (2.8) and are
tied to the compiler’s internal operator-handling as described in the following sections.
Type: Float
Represents a double-precision IEEE 64-bit floating point number.
Type: Int
Represents an integral number.
While every Int can be used where a Float is expected (that is, Int is assignable to or unifies
with Float), the reverse is not true: Assigning a Float to an Int might lose precision and is not
allowed implicitly.
2.1.2 Overflow
For performance reasons, the Haxe Compiler does not enforce any overflow behavior. The bur-
den of checking for overflows falls to the target platform. Here are some platform specific notes
on overflow behavior:
C++, Java, C#, Neko, Flash: 32-bit signed integers with usual overflow practices
PHP, JS, Flash 8: No native Int type, loss of precision will occur if they reach their float limit (252 )
Alternatively, the haxe.Int32 and haxe.Int64 classes can be used to ensure correct overflow
behavior regardless of the platform at the cost of additional computations depending on the
platform.
Dynamic: Comparison involving at least one Dynamic value is unspecifed and platform-
specific.
15
2.1.4 Bool
Type: Bool
Represents a value which can be either true or false.
Values of type Bool are a common occurrence in conditions such as if (5.16) and while (5.14).
The following operators accept and return Bool values:
• && (and)
• || (or)
• ! (not)
Haxe guarantees that compound boolean expressions are evaluated from left to right and
only as far as necessary at run-time. For instance, an expression like A && B will evaluate A first
and evaluate B only if the evaluation of A yielded true. Likewise, the expressions A || B will
not evaluate B if the evaluation of A yielded true, because the value of B is irrelevant in that
case. This is important in cases such as this:
1 if (object != null && object.field == 1) { }
Accessing object.field if object is null would lead to a run-time error, but the check
for object != null guards against it.
2.1.5 Void
Type: Void
Void denotes the absence of a type. It is used to express that something (usually a function)
has no value.
Void is a special case in the type system because it is not actually a type. It is used to express
the absence of a type, which applies mostly to function arguments and return types. We have
please review, doubled already “seen” Void in the initial “Hello World” example:
content
1 class Main {
2 static public function main():Void {
3 trace("Hello World");
4 }
5 }
The function type will be explored in detail in the section Function Type (Section 2.6) but a
quick preview helps here: The type of the function main in the example above is Void->Void,
which reads as “it has no arguments and returns nothing”. Haxe does not allow fields and
review please, sounds variables of type Void and will complain if an attempt at declaring such is made:
weird
1 // Arguments and variables of type Void are not allowed
2 var x:Void;
16
2.2 Nullability
Definition: nullable
A type in Haxe is considered nullable if null is a valid value for it.
It is common for programming languages to have a single, clean definition for nullability. How-
ever, Haxe has to find a compromise in this regard due to the nature of Haxe’s target languages:
While some of them allow and; in fact, default to null for anything, others do not even allow
null for certain types. This necessitates the distinction of two types of target languages:
There is nothing to worry about when working with null on dynamic targets; however, static
for starters...please re- ones may require some thought. For starters, basic types are initialized to their default values.
view
Definition: Default values
Basic types have the following default values on static targets:
Int: 0
Float: NaN on Flash, 0.0 on other static targets
Bool: false
As a consequence, the Haxe Compiler does not allow the assignment of null to a basic type
on static targets. In order to achieve this, the basic type has to be wrapped as Null<T>:
1 // error on static platforms
2 var a:Int = null;
3 var b:Null<Int> = null; // allowed
17
Type: Null<T>
On static targets the types Null<Int>, Null<Float> and Null<Bool> can be used to
allow null as a value. On dynamic targets this has no effect. Null<T> can also be used with
other types in order to document that null is an allowed value.
If a null-value is “hidden” in Null<T> or Dynamic and assigned to a basic type, the default
value is used:
1 var n : Null<Int> = null;
2 var a : Int = n;
3 trace(a); // 0 on static platforms
Is there a difference
between ?y : Int and
y : Null<Int> or Trivia: Argument vs. Parameter
can you even do the In some other programming languages, argument and parameter are used interchangeably. In
latter? Some more Haxe, argument is used when referring to methods and parameter refers to Type Parameters
explanation and ex- (Section 3.2).
amples with native
optional and Haxe op-
tional arguments and
how they relate to nul- 2.3 Class Instance
lability would be nice.
Similar to many object-oriented languages, classes are the primary data structure for the majority
of programs in Haxe. Each Haxe class has an explicit name, an implied path and zero or more
class fields. Here we will focus on the general structure of classes and their relations, while
please review future leaving the details of class fields for Class Fields (Chapter 4).
tense The following code example serves as basis for the remainder of this section:
1 class Point {
2 var x : Int;
3 var y : Int;
4 public function new(x,y) {
5 this.x = x;
6 this.y = y;
7 }
8 public function toString() {
9 return "Point("+x+","+y+")";
18
10 }
11 }
Semantically, this class represents a point in discrete 2-dimensional space - but this is not
important here. Let us instead describe the structure:
• Point is the name of the class and could be anything conforming to the rules for type
identifiers (5).
• Enclosed in curly braces {} are the class fields,
• Which consist of two variable fields x and y of type Int,
• followed by a special function field named new, which is the constructor of the class,
• as well as a normal function toString.
Type: Class<T>
This type is compatible with all class types which means that all classes (not their instances)
can be assigned to it. At compile-time, Class<T> is the common base type of all class types.
However, this relation is not reflected in generated code.
This type is useful when an API requires a value to be a class, but not a specific one. This
applies to several methods of the Haxe reflection API (10.7).
2.3.2 Inheritance
Classes may inherit from other classes, which in Haxe is denoted by the extends keyword:
1 class Point3 extends Point {
2 var z : Int;
3 public function new(x,y,z) {
4 super(x,y);
5 this.z = z;
6 }
7 }
19
This relation is often described as ”is-a”: Any instance of class Point3 is also an instance of
Point. Point is then known as the parent class of Point3, which is a child class of Point. A
class may have many child classes, but only one parent class. The term “a parent class of class
X” usually refers to its direct parent class, the parent class of its parent class and so on.
The code above is very similar to the original Point class, with two new constructs being
shown:
• extends Point denotes that this class inherits from class Point
• super(x, y) is the call to the constructor of the parent class, in this case Point.new
It is not necessary for child classes to define their own constructors, but if they do, a call to
super() is mandatory. Unlike some other object-oriented languages, this call can appear any-
where in the constructor code and does not have to be the first expression.
A class may override methods (4.3) of its parent class, which requires the explicit override
keyword. The effects and restrictions of this are detailed in Overriding Methods (Section 4.3.1).
2.3.3 Interfaces
An interface can be understood as the signature of a class because it describes the public fields of
a class. Interfaces do not provide implementations but pure structural information:
1 interface Printable {
2 public function toString():String;
3 }
The syntax is similar to classes, with the following exceptions:
Interfaces, unlike structural subtyping (3.5.2), describe a static relation between classes. A given
class is only considered to be compatible to an interface if it explicitly states so:
1 class Point implements Printable { }
Here, the implements keyword denotes that Point has a ”is-a” relationship to Printable,
i.e. each instance of Point is also an instance of Printable. While a class may only have one
parent class, it may implement multiple interfaces through multiple implements keywords:
1 class Point implements Printable
2 implements Serializable
The compiler checks if the implements assumption holds. That is, it makes sure the class ac-
tually does implement all the fields required by the interface. A field is considered implemented
if the class or any of its parent classes provide an implementation.
Interface fields are not limited to methods. They can be variables and properties as well:
1 interface Placeable {
2 public var x:Float;
3 public var y:Float;
4 }
5
6 class Main implements Placeable {
20
7 public var x:Float;
8 public var y:Float;
9 static public function main() { }
10 }
Interfaces can extend multiple other interfaces using the extends keyword:
1 interface Debuggable extends Printable extends Serializable
Semantically, this enum describes a color which is either red, green, blue or a specified RGB
value. The syntactic structure is as follows:
• The keyword enum denotes that we are declaring an enum.
• Color is the name of the enum and could be anything conforming to the rules for type
identifiers (5).
• Enclosed in curly braces {} are the enum constructors,
• which are Red, Green and Blue taking no arguments,
• as well as Rgb taking three Int arguments named r, g and b.
The Haxe type system provides a type which unifies with all enum types:
Type: Enum<T>
This type is compatible with all enum types. At compile-time, Enum<T> can be seen as the
common base type of all enum types. However, this relation is not reflected in generated
code.
21
2.4.1 Enum Constructor
Similar to classes and their constructors, enums provide a way of instantiating them by using
one of their constructors. However, unlike classes, enums provide multiple constructors which
can easily be used through their name:
1 var a = Red;
2 var b = Green;
3 var c = Rgb(255, 255, 0);
In this code the type of variables a, b and c is Color. Variable c is initialized using the Rgb
list arguments constructor with arguments.
All enum instances can be assigned to a special type named EnumValue.
Type: EnumValue
EnumValue is a special type which unifies with all enum instances. It is used by the Haxe
Standard Library to provide certain operations for all enum instances and can be employed
in user-code accordingly in cases where an API requires an enum instance, but not a specific
one.
It is important to distinguish enum types and enum constructors, as this example demon-
strates:
1 enum Color {
2 Red;
3 Green;
4 Blue;
5 Rgb(r:Int, g:Int, b:Int);
6 }
7
8 class Main {
9 static public function main() {
10 var ec:EnumValue = Red; // valid
11 var en:Enum<Color> = Color; // valid
12 // Error: Color should be Enum<Color>
13 //var x:Enum<Color> = Red;
14 }
15 }
If the commented line is uncommented, the program does not compile because Red (an enum
constructor) cannot be assigned to a variable of type Enum<Color> (an enum type). The relation
is analogous to a class and its instance.
22
2.4.2 Using enums
Enums are a good choice if only a finite set of values should be allowed. The individual construc-
tors (2.4.1) then represent the allowed variants and enable the compiler to check if all possible
values are respected. This can be seen here:
1 enum Color {
2 Red;
3 Green;
4 Blue;
5 Rgb(r:Int, g:Int, b:Int);
6 }
7
8 class Main {
9 static function main() {
10 var color = getColor();
11 switch (color) {
12 case Red: trace("Color was red");
13 case Green: trace("Color was green");
14 case Blue: trace("Color was blue");
15 case Rgb(r, g, b): trace("Color had a red value of " +r);
16 }
17 }
18
19 static function getColor():Color {
20 return Rgb(255, 0, 255);
21 }
22 }
After retrieving the value of color by assigning the return value of getColor() to it, a
switch expression (5.17) is used to branch depending on the value. The first three cases Red,
Green and Blue are trivial and correspond to the constructors of Color that have no arguments.
The final case Rgb(r, g, b) shows how the argument values of a constructor can be extracted:
they are available as local variables within the case body expression, just as if a var expression
(5.10) had been used.
Advanced information on using the switch expression will be explored later in the section
on pattern matching (6.4).
23
2. Has a comma-separated list of key-value-pairs.
3. A colon separates the key, which must be a valid identifier (5), from the value.
4. The value can be any Haxe expression.
Rule 4 implies that structures can be nested and complex, e.g.:
please reformat
1 var user = {
2 name : "Nicolas",
3 age : 32,
4 pos : [
5 { x : 0, y : 0 },
6 { x : 1, y : -1 }
7 ],
8 };
Fields of structures, like classes, are accessed using a dot (.) like so:
1 // get value of name, which is "Nicolas"
2 user.name;
3 // set value of age to 33
4 user.age = 33;
It is worth noting that using anonymous structures does not subvert the typing system. The
compiler ensures that only available fields are accessed, which means the following program
does not compile:
1 class Test {
2 static public function main() {
3 var point = { x: 0.0, y: 12.0 };
4 // { y : Float, x : Float } has no field z
5 point.z;
6 }
7 }
The error message indicates that the compiler knows the type of point: It is a structure with
fields x and y of type Float. Since it has no field z, the access fails. The type of point is
known through type inference (3.6), which thankfully saves us from using explicit types for local
variables. However, if point was a field, explicit typing would be necessary:
1 class Path {
2 var start : { x : Int, y : Int };
3 var target : { x : Int, y : Int };
4 var current : { x : Int, y : Int };
5 }
To avoid this kind of redundant type declaration, especially for more complex structures, it is
advised to use a typedef (3.1):
1 typedef Point = { x : Int, y : Int }
2
3 class Path {
4 var start : Point;
5 var target : Point;
6 var current : Point;
7 }
24
You may also use Extensions (2.5.5) to “inherit” fields from other structures.
1 typedef Point3 = { > Point, z : Int }
25
2.5.5 Extensions
Extensions are used to express that a structure has all the fields of a given type as well as some
additional fields of its own:
1 typedef IterableWithLength<T> = {
2 > Iterable<T>,
3 // read only property
4 var length(default, null):Int;
5 }
6
7 class Main {
8 static public function main() {
9 var array = [1, 2, 3];
10 var t:IterableWithLength<Int> = array;
11 }
12 }
The greater-than operator > denotes that an extension of Iterable<T> is being created, with
the additional class fields following. In this case, a read-only property (4.2) length of type Int
is required.
In order to be compatible with IterableWithLength<T>, a type then must be compatible
with Iterable<T> and also provide a read-only length property of type Int. The example
assigns an Array, which happens to fulfill these requirements.
Since Haxe 3.1.0
• Function arguments are separated by the special arrow token -> instead of commas, and
• the function return type appears at the end after another ->.
In either notation it is obvious that the function test accepts a first argument of type Int,
a second argument of type String and returns a value of type Bool. If a call to this function,
such as test(1, "foo"), is made within the second $type expression, the Haxe typer checks
if 1 can be assigned to Int and if "foo" can be assigned to String. The type of the call is then
equal to the type of the value test returns, which is Bool.
If a function type has other function types as argument or return type, parentheses can be
used to group them correctly. For example, Int -> (Int -> Void) -> Void represents a
function which has a first argument of type Int, a second argument of function type Int ->
Void and a return of Void.
27
3. The third call is made with two arguments 1 and "foo".
4. The fourth call is made with a singular argument "foo".
The output shows that optional arguments which are omitted from the call have a value of null.
This implies that the type of these arguments must admit null as value, which raises the ques-
tion of its nullability (2.2). The Haxe Compiler ensures that optional basic type arguments are
nullable by inferring their type as Null<T> when compiling to a static target (2.2).
While the first three calls are intuitive, the fourth one might come as a surprise: It is indeed
allowed to skip optional arguments if the supplied value is assignable to a later argument.
2.7 Dynamic
While Haxe has a static type system, this type system can, in effect, be turned off by using the
Dynamic type. A dynamic value can be assigned to anything; and anything can be assigned to it.
This has several drawbacks:
28
• The compiler can no longer type-check assignments, function calls and other constructs
where specific types are expected.
• Certain optimizations, in particular when compiling to static targets, can no longer be em-
ployed.
• Some common errors, e.g. a typo in a field access, can not be caught at compile-time and
likely cause an error at runtime.
• Dead Code Elimination (Section 8.2) cannot detect used fields if they are used through
Dynamic.
It is very easy to come up with examples where the usage of Dynamic can cause problems at
runtime. Consider compiling the following two lines to a static target:
1 var d:Dynamic = 1;
2 d.foo;
Trying to run a compiled program in the Flash Player yields an error Property foo not
found on Number and there is no default value. Without Dynamic, this would have
been detected at compile-time.
Use of Dynamic should be minimized as there are better options in many situations but some-
times it is just practical to use it. Parts of the Haxe Reflection (Section 10.7) API use it and it
is sometimes the best option when dealing with custom data structures that are not known at
compile-time.
Dynamic behaves in a special way when being unified (3.5) with a monomorph (2.9). Monomorphs
are never bound to Dynamic which can have surprising results in examples such as this:
1 class Main {
2 static function main() {
3 var jsonData = ’[1, 2, 3]’;
4 var json = haxe.Json.parse(jsonData);
5 $type(json); // Unknown<0>
6 for (i in 0...json.length) {
7 // Array access is not allowed on
8 // {+ length : Int }
9 trace(json[0]);
10 }
11 }
12 }
Although the return type of Json.parse is Dynamic, the type of local variable json is
not bound to it and remains a monomorph. It is then inferred as an anonymous structure
(2.5) upon the json.length field access, which causes the following json[0] array access
to fail. In order to avoid this, the variable json can be explicitly typed as Dynamic by using var
json:Dynamic.
29
Trivia: Dynamic in the Standard Library
Dynamic was quite frequent in the Haxe Standard Library before Haxe 3. With the continu-
ous improvements of the Haxe type system the occurrences of Dynamic were reduced over
the releases leading to Haxe 3.
30
3 public function new() {}
4 function resolve(field:String) {
5 return "Tried to resolve " +field;
6 }
7 }
8
9 class Main {
10 static public function main() {
11 var c = new Resolve();
12 c.present = 2;
13 trace(c.present);
14 trace(c.resolveMe);
15 }
16 }
2.8 Abstract
An abstract type is a type which is actually a different type at run-time. It is a compile-time
feature which defines types “over” concrete types in order to modify or augment their behavior:
1 abstract AbstractInt(Int) {
2 inline public function new(i:Int) {
3 this = i;
4 }
5 }
• AbstractInt is the name of the abstract and could be anything conforming to the rules
for type identifiers.
• Enclosed in parenthesis () is the underlying type Int.
• Enclosed in curly braces {} are the fields,
• which are a constructor function new accepting one argument i of type Int.
The syntax is reminiscent of classes and the semantics are indeed similar. In fact, everything
in the “body” of an abstract (that is everything after the opening curly brace) is parsed as class
fields. Abstracts may have method (4.3) fields and non-physical (4.2.3) property (4.2) fields.
Furthermore, abstracts can be instantiated and used just like classes:
1 class Main {
2 static public function main() {
3 var a = new AbstractInt(12);
31
4 trace(a); //12
5 }
6 }
As mentioned before, abstracts are a compile-time feature, so it is interesting to see what the
above actually generates. A suitable target for this is JavaScript, which tends to generate concise
and clean code. Compiling the above (using haxe -main MyAbstract -js myabstract.js)
shows this JavaScript code:
1 var a = 12;
2 console.log(a);
The abstract type Abstract completely disappeared from the output and all that is left is a value
of its underlying type, Int. This is because the constructor of Abstract is inlined - something
we shall learn about later in the section Inline (Section 4.4.2) - and its inlined expression assigns
a value to this. This might be surprising when thinking in terms of classes. However, it is
precisely what we want to express in the context of abstracts. Any inlined member method of an
abstract can assign to this, and thus modify the “internal value”.
A good question at this point is “What happens if a member function is not declared inline”
because the code obviously has to go somewhere. Haxe creates a private class, known to be the
implementation class, which has all the abstract member functions as static functions accepting an
additional first argument this of the underlying type.
Direct: Allows direct casting of the abstract type to or from another type. This is defined by
adding to and from rules to the abstract type and is only allowed for types which unify
with the underlying type of the abstract.
Class field: Allows casting via calls to special cast functions. These functions are defined using
@:to and @:from metadata. This kind of cast is allowed for all types.
32
11 }
12 }
We declare MyAbstract as being from Int and to Int, meaning it can be assigned from Int
and assigned to Int. This is shown in lines 9 and 10, where we first assign the Int 12 to variable
a of type MyAbstract (this works due to the from Int declaration) and then that abstract back
to variable b of type Int (this works due to the to Int declaration).
Class field casts have the same semantics, but are defined completely differently:
1 abstract MyAbstract(Int) {
2 inline function new(i:Int) {
3 this = i;
4 }
5
6 @:from
7 static public function fromString(s:String) {
8 return new MyAbstract(Std.parseInt(s));
9 }
10
11 @:to
12 public function toArray() {
13 return [this];
14 }
15 }
16
17 class Main {
18 static public function main() {
19 var a:MyAbstract = "3";
20 var b:Array<Int> = a;
21 trace(b); // [3]
22 }
23 }
By adding @:from to a static function, that function qualifies as implicit cast function from its
argument type to the abstract. These functions must return a value of the abstract type. They
must also be declared static.
Similarly, adding @:to to a function qualifies it as implicit cast function from the abstract to
its return type.
In the example the method fromString allows the assignment of value "3" to variable a
of type MyAbstract while the method toArray allows assigning that abstract to variable b of
type Array<Int>.
When using this kind of cast, calls to the cast-functions are inserted where required. This
becomes obvious when looking at the JavaScript output:
1 var a = _ImplicitCastField.MyAbstract_Impl_.fromString("3");
2 var b = _ImplicitCastField.MyAbstract_Impl_.toArray(a);
This can be further optimized by inlining (4.4.2) both cast functions, turning the output into the
please review your following:
use of “this” and try
1 var a = Std.parseInt("3");
to vary somewhat to
2 var b = [a];
avoid too much word
repetition The selection algorithm when assigning a type A to a type B with at least one of them being an
abstract is simple:
33
1. If A is not an abstract, go to 3.
2. If A defines a to-conversions that admits B, go to 6.
3. If B is not an abstract, go to 5.
4. If B defines a from-conversions that admits A, go to 6.
No
A is abstract
Yes
Yes
A defines to for B
No
No B is abstract
Yes
Yes
B defines from for A
No
By design, implicit casts are not transitive, as the following example shows:
1 abstract A(Int) {
2 public function new() this = 0;
3 @:to public function toB() return new B();
4 }
5
6 abstract B(Int) {
7 public function new() this = 0;
8 @:to public function toC() return new C();
9 }
10
11 abstract C(Int) {
12 public function new() this = 0;
13 }
14
15 class Main {
16 static public function main() {
34
17 var a = new A();
18 var b:B = a; // valid, uses A.toB
19 var c:C = b; // valid, uses B.toC
20 var c:C = a; // error, A should be C
21 }
22 }
While the individual casts from A to B and from B to C are allowed, a transitive cast from A to C
is not. This is to avoid ambiguous cast-paths and retain a simple selection algorithm.
By defining @:op(A * B), the function repeat serves as operator method for the multiplica-
tion * operator when the type of the left value is MyAbstract and the type of the right value is
Int. The usage is shown in line 17, which turns into this when compiled to JavaScript:
1 console.log(_AbstractOperatorOverload.
2 MyAbstract_Impl_.repeat(a,3));
Similar to implicit casts with class fields (2.8.1), a call to the overload method is inserted where
required.
The example repeat function is not commutative: While MyAbstract * Int works, Int
* MyAbstract does not. If this should be allowed as well, the @:commutative metadata can
be added. If it should work only for Int * MyAbstract, but not for MyAbstract * Int, the
overload method can be made static, accepting Int and MyAbstract as first and second type
respectively.
Overloading unary operators is analogous:
1 abstract MyAbstract(String) {
2 public inline function new(s:String) {
3 this = s;
35
4 }
5
6 @:op(++A) public function pre()
7 return "pre" + this;
8 @:op(A++) public function post()
9 return this + "post";
10 }
11
12 class Main {
13 static public function main() {
14 var a = new MyAbstract("foo");
15 trace(++a); // prefoo
16 trace(a++); // foopost
17 }
18 }
Both binary and unary operator overloads can return any type.
Exposing underlying type operations It is also possible to omit the method body of a @:op
function, but only if the underlying type of the abstract allows the operation in question and if
the resulting type can be assigned back to the abstract.
1 abstract MyAbstractInt(Int) from Int to Int {
2 // The following line exposes the (A > B) operation from the
underlying Int
3 // type. Note that no function body is used:
4 @:op(A > B) static function gt( a:MyAbstractInt, b:MyAbstractInt ) :
Bool;
5 }
6
7 class Main {
8 static function main() {
9 var a:MyAbstractInt = 42;
10 if(a > 0) trace(’Works fine, > operation implemented!’);
11
12 // The < operator is not implemented.
13 // This will cause an ’Cannot compare MyAbstractInt and Int’ error
:
14 if(a < 100) { }
15 }
16 }
please review for cor-
rectness
2.8.3 Array Access
Array access describes the particular syntax traditionally used to access the value in an array at
a certain offset. This is usually only allowed with arguments of type Int. Nevertheless, with
abstracts it is possible to define custom array access methods. The Haxe Standard Library (10)
You have marked uses this in its Map type, where the following two methods can be found:
“Map” for some rea-
1 @:arrayAccess
son
2 public inline function get(key:K) {
36
3 return this.get(key);
4 }
5 @:arrayAccess
6 public inline function arrayWrite(k:K, v:V):V {
7 this.set(k, v);
8 return v;
9 }
The methods get and arrayWrite seen above then allow the following usage:
1 class Main {
2 public static function main() {
3 var map = new Map();
4 map["foo"] = 1;
5 trace(map["foo"]);
6 }
7 }
At this point it should not be surprising to see that calls to the array access fields are inserted
in the output:
1 map.set("foo",1);
2 console.log(map.get("foo")); // 1
Order of array access resolving Due to a bug in Haxe versions before 3.2 the order of checked
:arrayAccess fields was undefined. This was fixed for Haxe 3.2 so that the fields are now
consistently checked from top to bottom:
1 abstract AString(String) {
2 public function new(s) this = s;
3 @:arrayAccess function getInt1(k:Int) {
4 return this.charAt(k);
5 }
6 @:arrayAccess function getInt2(k:Int) {
7 return this.charAt(k).toUpperCase();
8 }
9 }
10
11 class Main {
12 static function main() {
13 var a = new AString("foo");
14 trace(a[0]); // f
15 }
16 }
The array access a[0] is resolved to the getInt1 field, leading to lower case f being re-
turned. The result might be different in Haxe versions before 3.2.
Fields which are defined earlier take priority even if they require an implicit cast (2.8.1).
37
2.8.4 Enum abstracts
Since Haxe 3.1.0
By adding the :enum metadata to an abstract definition, that abstract can be used to define
finite value sets:
1 @:enum
2 abstract HttpStatus(Int) {
3 var NotFound = 404;
4 var MethodNotAllowed = 405;
5 }
6
7 class Main {
8 static public function main() {
9 var status = HttpStatus.NotFound;
10 var msg = printStatus(status);
11 }
12
13 static function printStatus(status:HttpStatus) {
14 return switch(status) {
15 case NotFound:
16 "Not found";
17 case MethodNotAllowed:
18 "Method not allowed";
19 }
20 }
21 }
The Haxe Compiler replaces all field access to the HttpStatus abstract with their values, as
evident in the JavaScript output:
1 Main.main = function() {
2 var status = 404;
3 var msg = Main.printStatus(status);
4 };
5 Main.printStatus = function(status) {
6 switch(status) {
7 case 404:
8 return "Not found";
9 case 405:
10 return "Method not allowed";
11 }
12 };
This is similar to accessing variables declared as inline (4.4.2), but has several advantages:
• The typer can ensure that all values of the set are typed correctly.
• The pattern matcher checks for exhaustiveness (6.4.10) when matching (6.4) an enum ab-
stract.
38
2.8.5 Forwarding abstract fields
Since Haxe 3.1.0
When wrapping an underlying type, it is sometimes desirable to “keep” parts of its function-
ality. Because writing forwarding functions by hand is cumbersome, Haxe allows adding the
:forward metadata to an abstract type:
1 @:forward(push, pop)
2 abstract MyArray<S>(Array<S>) {
3 public inline function new() {
4 this = [];
5 }
6 }
7
8 class Main {
9 static public function main() {
10 var myArray = new MyArray();
11 myArray.push(12);
12 myArray.pop();
13 // MyArray<Int> has no field length
14 //myArray.length;
15 }
16 }
The MyArray abstract in this example wraps Array. Its :forward metadata has two ar-
guments which correspond to the field names to be forwarded to the underlying type. In this
example, the main method instantiates MyArray and accesses its push and pop methods. The
commented line demonstrates that the length field is not available.
As usual we can look at the JavaScript output to see how the code is being generated:
1 Main.main = function() {
2 var myArray = [];
3 myArray.push(12);
4 myArray.pop();
5 };
It is also possible to use :forward without any arguments in order to forward all fields. Of
course the Haxe Compiler still ensures that the field actually exists on the underlying type.
39
Introducing custom core-type abstracts is rarely necessary in user code as it requires the Haxe
target to be able to make sense of it. However, there could be interesting use-cases for authors of
macros and new Haxe targets.
In contrast to opaque abstracts, core-type abstracts have the following properties:
• Operator overloading fields (2.8.2) that have no expression are not forced to adhere to the
Haxe type semantics.
2.9 Monomorph
A monomorph is a type which may, through unification (3.5), morph into a different type later.
We shall see details about this type when talking about type inference (3.6).
40
Chapter 3
Type System
We learned about the different kinds of types in Types (Chapter 2) and it is now time to see how
they interact with each other. We start off easy by introducing typedef (3.1), a mechanism to give
a name (or alias) to a more complex type. Among other things, this will come in handy when
working with types having type parameters (3.2).
A lot of type-safety is achieved by checking if two given types of the type groups above
are compatible. Meaning, the compiler tries to perform unification between them as detailed in
Unification (Section 3.5).
All types are organized in modules and can be addressed through paths. Modules and Paths
(Section 3.7) will give a detailed explanation of the related mechanics.
3.1 Typedef
We briefly looked at typedefs while talking about anonymous structures (2.5) and saw how we
could shorten a complex structure type (2.5) by giving it a name. This is precisely what typedefs
are good for. Giving names to structure types might even be considered their primary use. In
fact, it is so common that the distinction appears somewhat blurry and many Haxe users consider
typedefs to actually be the structure.
A typedef can give a name to any other type:
1 typedef IA = Array<Int>;
This enables us to use IA in places where we would normally use Array<Int>. While this
saves only a few keystrokes in this particular case, it can make a much bigger difference for more
complex, compound types. Again, this is why typedef and structures seem so connected:
1 typedef User = {
2 var age : Int;
3 var name : String;
4 }
A typedef is not a textual replacement but actually a real type. It can even have type parameters
(3.2) as the Iterable type from the Haxe Standard Library demonstrates:
1 typedef Iterable<T> = {
2 function iterator() : Iterator<T>;
3 }
41
3.2 Type Parameters
Haxe allows parametrization of a number of types, as well as class fields (4) and enum construc-
tors (2.4.1). Type parameters are defined by enclosing comma-separated type parameter names
in angle brackets <>. A simple example from the Haxe Standard Library is Array:
1 class Array<T> {
2 function push(x : T) : Int;
3 }
Whenever an instance of Array is created, its type parameter T becomes a monomorph (2.9).
That is, it can be bound to any type, but only one at a time. This binding can happen
Inside the definition of a class with type parameters, these type parameters are an unspecific type.
Unless constraints (3.2.1) are added, the compiler has to assume that the type parameters could
be used with any type. As a consequence, it is not possible to access fields of type parameters
or cast (5.23) to a type parameter type. It is also not possible to create a new instance of a type
parameter type, unless the type parameter is generic (3.3) and constrained accordingly.
The following table shows where type parameters are allowed:
With function type parameters being bound upon invocation, such a type parameter (if uncon-
strained) accepts any type. However, only one type per invocation is accepted. This can be
utilized if a function has multiple arguments:
1 class Main {
2 static public function main() {
3 equals(1, 1);
4 // runtime message: bar should be foo
5 equals("foo", "bar");
6 // compiler error: String should be Int
7 equals(1, "foo");
8 }
9
10 static function equals<T>(expected:T, actual:T) {
11 if (actual != expected) {
12 trace(’$actual should be $expected’);
13 }
14 }
15 }
Both arguments expected and actual of the equals function have type T. This implies
that for each invocation of equals the two arguments must be of the same type. The compiler
admits the first call (both arguments being of Int) and the second call (both arguments being of
String) but the third attempt causes a compiler error.
42
Trivia: Type parameters in expression syntax
We often get the question why a method with type parameters cannot be called as
method<String>(x). The error messages the compiler gives are not very helpful. How-
ever, there is a simple reason for that: The above code is parsed as if both < and > were binary
operators, yielding (method < String) > (x).
3.2.1 Constraints
Type parameters can be constrained to multiple types:
1 typedef Measurable = {
2 public var length(default, null):Int;
3 }
4
5 class Main {
6 static public function main() {
7 trace(test([]));
8 trace(test(["bar", "foo"]));
9 // String should be Iterable<String>
10 //test("foo");
11 }
12
13 static function test<T:(Iterable<String>, Measurable)>(a:T) {
14 if (a.length == 0) return "empty";
15 return a.iterator().next();
16 }
17 }
Type parameter T of method test is constrained to the types Iterable<String> and Measurable.
The latter is defined using a typedef (3.1) for convenience and requires compatible types to have
a read-only property (4.2) named length of type Int. The constraints then say that a type is
compatible if
• it is compatible with Iterable<String> and
• has a length-property of type Int.
We can see that invoking test with an empty array in line 7 and an Array<String> in line
8 works fine. This is because Array has both a length-property and an iterator-method.
However, passing a String as argument in line 9 fails the constraint check because String is
not compatible with Iterable<T>.
When constraining to a single type, the parentheses can be omitted:
1 class Main {
2 static public function main() {
3 trace(test([]));
4 trace(test(["bar", "foo"]));
5 }
6
7 static function test<T:Iterable<String>>(a:T) {
8 return a.iterator().next();
9 }
10 }
43
3.3 Generic
Usually, the Haxe Compiler generates only a single class or function even if it has type param-
eters. This results in a natural abstraction where the code generator for the target language has
to assume that a type parameter could be of any type. The generated code then might have to
perform some type checks which can be detrimental to performance.
A class or function can be made generic by attributing it with the :generic metadata (6.9).
This causes the compiler to emit a distinct class/function per type parameter combination with
mangled names. A specification like this can yield a boost in performance-critical code portions
on static targets (2.2) at the cost of a larger output size:
1 @:generic
2 class MyValue<T> {
3 public var value:T;
4 public function new(value:T) {
5 this.value = value;
6 }
7 }
8
9 class Main {
10 static public function main() {
11 var a = new MyValue<String>("Hello");
12 var b = new MyValue<Int>(42);
13 }
14 }
It seems unusual to see the explicit type MyValue<String> here as we usually let type
inference (3.6) deal with this. Nonetheless, it is indeed required in this case. The compiler has to
know the exact type of a generic class upon construction. The JavaScript output shows the result:
1 (function () { "use strict";
2 var Test = function() { };
3 Test.main = function() {
4 var a = new MyValue_String("Hello");
5 var b = new MyValue_Int(5);
6 };
7 var MyValue_Int = function(value) {
8 this.value = value;
9 };
10 var MyValue_String = function(value) {
11 this.value = value;
12 };
13 Test.main();
14 })();
We can identify that MyValue<String> and MyValue<Int> have become MyValue_-
String and MyValue_Int respectively. This is similar for generic functions:
1 class Main {
2 static public function main() {
3 method("foo");
4 method(1);
5 }
6
44
7 @:generic static function method<T>(t:T) { }
8 }
Again, the JavaScript output makes it obvious:
1 (function () { "use strict";
2 var Main = function() { }
3 Main.method_Int = function(t) {
4 }
5 Main.method_String = function(t) {
6 }
7 Main.main = function() {
8 Main.method_String("foo");
9 Main.method_Int(1);
10 }
11 Main.main();
12 })();
It is not possible to construct normal type parameters, e.g. new T() is a compiler error. The
reason for this is that Haxe generates only a single function and the construct makes no sense in
that case. This is different when the type parameter is generic: Since we know that the compiler
will generate a distinct function for each type parameter combination, it is possible to replace the
T new T() with the real type.
1 import haxe.Constraints;
2
3 class Main {
4 static public function main() {
5 var s:String = make();
6 var t:haxe.Template = make();
7 }
8
9 @:generic
10 static function make<T:Constructible<String->Void>>():T {
11 return new T("foo");
12 }
13 }
It should be noted that top-down inference (3.6.1) is used here to determine the actual type
of T. There are two requirements for this kind of type parameter construction to work: The con-
structed type parameter must be
1. generic and
2. be explicitly constrained (3.2.1) to having a constructor (2.3.1).
45
Here, 1. is given by make having the @:generic metadata, and 2. by T being constrained to
Constructible. The constraint holds for both String and haxe.Template as both have a
constructor accepting a singular String argument. Sure enough, the relevant JavaScript output
looks as expected:
1 var Main = function() { }
2 Main.__name__ = true;
3 Main.make_haxe_Template = function() {
4 return new haxe.Template("foo");
5 }
6 Main.make_String = function() {
7 return new String("foo");
8 }
9 Main.main = function() {
10 var s = Main.make_String();
11 var t = Main.make_haxe_Template();
12 }
3.4 Variance
While variance is also relevant in other places, it occurs particularly often with type parameters
and comes as a surprise in this context. Additionally, it is very easy to trigger variance errors:
1 class Base {
2 public function new() { }
3 }
4
5 class Child extends Base { }
6
7 class Main {
8 public static function main () {
9 var children = [new Child()];
10 // Array<Child> should be Array<Base>
11 // Type parameters are invariant
12 // Child should be Base
13 var bases:Array<Base> = children;
14 }
15 }
Apparently, an Array<Child> cannot be assigned to an Array<Base>, even though Child
can be assigned to Base. The reason for this might be somewhat unexpected: It is not allowed
because arrays can be written to, e.g. via their push() method. It is easy to generate problems
by ignoring variance errors:
1 class Base {
2 public function new() { }
3 }
4
5 class Child extends Base { }
6
7 class OtherChild extends Base { }
8
46
9 class Main {
10 public static function main () {
11 var children = [new Child()];
12 // subvert type checker
13 var bases:Array<Base> = cast children;
14 bases.push(new OtherChild());
15 for(child in children) {
16 trace(child);
17 }
18 }
19 }
Here we subvert the type checker by using a cast (5.23), thus allowing the assignment af-
ter the commented line. With that we hold a reference bases to the original array, typed as
Array<Base>. This allows pushing another type compatible with Base (OtherChild) onto
that array. However, our original reference children is still of type Array<Child> and things
go bad when we encounter the OtherChild instance in one of its elements while iterating.
If Array had no push() method and no other means of modification, the assignment would
be safe because no incompatible type could be added to it. In Haxe, we can achieve this by
restricting the type accordingly using structural subtyping (3.5.2):
1 class Base {
2 public function new() { }
3 }
4
5 class Child extends Base { }
6
7 typedef MyArray<T> = {
8 public function pop():T;
9 }
10
11 class Main {
12 public static function main () {
13 var a = [new Child()];
14 var b:MyArray<Base> = a;
15 }
16 }
We can safely assign with b being typed as MyArray<Base> and MyArray only having a
pop() method. There is no method defined on MyArray which could be used to add incompat-
ible types, it is thus said to be covariant.
Definition: Covariance
A compound type (2) is considered covariant if its component types can be assigned to less
specific components, i.e. if they are only read, but never written.
Definition: Contravariance
A compound type (2) is considered contravariant if its component types can be assigned to
less generic components, i.e. if they are only written, but never read.
47
3.5 Unification
Mention
toString()/String con- Unification is the heart of the type system and contributes immensely to the robustness of
version somewhere in Haxe programs. It describes the process of checking if a type is compatible to another type.
this chapter.
Definition: Unification
Unification between two types A and B is a directional process which answers the question
if A can be assigned to B. It may mutate either type if it is or has a monomorph (2.9).
We try to assign a value of type Int to a variable of type String, which causes the compiler
to try and unify Int with String. This is, of course, not allowed and makes the compiler emit the
error Int should be String.
In this particular case, the unification is triggered by an assignment, a context in which the “is
assignable to” definition is intuitive. It is one of several cases where unification is performed:
48
• class to implementing interface
• interface to base interface
These rules are transitive, meaning that a child class can also be assigned to the base class of its
base class, an interface its base class implements, the base interface of an implementing interface
”parent class” should and so on.
probably be used
here, but I have no 3.5.2 Structural Subtyping
idea what it means,
so I will refrain from
changing it myself. Definition: Structural Subtyping
Structural subtyping defines an implicit relation between types that have the same structure.
The following example is part of the Lambda class of the Haxe Standard Library (10):
1 public static function empty<T>(it : Iterable<T>):Bool {
2 return !it.iterator().hasNext();
3 }
The empty-method checks if an Iterable has an element. For this purpose, it is not necessary
to know anything about the argument type other than the fact that it is considered an iterable.
This allows calling the empty-method with any type that unifies with Iterable<T> which
applies to a lot of types in the Haxe Standard Library.
This kind of typing can be very convenient but extensive use may be detrimental to perfor-
mance on static targets, which is detailed in Impact on Performance (Section 2.5.4).
3.5.3 Monomorphs
Unification of types having or being a monomorph (2.9) is detailed in Type Inference (Section 3.6).
49
3.5.5 Common Base Type
Given a set of multiple types, a common base type is a type which all types of the set unify against:
1 class Base {
2 public function new() { }
3 }
4
5 class Child1 extends Base { }
6 class Child2 extends Base { }
7
8 class Main {
9 static public function main() {
10 var a = [new Child1(), new Child2()];
11 $type(a); // Array<Base>
12 }
13 }
Although Base is not mentioned, the Haxe Compiler manages to infer it as the common type
of Child1 and Child2. The Haxe Compiler employs this kind of unification in the following
situations:
• array declarations
• if/else
• cases of a switch
The special construct $type was previously mentioned in order to simplify the explanation of
the Function Type (Section 2.6) type, so let us now introduce it officially:
Construct: $type
$type is a compile-time mechanism being called like a function, with a single argument. The
compiler evaluates the argument expression and then outputs the type of that expression.
In the example above, the first $type prints Unknown<0>. This is a monomorph (2.9), a type
that is not yet known. The next line x = "foo" assigns a String literal to x, which causes
the unification (3.5) of the monomorph with String. We then see that the type of x indeed has
changed to String.
50
Whenever a type other than Dynamic (Section 2.7) is unified with a monomorph, that monomorph
becomes that type: it morphs into that type. Therefore it cannot morph into a different type after-
wards, a property expressed in the mono part of its name.
Following the rules of unification, type inference can occur in compound types:
1 class Main {
2 public static function main() {
3 var x = [];
4 $type(x); // Array<Unknown<0>>
5 x.push("foo");
6 $type(x); // Array<String>
7 }
8 }
Variable x is first initialized to an empty Array. At this point we can tell that the type of x is an
array, but we do not yet know the type of the array elements. Consequentially, the type of x is
Array<Unknown<0>>. It is only after pushing a String onto the array that we know the type
to be Array<String>.
A good example are arrays of mixed types. As mentioned in Dynamic (Section 2.7), the com-
piler refuses [1, "foo"] because it cannot determine an element type. Employing top-down
inference, this can be overcome:
1 class Main {
2 static public function main() {
3 var a:Array<Dynamic> = [1, "foo"];
4 }
5 }
Here, the compiler knows while typing [1, "foo"] that the expected type is Array<Dynamic>,
so the element type is Dynamic. Instead of the usual unification behavior where the compiler
would attempt (and fail) to determine a common base type (3.5.5), the individual elements are
typed against and unified with Dynamic.
We have seen another interesting use of top-down inference when construction of generic
type parameters (3.3.1) was introduced:
1 import haxe.Constraints;
2
3 class Main {
4 static public function main() {
5 var s:String = make();
6 var t:haxe.Template = make();
51
7 }
8
9 @:generic
10 static function make<T:Constructible<String->Void>>():T {
11 return new T("foo");
12 }
13 }
The explicit types String and haxe.Template are used here to determine the return type
of make. This works because the method is invoked as make(), so we know the return type will
be assigned to the variables. Utilizing this information, it is possible to bind the unknown type
T to String and haxe.Template respectively.
3.6.2 Limitations
Type inference saves a lot of manual type hints when working with local variables, but sometimes
the type system still needs some help. In fact, it does not even try to infer the type of a variable
(4.1) or property (4.2) field unless it has a direct initialization.
There are also some cases involving recursion where type inference has limitations. If a func-
tion calls itself recursively while its type is not (completely) known yet, type inference may infer
a wrong, too specialized type.
A different kind of limitation involves the readability of code. If type inference is overused
it might be difficult to understand parts of a program due to the lack of visible types. This is
particularly true for method signatures. It is recommended to find a good balance between type
inference and explicit type hints.
Definition: Module
All Haxe code is organized in modules, which are addressed using paths. In essence, each
.hx file represents a module which may contain several types. A type may be private, in
which case only its containing module can access it.
The distinction of a module and its containing type of the same name is blurry by design. In fact,
addressing haxe.ds.StringMap<Int> can be considered shorthand for haxe.ds.StringMap.StringMa
The latter version consists of four parts:
If the module and type name are equal, the duplicate can be removed, leading to the haxe.ds.StringMap<In
short version. However, knowing about the extended version helps with understanding how
module sub-types (3.7.1) are addressed.
Paths can be shortened further by using an import (3.7.2), which typically allows omitting the
package part of a path. This may lead to usage of unqualified identifiers, for which understand-
ing the resolution order (3.7.3) is required.
52
Definition: Type path
The (dot-)path to a type consists of the package, the module name and the type name. Its
general form is pack1.pack2.packN.ModuleName.TypeName.
The accessibility of types can be controlled more fine-grained by using access control (6.10).
3.7.2 Import
If a type path is used multiple times in a .hx file, it might make sense to use an import to shorten
it. This allows omitting the package when using the type:
1 import haxe.ds.StringMap;
2
3 class Main {
4 static public function main() {
5 // instead of: new haxe.ds.StringMap();
6 new StringMap();
7 }
8 }
With haxe.ds.StringMap being imported in the first line, the compiler is able to resolve the
unqualified identifier StringMap in the main function to this package. The module StringMap
is said to be imported into the current file.
In this example, we are actually importing a module, not just a specific type within that mod-
ule. This means that all types defined within the imported module are available:
53
1 import haxe.macro.Expr;
2
3 class Main {
4 static public function main() {
5 var e:Binop = OpAdd;
6 }
7 }
The type Binop is an enum (2.4) declared in the module haxe.macro.Expr, and thus avail-
able after the import of said module. If we were to import only a specific type of that module,
e.g. import haxe.macro.Expr.ExprDef, the program would fail to compile with Class
not found : Binop.
There are several aspects worth knowing about importing:
• The bottommost import takes priority (detailed in Resolution Order (Section 3.7.3)).
• The static extension (6.3) keyword using implies the effect of import.
• If an enum is imported (directly or as part of a module import), all its enum constructors
(2.4.1) are also imported (this is what allows the OpAdd usage in the above example).
Furthermore, it is also possible to import static fields (4) of a class and use them unqualified:
1 import Math.random;
2
3 class Main {
4 static public function main() {
5 random();
6 }
7 }
Special care has to be taken with field names or local variable names that conflict with a
package name: Since they take priority over packages, a local variable named haxe blocks off
usage the entire haxe package.
Wildcard import Haxe allows using .* to allow import of all modules in a package, all types
in a module or all static fields in a type. It is important to understand that this kind of import
only crosses a single level as we can see in the following example:
1 import haxe.macro.*;
2
3 class Main {
4 static function main() {
5 var expr:Expr = null;
6 //var expr:ExprDef = null; // Class not found : ExprDef
7 }
8 }
Using the wildcard import on haxe.macro allows accessing Expr which is a module in this
package, but it does not allow accessing ExprDef which is a sub-type of the Expr module. This
rule extends to static fields when a module is imported.
When using wildcard imports on a package the compiler does not eagerly process all modules
in that package. This means that these modules are never actually seen by the compiler unless
used explicitly and are then not part of the generated output.
54
Import with alias If a type or static field is used a lot in an importing module it might help
to alias it to a shorter name. This can also be used to disambiguate conflicting names by giving
them a unique identifier.
1 import String.fromCharCode in f;
2
3 class Main {
4 static function main() {
5 var c1 = f(65);
6 var c2 = f(66);
7 trace(c1 + c2); // AB
8 }
9 }
55
6. If the current class has a static field named i, resolve to it and halt.
7. If an enum constructor named i is declared on an imported enum, resolve to it and halt.
8. If a static named i is explicitly imported, resolve to it and halt.
9. If i starts with a lower-case character, go to 11.
For step 10, it is also necessary to define the resolution order of types:
2. If the current package contains a module named i with a type named i, resolve to it and
halt.
3. If a type named i is available at top-level, resolve to it and halt.
4. Fail
For step 1 of this algorithm as well as steps 5 and 7 of the previous one, the order of import
resolution is important:
• Imported modules and static extensions are checked from bottom to top with the first match
being picked.
56
Yes
’i’ == ’true’, ’false’, ’this’, ’super’ or ’null’
No
Yes
Local variable ’i’ exists
No
Yes
Current field is static?
No
Yes
Current class or parent class have field ’i’
No
Yes
Static extension with ’this’-type
No
Yes
Current class has static field ’i’
No
Yes
Imported enum has constructor ‘i’
No
Yes
Static ‘i’ imported
No
Yes
‘i’ starts with lower-case character
No
Yes
Type ‘i’ imported
No
Yes
Current package contains module ‘i’ with type ‘i’
No
Yes
Top-level type ‘i’ exists
No
No
Untyped mode Fail
Yes
Yes
‘i’ == ‘ this ’
No
57
Chapter 4
Class Fields
So far we have seen how types and Haxe programs in general are structured. This section about
class fields concludes the structural part and at the same time bridges to the behavioral part of
Haxe. This is because class fields are the place where expressions (5) are at home.
There are three kinds of class fields:
Variable: A variable (4.1) class field holds a value of a certain type, which can be read or written.
Property: A property (4.2) class field defines a custom access behavior for something that, out-
side the class, looks like a variable field.
Method: A method (4.3) is a function which can be called to execute code.
Strictly speaking, a variable could be considered to be a property with certain access modifiers.
Indeed, the Haxe Compiler does not distinguish variables and properties during its typing phase,
but they remain separated at syntax level.
Regarding terminology, a method is a (static or non-static) function belonging to a class. Other
functions, such as a local functions (5.11) in expressions, are not considered methods.
4.1 Variable
We have already seen variable fields in several code examples of previous sections. Variable
fields hold values, a characteristic which they share with most (but not all) properties:
1 class Main {
2 static var member:String = "bar";
3
4 public static function main() {
5 trace(member);
6 member = "foo";
7 trace(member);
8 }
9 }
58
We can learn from this that a variable
The example first prints the initialization value of member, then sets it to "foo" before print-
ing its new value. The effect of access modifiers is shared by all three class field kinds and
explained in a separate section.
It should be noted that the explicit type is not required if there is an initialization value. The
compiler will infer (3.6) it in this case.
inline
Yes No
static extern
No
No Yes Yes
No Yes
No ’this’ Anything
4.2 Property
Next to variables (4.1), properties are the second option for dealing with data on a class. Unlike
variables however, they offer more control of which kind of field access should be allowed and
how it should be generated. Common use cases include:
• Have a field which can be read from anywhere, but only be written from within the defining
class.
59
• Have a field which invokes a getter-method upon read-access.
• Have a field which invokes a setter-method upon write-access.
When dealing with properties, it is important to understand the two kinds of access:
Read access and write access are directly reflected in the syntax, as the following example
shows:
1 class Main {
2 public var x(default, null):Int;
3 static public function main() { }
4 }
For the most part, the syntax is similar to variable syntax, and the same rules indeed apply.
Properties are identified by
The access identifiers define the behavior when the field is read (first identifier) and written
(second identifier). The accepted values are:
default: Allows normal field access if the field has public visibility, otherwise equal to null
access.
null: Allows access only from within the defining class.
get/set: Access is generated as a call to an accessor method. The compiler ensures that the acces-
sor is available.
dynamic: Like get/set access, but does not verify the existence of the accessor field.
never: Allows no access at all.
60
Trivia: Accessor names
In Haxe 2, arbitrary identifiers were allowed as access identifiers and would lead to custom
accessor method names to be admitted. This made parts of the implementation quite tricky
to deal with. In particular, Reflect.getProperty() and Reflect.setProperty() had
to assume that any name could have been used, requiring the target generators to generate
meta-information and perform lookups.
We disallowed these identifiers and went for the get_ and set_ naming convention which
greatly simplified implementation. This was one of the breaking changes between Haxe 2
and 3.
61
1 var Main = function() {
2 var v = this.get_x();
3 this.set_x(2);
4 var _g = this;
5 _g.set_x(_g.get_x() + 1);
6 };
As specified, the read access generates a call to get_x(), while the write access generates a
call to set_x(2) where 2 is the value being assigned to x. The way the += is being generated
might look a little odd at first, but can easily be justified by the following example:
1 class Main {
2 public var x(get, set):Int;
3 function get_x() return 1;
4 function set_x(x) return x;
5
6 public function new() { }
7
8 static public function main() {
9 new Main().x += 1;
10 }
11 }
What happens here is that the expression part of the field access to x in the main method
is complex: It has potential side-effects, such as the construction of Main in this case. Thus, the
compiler cannot generate the += operation as new Main().x = new Main().x + 1 and has
to cache the complex expression in a local variable:
1 Main.main = function() {
2 var _g = new Main();
3 _g.set_x(_g.get_x() + 1);
4 }
The method get_x is missing, but it need not be declared on the class defining the property
itself as long as a parent class defines it:
1 class Base {
2 public function get_x() return 1;
3 }
4
62
5 class Main extends Base {
6 // ok, get_x is declared by parent class
7 public var x(get, null):Int;
8
9 static public function main() {}
10 }
The dynamic access modifier works exactly like get or set, but does not check for the
existence
• a property (4.2) with the read-access or write-access identifier being default or null
• a property (4.2) with :isVar metadata (6.9)
If this is not the case, access to the field from within an accessor method causes a compilation
error:
1 class Main {
2 // This field cannot be accessed because it is not a real variable
3 public var x(get, set):Int;
4
5 function get_x() {
6 return x;
7 }
8
9 function set_x(x) {
10 return this.x = x;
63
11 }
12
13 static public function main() {}
14 }
If a physical field is indeed intended, it can be forced by attributing the field in question with
the :isVar metadata (6.9):
1 class Main {
2 // @isVar forces the field to be physical allowing the program to
compile.
3 @:isVar public var x(get, set):Int;
4
5 function get_x() {
6 return x;
7 }
8
9 function set_x(x) {
10 return this.x = x;
11 }
12
13 static public function main() {}
14 }
4.3 Method
While variables (4.1) hold data, methods are defining behavior of a program by hosting expres-
sions (5). We have seen method fields in every code example of this document with even the
initial Hello World (1.3) example containing a main method:
1 class Main {
2 static public function main():Void {
3 trace("Hello World");
4 }
5 }
Methods are identified by the function keyword. We can also learn that they
64
4. may have access modifiers (4.4) (here: static and public) and
5. may have an expression (here: {trace("Hello World");}).
We can also look at the next example to learn more about arguments and return types:
1 class Main {
2 static public function main() {
3 myFunc("foo", 1);
4 }
5
6 static function myFunc(f:String, i) {
7 return true;
8 }
9 }
Arguments are given by an opening parenthesis ( after the field name, a comma , separated
list of argument specifications and a closing parenthesis ). Additional information on the argu-
ment specification is described in Function Type (Section 2.6).
The example demonstrates how type inference (3.6) can be used for both argument and return
types. The method myFunc has two arguments but only explicitly gives the type of the first one,
f, as String. The second one, i, is not type-hinted and it is left to the compiler to infer its type
from calls made to it. Likewise, the return type of the method is inferred from the return true
expression as Bool.
65
• the class Child which extends Base and also has a method myMethod being declared
with override, and
• the Main class whose main method creates an instance of Child, assigns it to a variable
child of explicit type Base and calls myMethod() on it.
The section on Inheritance (Section 2.3.2) explains the use of super() from within a new
constructor.
66
11 class ChildChild extends Child {
12 public override function method(obj:Base):ChildChild {
13 return null;
14 }
15 }
16
17 class Main {
18 static public function main() { }
19 }
Intuitively, this follows from the fact that arguments are “written to” the function and the
return value is “read from” it.
The example also demonstrates how visibility (4.4.1) may be changed: An overriding field
may be public if the overridden field is private, but not the other way around.
It is not possible to override fields which are declared as inline (4.4.2). This is due to the
conflicting concepts: While inlining is done at compile-time by replacing a call with the function
body, overriding fields necessarily have to be resolved at runtime.
67
6 class Child1 extends Base {
7 private function child1Field() { }
8 }
9
10 class Child2 extends Base {
11 public function child2Field() {
12 var child1 = new Child1();
13 child1.baseField();
14 // Cannot access private field child1Field
15 child1.child1Field();
16 }
17 }
18
19 class Main {
20 static public function main() { }
21 }
We can see that access to child1.baseField() is allowed from within Child2 even though
child1 is of a different type, Child1. This is because the field is defined on their common ances-
tor class Base, contrary to field child1Field which can not be accessed from within Child2.
Omitting the visibility modifier usually defaults the visibility to private, but there are ex-
ceptions where it becomes public instead:
Trivia: Protected
Haxe has no notion of a protected keyword known from Java, C++ and other object-
oriented languages. However, its private behavior is equal to those language’s protected
behavior, so Haxe actually lacks their real private behavior.
4.4.2 Inline
The inline keyword allows function bodies to be directly inserted in place of calls to them. This
can be a powerful optimization tool, but should be used judiciously as not all functions are good
candidates for inline behavior. The following example demonstrates the basic usage:
1 class Main {
2 static inline function mid(s1:Int, s2:Int) {
3 return (s1 + s2) / 2;
4 }
5
6 static public function main() {
7 var a = 1;
8 var b = 2;
9 var c = mid(a, b);
68
10 }
11 }
The generated JavaScript output reveals the effect of inline:
1 (function () { "use strict";
2 var Main = function() { }
3 Main.main = function() {
4 var a = 1;
5 var b = 2;
6 var c = (a + b) / 2;
7 }
8 Main.main();
9 })();
As evident, the function body (s1 + s2) / 2 of field mid was generated in place of the
call to mid(a, b), with s1 being replaced by a and s2 being replaced by b. This avoids a
function call which, depending on the target and frequency of occurrences, may yield noticeable
performance improvements.
It is not always easy to judge if a function qualifies for being inline. Short functions that have
no writing expressions (such as a = assignment) are usually a good choice, but even more com-
plex functions can be candidates. However, in some cases inlining can actually be detrimental
to performance, e.g. because the compiler has to create temporary variables for complex expres-
sions.
Inline is not guaranteed to be done. The compiler might cancel inlining for various reasons
or a user could supply the --no-inline command line argument to disable inlining. The only
exception is if the class is extern (6.2) or if the class field has the :extern metadata (6.9), in
which case inline is forced. If it cannot be done, the compiler emits an error.
It is important to remember this when relying on inline:
1 class Main {
2 public static function main () { }
3
4 static function test() {
5 if (Math.random() > 0.5) {
6 return "ok";
7 } else {
8 error("random failed");
9 }
10 }
11
12 @:extern static inline function error(s:String) {
13 throw s;
14 }
15 }
If the call to error is inlined the program compiles correctly because the control flow checker
is satisfied due to the inlined throw (5.22) expression. If inline is not done, the compiler only sees
a function call to error and emits the error A return is missing here.
4.4.3 Dynamic
Methods can be denoted with the dynamic keyword to make them (re-)bindable:
69
1 class Main {
2 static dynamic function test() {
3 return "original";
4 }
5
6 static public function main() {
7 trace(test()); // original
8 test = function() { return "new"; }
9 trace(test()); // new
10 }
11 }
The first call to test() invokes the original function which returns the String "original".
In the next line, test is assigned a new function. This is precisely what dynamic allows: Func-
tion fields can be assigned a new function. As a result, the next invocation of test() returns the
String "new".
Dynamic fields cannot be inline for obvious reasons: While inlining is done at compile-
time, dynamic functions necessarily have to be resolved at runtime.
4.4.4 Override
The access modifier override is required when a field is declared which also exists on a parent
class (2.3.2). Its purpose is to ensure that the author of a class is aware of the override as this may
not always be obvious in large class hierarchies. Likewise, having override on a field which
does not actually override anything (e.g. due to a misspelled field name) triggers an error.
The effects of overriding fields are detailed in Overriding Methods (Section 4.3.1). This mod-
ifier is only allowed on method (4.3) fields.
4.4.5 Static
All fields are member fields unless the modifier static is used. Static fields are used “on the
class” whereas non-static fields are used “on a class instance”:
1 class Main {
2 static function main() {
3 Main.staticField; // static read
4 Main.staticField = 2; // static write
5 }
6
7 static var staticField:Int;
8 }
Static variable (4.1) and property (4.2) fields can have arbitrary initialization expressions (5).
70
Chapter 5
Expressions
Expressions in Haxe define what a program does. Most expressions are found in the body of a
method (4.3), where they are combined to express what that method should do. This section
explains the different kinds of expressions. Some definitions help here:
Definition: Name
A general name may refer to
• a type,
• a local variable,
• a local function or
• a field.
Definition: Identifier
Haxe identifiers start with an underscore _, a dollar $, a lower-case character a-z or an
upper-case character A-Z. After that, any combination and number of _, A-Z, a-z and 0-9
may follow.
Further limitations follow from the usage context, which are checked upon typing:
• Type names must start with an upper-case letter A-Z or an underscore _.
• Leading dollars are not allowed for any kind of name (5) (dollar-names are mostly used
for macro reification (9.3)).
Haxe reserves the identifier prefix _hx_ for internal use. This is not enforced by the parser or
typer.
• abstract
• break
71
• case
• cast
• catch
• class
• continue
• default
• do
• dynamic
• else
• enum
• extends
• extern
• false
• for
• function
• if
• implements
• import
• in
• inline
• interface
• macro
• new
• null
• override
• package
• private
• public
• return
• static
• switch
72
• this
• throw
• true
• try
• typedef
• untyped
• using
• var
• while
Related content
• Haxe Code Cookbook article: Everything is an expression.
5.1 Blocks
A block in Haxe starts with an opening curly brace { and ends with a closing curly brace }. A
block may contain several expressions, each of which is followed by a semicolon ;. The general
syntax is thus:
1 {
2 expr1;
3 expr2;
4 ...
5 exprN;
6 }
The value and by extension the type of a block-expression is equal to the value and the type of
the last sub-expression.
Blocks can contain local variables declared by var expression (5.10), as well as local func-
tions declared by function expressions (5.11). These are available within the block and within
sub-blocks, but not outside the block. Also, they are available only after their declaration. The
following example uses var, but the same rules apply to function usage:
1 {
2 a; // error, a is not declared yet
3 var a = 1; // declare a
4 a; // ok, a was declared
5 {
6 a; // ok, a is available in sub-blocks
7 }
8 // ok, a is still available after
9 // sub-blocks
10 a;
11 }
12 a; // error, a is not available outside
At runtime, blocks are evaluated from top to bottom. Control flow (e.g. exceptions (5.18) or
return expressions (5.19)) may leave a block before all expressions are evaluated.
73
Variable Shadowing Haxe allows local variable shadowing within the same block. This means
that a var or function can be declared with the same name that was previously available in a
block, effectively hiding it from the further code:
1 {
2 var v = 42; // declare v
3 $type(v); // Int
4 var v = "hi"; // declare a new v
5 $type(v); // String, previous declaration is not available
6 }
It might come as a surprise that this is allowed, but it’s useful to avoid pollution of local name
space and thus prevent accidental usage of a wrong variable.
Note, that the shadowing strictly follows syntax, so if a variable was captured in a closure
before it was shadowed, that closure would still reference the original declaration:
1 {
2 var a = 1;
3 function f() {
4 trace(a);
5 }
6 var a = 2;
7 f(); // traces 1
8 }
5.2 Constants
The Haxe syntax supports the following constants:
Int: An integer (2.1.1), such as 0, 1, 97121, -12, 0xFF0000.
Float: A floating point number (2.1.1), such as 0.0, 1., .3, -93.2.
String: A string of characters (10.1), such as "", "foo", ’’, ’bar’.
true,false: A boolean (2.1.4) value.
null: The null value.
Furthermore, the internal syntax structure treats identifiers (5) as constants, which may be
relevant when working with macros (9).
74
1 var a = new Array();
2 a.push(1);
3 a.push(2);
4 a.push(3);
This should be considered when deciding if a function should be inlined (4.4.2) as it may inline
more code than visible in the syntax.
Advanced initialization techniques are described in Array Comprehension (Section 6.6).
• expr is an abstract type (2.8) which defines a matching array access (2.8.3)
75
5.9 Function Call
Functions calls consist of an arbitrary subject expression followed by an opening parenthesis (,
a comma , separated list of expressions as arguments and a closing parenthesis ).
1 subject(); // call with no arguments
2 subject(e1); // call with one argument
3 subject(e1, e2); // call with two arguments
4 // call with multiple arguments
5 subject(e1, ..., eN);
Related content
• Haxe Code Cookbook article: How to declare functions
5.10 var
The var keyword allows declaring multiple variables, separated by comma ,. Each variable
has a valid identifier (5) and optionally a value assignment following the assignment operator =.
Variables can also have an explicit type-hint.
1 var a; // declare local a
2 var b:Int; // declare variable b of type Int
3 // declare variable c, initialized to value 1
4 var c = 1;
5 // declare an uninitialized variable d
6 // and variable e initialized to value 2
7 var d,e = 2;
76
1 var myLocalFunction = function(a) { }
However, there are some differences related to type parameters and the position of the func-
tion. We speak of a “lvalue” function if it is not assigned to anything upon its declaration, and
an “rvalue” function otherwise.
• Lvalue functions require a name and can have type parameters (3.2).
• Rvalue functions may have a name, but cannot have type parameters.
5.12 new
The new keyword signals that a class (2.3) or an abstract (2.8) is being instantiated. It is followed
by the type path (3.7) of the type which is to be instantiated. It may also list explicit type param-
eters (3.2) enclosed in <> and separated by comma ,. After an opening parenthesis ( follow the
constructor arguments, again separated by comma ,, with a closing parenthesis ) at the end.
1 class Main<T> {
2 static public function main() {
3 new Main<Int>(12, "foo");
4 }
5
6 function new(t:T, s:String) { }
7 }
Within the main method we instantiate an instance of Main itself, with an explicit type pa-
rameter Int and the arguments 12 and "foo". As we can see, the syntax is very similar to the
function call syntax (5.9) and it is common to speak of “constructor calls”.
5.13 for
Haxe does not support traditional for-loops known from C. Its for keyword expects an opening
parenthesis (, then a variable identifier followed by the keyword in and an arbitrary expres-
sion used as iterating collection. After the closing parenthesis ) follows an arbitrary loop body
expression.
1 for (v in e1) e2;
The typer ensures that the type of e1 can be iterated over, which is typically the case if it has
an iterator (6.7) method returning an Iterator<T>, or if it is an Iterator<T> itself.
Variable v is then available within loop body e2 and holds the value of the individual ele-
ments of collection e1.
1 var list = ["apple", "pear", "banana"];
2 for (v in list) {
3 trace(v);
4 }
5 // apple
6 // pear
7 // banana
77
Range iteration Haxe has a special range operator to iterate over intervals. It is a binary oper-
ator taking two Int operands: min...max returns an IntIterator instance that iterates from min
(inclusive) to max (exclusive). Note that max may not be smaller than min.
1 for (i in 0...10) trace(i); // 0 to 9
The type of a for expression is always Void, meaning it has no value and cannot be used as
right-side expression.
The control flow of loops can be affected by break (5.20) and continue (5.21) expressions.
1 for (i in 0...10) {
2 if (i == 2) continue; // skip 2
3 if (i == 5) break; // stop at 5
4 trace(i);
5 }
6 // 0
7 // 1
8 // 3
9 // 4
Related content
• Manual: Haxe iterators documentation (6.7), Haxe Data Structures documentation (10.2)
5.14 while
A normal while loop starts with the while keyword, followed by an opening parenthesis (, the
condition expression and a closing parenthesis ). After that follows the loop body expression:
1 while(condition) expression;
The condition expression has to be of type Bool.
Upon each iteration, the condition expression is evaluated. If it evaluates to false, the loop
stops, otherwise it evaluates the loop body expression.
1 class Main {
2 static public function main() {
3 var f = 0.0;
4 while (f < 0.5) {
5 trace(f);
6 f = Math.random();
7 }
8 }
9 }
This kind of while-loop is not guaranteed to evaluate the loop body expression at all: If the
condition does not hold from the start, it is never evaluated. This is different for do-while loops
(5.15).
78
5.15 do-while
A do-while loop starts with the do keyword followed by the loop body expression. After that
follows the while keyword, an opening parenthesis (, the condition expression and a closing
parenthesis ):
1 do expression while(condition);
The condition expression has to be of type Bool.
As the syntax suggests, the loop body expression is always evaluated at least once, unlike
while (5.14) loops.
5.16 if
Conditional expressions come in the form of a leading if keyword, a condition expression en-
closed in parentheses () and a expression to be evaluated in case the condition holds:
1 if (condition) expression;
The condition expression has to be of type Bool.
Optionally, expression may be followed by the else keyword as well as another expres-
sion to be evaluated if the condition does not hold:
1 if (condition) expression1 else expression2;
Here, expression2 may consist of another if expression:
1 if (condition1) expression1
2 else if(condition2) expression2
3 else expression3
If the value of an if expression is required, e.g. for var x = if(condition) expression1
else expression2, the typer ensures that the types of expression1 and expression2
unify (3.5). If no else expression is given, the type is inferred to be Void.
5.17 switch
A basic switch expression starts with the switch keyword and the switch subject expression, as
well as the case expressions between curly braces {}. Case expressions either start with the case
keyword and are followed by a pattern expression, or consist of the default keyword. In both
cases a colon : and an optional case body expression follows:
1 switch subject {
2 case pattern1: case-body-expression-1;
3 case pattern2: case-body-expression-2;
4 default: default-expression;
5 }
Case body expressions never “fall through”, so the break (5.20) keyword is not supported in
Haxe.
Switch expressions can be used as value; in that case the types of all case body expressions
and the default expression must unify (3.5).
79
Related content
• Further details on syntax of pattern expressions are detailed in Pattern Matching (Sec-
tion 6.4).
• Snippets and tutorials about pattern matching in the Haxe Code Cookbook.
5.18 try/catch
Haxe allows catching values using its try/catch syntax:
1 try try-expr
2 catch(varName1:Type1) catch-expr-1
3 catch(varName2:Type2) catch-expr-2
If during runtime the evaluation of try-expression causes a throw (5.22), it can be caught
by any subsequent catch block. These blocks consist of
• a variable name which holds the thrown value,
• an explicit type annotation which determines which types of values to catch, and
• the expression to execute in that case.
Haxe allows throwing and catching any kind of value, it is not limited to types inheriting
from a specific exception or error class. Catch blocks are checked from top to bottom with the
first one whose type is compatible with the thrown value being picked.
This process has many similarities to the compile-time unification (3.5) behavior. However,
since the check has to be done at runtime there are several restrictions:
• The type must exist at runtime: Class instances (2.3), enum instances (2.4), abstract core
types (2.8.6) and Dynamic (2.7).
• Type parameters can only be Dynamic (2.7).
5.19 return
A return expression can come with or without an value expression:
1 return;
2 return expression;
It leaves the control-flow of the innermost function it is declared in, which has to be distin-
guished when local functions (5.11) are involved:
1 function f1() {
2 function f2() {
3 return;
4 }
5 f2();
6 expression;
7 }
The return leaves local function f2, but not f1, meaning expression is still evaluated.
If return is used without a value expression, the typer ensures that the return type of the
function it returns from is of Void. If it has a value expression, the typer unifies (3.5) its type with
the return type (explicitly given or inferred by previous return expressions) of the function it
returns from.
80
5.20 break
The break keyword leaves the control flow of the innermost loop (for or while) it is declared
in, stopping further iterations:
1 while(true) {
2 expression1;
3 if (condition) break;
4 expression2;
5 }
Here, expression1 is evaluated for each iteration, but as soon as condition holds, expression2
is not evaluated anymore.
The typer ensures that it appears only within a loop. The break keyword in switch cases
(5.17) is not supported in Haxe.
5.21 continue
The continue keyword ends the current iteration of the innermost loop (for or while) it is
declared in, causing the loop condition to be checked for the next iteration:
1 while(true) {
2 expression1;
3 if(condition) continue;
4 expression2;
5 }
Here, expression1 is evaluated for each iteration, but if condition holds, expression2
is not evaluated for the current iteration. Unlike break, iterations continue.
The typer ensures that it appears only within a loop.
5.22 throw
Haxe allows throwing any kind of value using its throw syntax:
1 throw expr
A value which is thrown like this can be caught by catch blocks (5.18). If no such block
catches it, the behavior is target-dependent.
5.23 cast
Haxe allows two kinds of casts:
1 cast expr; // unsafe cast
2 cast (expr, Type); // safe cast
81
1 class Main {
2 public static function main() {
3 var i = 1;
4 $type(i); // Int
5 var s = cast i;
6 $type(s); // Unknown<0>
7 Std.parseInt(s);
8 $type(s); // String
9 }
10 }
Variable i is typed as Int and then assigned to variable s using the unsafe cast cast i. This
causes s to be of an unknown type, a monomorph. Following the usual rules of unification (3.5),
it can then be bound to any type, such as String in this example.
These casts are called ”unsafe” because the runtime behavior for invalid casts is not defined.
While most dynamic targets (2.2) are likely to work, it might lead to undefined errors on static
targets (2.2).
Unsafe casts have little to no runtime overhead.
82
5.24 type check
Since Haxe 3.1.0
This has the usual effect of both operations such as the given type being used as expected
type when performing unqualified identifier resolution (3.7.3) and the unification checking for
abstract casts (2.8.1).
83
Chapter 6
Language Features
84
Inlined calls (4.4.2):
Functions can be designated as being inline, allowing their code to be inserted at call-site.
This can yield significant performance benefits without resorting to code duplication via manual
inlining.
Iterators (6.7):
Iterating over a set of values, e.g. the elements of an array, is very easy in Haxe courtesy of
iterators. Custom classes can quickly implement iterator functionality to allow iteration.
1 for (i in [1, 2, 3]) {
2 trace(i);
3 }
Metadata (6.9):
Add metadata to fields, classes or expressions. This can communicate information to the
compiler, macros, or runtime classes.
1 class MyClass {
2 @range(1, 8) var value:Int;
3 }
4 trace(haxe.rtti.Meta.getFields(MyClass).value.range); // [1,8]
Static Extensions (6.3):
Existing classes and other types can be augmented with additional functionality through us-
ing static extensions.
1 using StringTools;
2 " Me & You ".trim().htmlEscape();
String Interpolation (6.5):
Strings declared with a single quotes are able to access variables in the current context.
1 trace(’My name is $name and I work in ${job.industry}’);
Partial function application (6.8):
Any function can be applied partially, providing the values of some arguments and leaving
the rest to be filled in later.
1 var map = new haxe.ds.IntMap();
2 var setToTwelve = map.set.bind(_, 12);
3 setToTwelve(1);
4 setToTwelve(2);
Pattern Matching (6.4):
Complex structures can be matched against patterns, extracting information from an enum
or a structure and defining specific operations for specific value combination.
85
1 var a = { foo: 12 };
2 switch (a) {
3 case { foo: i }: trace(i);
4 default:
5 }
Properties (4.2):
Variable class fields can be designed as properties with custom read and write access, allow-
ing fine grained access control.
1 public var color(get,set);
2 function get_color() {
3 return element.style.backgroundColor;
4 }
5 function set_color(c:String) {
6 trace(’Setting background of element to $c’);
7 return element.style.backgroundColor = c;
8 }
86
4 trace("ok");
5 #elseif (debug_level > 3)
6 trace(3);
7 #else
8 trace("debug level too low");
9 #end
10 }
11 }
Compiling this without any flags will leave only the trace("ok"); line in the body of the
main method. The other branches are discarded while parsing the file. These other branches
must still contain valid Haxe syntax, but the code is not type-checked.
The conditions after #if and #elseif allow the following expressions:
• Any identifier is replaced by the value of the compiler flag by the same name. Note that
-D some-flag from command line leads to the flags some-flag and some_flag to be
defined.
• The values of String, Int and Float constants are used directly.
• The boolean operators && (and), || (or) and ! (not) work as expected, however the full
expression must be completely contained by parentheses.
• The operators ==, !=, >, >=, <, <= can be used to compare values.
• Parentheses () can be used to group expressions as usual.
The Haxe parser does not parse some-flag as a single token and instead reads it as a sub-
traction binary operator some - flag. In cases like this the underscore version some_flag
has to be used.
Working with compiler flags Compiler flags are available at compile time, the following meth-
ods only work in macro context:
Haxelibs By default, each used haxelib version is automatically added as flag, e.g. when you
add -lib actuate, the compiler adds -D actuate=1.8.7. To test if a library exists in current
context, use #if actuate. To check a specific haxelib version, use the operators, for example
#if (actuate <= "1.8.7")
Built-in Compiler Flags An exhaustive list of all built-in defines can be obtained by invoking
the Haxe Compiler with the --help-defines argument. The Haxe Compiler allows multiple
-D flags per compilation.
Related content
• See also the Compiler Flags list (7.2).
87
6.2 Externs
Externs can be used to describe target-specific interaction in a type-safe manner. They are defined
like normal classes, except that
A common example from the Haxe Standard Library (10) is the Math class, as an excerpt
shows:
1 extern class Math
2 {
3 static var PI(default,null) : Float;
4 static function floor(v:Float):Int;
5 }
We see that externs can define both methods and variables (actually, PI is declared as a read-
only property (4.2)). Once this information is available to the compiler, it allows field access
accordingly and also knows the types:
1 class Main {
2 static public function main() {
3 var pi = Math.floor(Math.PI);
4 $type(pi); // Int
5 }
6 }
This works because the return type of method floor is declared to be Int.
The Haxe Standard Library comes with many externs for the Flash and JavaScript target.
They allow accessing the native APIs in a type-safe manner and are instrumental for designing
higher-level APIs. There are also externs for many popular native libraries on haxelib (11).
The Flash, Java and C# targets allow direct inclusion of native libraries from command line
(7). Target-specific details are explained in the respective sections of Target Details (Chapter 12).
Some targets such as Python or JavaScript may require generating additional ”import” code
that loads an extern class from a native module. Haxe provides ways to declare such depen-
dencies also described in respective sections Target Details (Chapter 12).
The haxe.extern package provides two types that help mapping native semantics to Haxe:
Rest<T>: This type can be used as a final function argument to allow passing an arbitrary num-
ber of additional call arguments. The type parameter can be used to constrain these argu-
ments to a specific type.
EitherType<T1,T2>: This type allows using either of its parameter types, thus representing a
type choice. It can be nested to allow more than two different types.
88
1 import haxe.extern.Rest;
2 import haxe.extern.EitherType;
3
4 extern class MyExtern {
5 static function f1(s:String, r:Rest<Int>):Void;
6 static function f2(e:EitherType<Int, String>):Void;
7 }
8
9 class Main {
10 static function main() {
11 MyExtern.f1("foo", 1, 2, 3); // use 1, 2, 3 as rest argument
12 MyExtern.f1("foo"); // no rest argument
13 //MyExtern.f1("foo", "bar"); // String should be Int
14
15 MyExtern.f2("foo");
16 MyExtern.f2(12);
17 //MyExtern.f2(true); // Bool should be EitherType<Int, String>
18 }
19 }
Visibility Externs support the private visibility modifier. However, because the default visi-
bility in an extern class is public, private needs to be explicitly specified.
Specifying private members is helpful when an API intends to allow overriding functions.
Also, Haxe cannot prevent subclasses from reusing field names unless if the fields are included
in the extern definition. This is important on targets such as JavaScript where reusing a super
classs field name as a new field in a subclass is not supported.
1 extern class ExampleSuperClass
2 {
3 private function new(); // Require subclassing to use.
4 // Only allow subclasses access to this overridable function.
5 private function overridableFunction():String;
6 // This function is implicitly public:
7 function doSomething():String;
8 }
Static extensions can be a powerful tool which allows augmenting types without actually chang-
ing them. The following example demonstrates the usage:
1 using Main.IntExtender;
2
3 class IntExtender {
89
4 static public function triple(i:Int) {
5 return i * 3;
6 }
7 }
8
9 class Main {
10 static public function main() {
11 trace(12.triple());
12 }
13 }
Clearly, Int does not natively provide a triple method, yet this program compiles and out-
puts 36 as expected. This is because the call to 12.triple() is transformed into IntExtender.triple(12)
There are three requirements for this:
1. Both the literal 12 and the first argument of triple are known to be of type Int.
2. The class IntExtender is brought into context through using Main.IntExtender.
3. Int does not have a triple field by itself (if it had, that field would take priority over the
static extension).
Static extensions are usually considered syntactic sugar and indeed they are, but it is worth
noting that they can have a dramatic effect on code readability: Instead of nested calls in the form
of f1(f2(f3(f4(x)))), chained calls in the form of x.f4().f3().f2().f1() can be used.
Following the rules previously described in Resolution Order (Section 3.7.3), multiple using
expressions are checked from bottom to top, with the types within each module as well as the
fields within each type being checked from top to bottom. Using a module (as opposed to a
specific type of a module, see Modules and Paths (Section 3.7)) as static extension brings all its
types into context.
Related content
• Haxe snippets and tutorials about static extensions in the Haxe Code Cookbook.
90
The following classes from the Haxe Standard Library are designed to be used as static exten-
sions:
• The topmost pattern that matches the input value has its expression executed.
• A _ pattern matches anything, so case _: is equal to default:
Related content
• More about the switch expression (5.17).
• Haxe snippets and tutorials about pattern matching in the Haxe Code Cookbook.
91
1 var myTree = Node(Leaf("foo"), Node(Leaf("bar"), Leaf("foobar")));
2 var match = switch(myTree) {
3 // matches any Leaf
4 case Leaf(_): "0";
5 // matches any Node that has r = Leaf
6 case Node(_, Leaf(_)): "1";
7 // matches any Node that has has
8 // r = another Node, which has
9 // l = Leaf("bar")
10 case Node(_, Node(Leaf("bar"), _)): "2";
11 // matches anything
12 case _: "3";
13 }
14 trace(match); // 2
The pattern matcher will check each case from top to bottom and pick the first one that
matches the input value. The following manual interpretation of each case rule helps under-
standing the process:
92
Here, leafNode is bound to Leaf("foo") if the input matches that. In all other cases,
myTree itself is returned: case x works similar to case _ in that it matches anything, but
with an identifier name like x it also binds the matched value to that variable.
6.4.6 Or patterns
The | operator can be used anywhere within patterns to describe multiple accepted patterns:
1 var match = switch(7) {
2 case 4 | 1: "0";
3 case 6 | 7: "1";
4 case _: "2";
5 }
6 trace(match); // 1
If there is a captured variable in an or-pattern, it must appear in both its sub-patterns.
93
6.4.7 Guards
It is also possible to further restrict patterns with the case ... if(condition): syntax:
1 var myArray = [7, 6];
2 var s = switch(myArray) {
3 case [a, b] if (b > a):
4 b + ">" +a;
5 case [a, b]:
6 b + "<=" +a;
7 case _: "found something else";
8 }
9 trace(s); // 6<=7
The first case has an additional guard condition if (b > a). It will only be selected if that
condition holds, otherwise matching continues with the next case.
• The number of elements is fixed, so patterns of different array length will not be accepted.
• It is not possible to capture the switch value in a variable, i.e. case x is not allowed (case
_ still is).
6.4.9 Extractors
Since Haxe 3.1.0
Extractors allow applying transformations to values being matched. This is often useful when
a small operation is required on a matched value before matching can continue:
1 enum Test {
2 TString(s:String);
3 TInt(i:Int);
4 }
5
6 class Main {
7 static public function main() {
8 var e = TString("fOo");
9 switch(e) {
10 case TString(temp):
11 switch(temp.toLowerCase()) {
12 case "foo": true;
13 case _: false;
94
14 }
15 case _: false;
16 }
17 }
18 }
Here we have to capture the argument value of the TString enum constructor in a variable
temp and use a nested switch on temp.toLowerCase(). Obviously, we want matching to
succeed if TString holds a value of "foo" regardless of its casing. This can be simplified with
extractors:
1 enum Test {
2 TString(s:String);
3 TInt(i:Int);
4 }
5
6 class Main {
7 static public function main() {
8 var e = TString("fOo");
9 var success = switch(e) {
10 case TString(_.toLowerCase() => "foo"):
11 true;
12 case _:
13 false;
14 }
15 }
16 }
Extractors are identified by the extractorExpression => match expression. The com-
piler generates code which is similar to the previous example, but the original syntax was greatly
simplified. Extractors consist of two parts, which are separated by the => operator:
1. The left side can be any expression, where all occurrences of underscore _ are replaced with
the currently matched value.
2. The right side is a pattern which is matched against the result of the evaluation of the left
side.
Since the right side is a pattern, it can contain another extractor. The following example
“chains” two extractors:
1 class Main {
2 static public function main() {
3 switch(3) {
4 case add(_, 1) => mul(_, 3) => a:
5 trace(a);
6 }
7 }
8
9 static function add(i1:Int, i2:Int) {
10 return i1 + i2;
11 }
12
13 static function mul(i1:Int, i2:Int) {
95
14 return i1 * i2;
15 }
16 }
This traces 12 as a result of the calls to add(3, 1), where 3 is the matched value, and
mul(4, 3) where 4 is the result of the add call. It is worth noting that the a on the right side of
the second => operator is a capture variable (6.4.3).
It is currently not possible to use extractors within or-patterns (6.4.6):
1 class Main {
2 static public function main() {
3 switch("foo") {
4 // Extractors in or patterns are not allowed
5 case (_.toLowerCase() => "foo") | "bar":
6 }
7 }
8 }
However, it is possible to have or-patterns on the right side of an extractor, so the previous
example would compile without the parentheses.
The matched type Bool admits two values true and false, but only false is checked.
Figure out wtf our
rules are now for
when this is checked. 6.4.11 Useless pattern checks
In a similar fashion, the compiler detects patterns which will never match the input value:
1 switch(Leaf("foo")) {
2 case Leaf(_)
3 | Leaf("foo"): // This pattern is unused
4 case Node(l,r):
5 case _: // This pattern is unused
6 }
Furthermore, it is possible to include whole expressions in the string by using ${expr}, with
expr being any valid Haxe expression:
96
1 var x = 12;
2 // The sum of 12 and 3 is 15
3 trace(’The sum of $x and 3 is ${x + 3}’);
String interpolation is a compile-time feature and has no impact on the runtime. The above
example is equivalent to manual concatenation, which is exactly what the compiler generates:
1 trace("The sum of " + x +
2 " and 3 is " + (x + 3));
Of course the use of single-quote enclosed strings without any interpolation remains valid, but
care has to be taken regarding the $ character as it triggers interpolation. If an actual dollar-sign
should be used in the string, $$ can be used.
97
1 class Main {
2 static public function main() {
3 var a = [
4 for (a in 1...11)
5 for(b in 2...4)
6 if (a % b == 0)
7 a+ "/" +b
8 ];
9 // [2/2,3/3,4/2,6/2,6/3,8/2,9/3,10/2]
10 trace(a);
11 }
12 }
6.7 Iterators
With Haxe it is very easy to define custom iterators and iterable data types. These concepts are
represented by the types Iterator<T> and Iterable<T> respectively:
1 typedef Iterator<T> = {
2 function hasNext() : Bool;
3 function next() : T;
4 }
5
6 typedef Iterable<T> = {
7 function iterator() : Iterator<T>;
8 }
Any class (2.3) which structurally unifies (3.5.2) with one of these types can be iterated over
using a for-loop (5.13). That is, if the class defines methods hasNext and next with matching re-
turn types it is considered an iterator, if it defines a method iterator returning an Iterator<T>
it is considered an iterable type.
1 class MyStringIterator {
2 var s:String;
3 var i:Int;
4
5 public function new(s:String) {
6 this.s = s;
7 i = 0;
8 }
9
10 public function hasNext() {
11 return i < s.length;
12 }
13
14 public function next() {
15 return s.charAt(i++);
16 }
17 }
18
19 class Main {
20 static public function main() {
98
21 var myIt = new MyStringIterator("string");
22 for (chr in myIt) {
23 trace(chr);
24 }
25 }
26 }
The type MyStringIterator in this example qualifies as iterator: It defines a method
hasNext returning Bool and a method next returning String, making it compatible with
Iterator<String>. The main method instantiates it, then iterates over it.
1 class MyArrayWrap<T> {
2 var a:Array<T>;
3 public function new(a:Array<T>) {
4 this.a = a;
5 }
6
7 public function iterator() {
8 return a.iterator();
9 }
10 }
11
12 class Main {
13 static public function main() {
14 var myWrap = new MyArrayWrap([1, 2, 3]);
15 for (elt in myWrap) {
16 trace(elt);
17 }
18 }
19 }
Here we do not setup a full iterator like in the previous example, but instead define that the
MyArrayWrap<T> has a method iterator, effectively forwarding the iterator method of the
wrapped Array<T> type.
Related content
• See the Iterator API documentation.
• Haxe snippets and tutorials about iterators in the Haxe Code Cookbook.
99
7 f(1);
8 f(2);
9 f(3);
10 trace(map); // {1 => 12, 2 => 12, 3 => 12}
11 }
12 }
Line 4 binds the function map.set to a variable named f, and applies 12 as second argument.
The underscore _ is used to denote that this argument is not bound, which is shown by compar-
ing the types of map.set and f: The bound String argument is effectively cut from the type,
turning a Int->String->Void type into Int->Void.
A call to f(1) then actually invokes map.set(1, "12"), the calls to f(2) and f(3) are
analogous. The last line proves that all three indices indeed are mapped to the value "12".
The underscore _ can be skipped for trailing arguments, so the first argument could be bound
through map.set.bind(1), yielding a String->Void function that sets a new value for index
1 on invocation.
Optional arguments By default, trailing optional arguments are bound to their default values
and do not become arguments of the result function. This can be changed by using an explicit
underscore _ instead, in which case the optional argument of the original function becomes a
non-optional argument of the result function.
1 class Main {
2 static function test(a:Int, ?b:String):Void {}
3
4 static public function main() {
5 var fn = test.bind(1);
6 $type(fn); // Void->Void
7 fn(’foo’); //Compiler error: Too many arguments
8
9 var fn = test.bind(1, _);
10 $type(fn); // ?String->Void
11 fn(’foo’); //works
12 }
13 }
Trivia: Callback
Prior to Haxe 3, Haxe used to know a callback-keyword which could be called with a
function argument followed by any number of binding arguments. The name originated
from a common usage were a callback-function is created with the this-object being bound.
Callback would allow binding of arguments only from left to right as there was no support
for the underscore _. The choice to use an underscore was controversial and several other
suggestions were made, none of which were considered superior. After all, the underscore
_ at least looks like it’s saying “fill value in here”, which nicely describes its semantics.
6.9 Metadata
Several constructs can be attributed with custom metadata:
100
• Class fields
• Enum constructors
• Expressions
These metadata information can be obtained at runtime through the haxe.rtti.Meta API:
1 import haxe.rtti.Meta;
2
3 @author("Nicolas")
4 @:keep
5 class MyClass {
6 @range(1, 8)
7 var value:Int;
8
9 @broken
10 static function method() { }
11 }
12
13 class Main {
14 static public function main() {
15 // { author : ["Nicolas"] }
16 trace(Meta.getType(MyClass));
17 // [1,8]
18 trace(Meta.getFields(MyClass).value.range);
19 // { broken: null }
20 trace(Meta.getStatics(MyClass).method);
21 }
22 }
We can easily identify metadata by the leading @ character, followed by the metadata name
and, optionally, by a number of comma-separated constant arguments enclosed in parentheses.
• Class MyClass has an author metadata with a single String argument "Nicolas", as
well as a :keep metadata without arguments.
• The member variable value has a range metadata with two Int arguments 1 and 8.
• The static method method has a broken metadata without arguments.
The main method accesses these metadata values using their API. The output reveals the
structure of the obtained data:
• There is a field for each metadata, with the field name being the metadata name.
• The field values correspond to the metadata arguments. If there are no arguments, the field
value is null. Otherwise the field value is an array with one element per argument.
• Metadata starting with : is omitted. This kind of metadata is known as compile-time meta-
data.
Allowed values for metadata arguments are:
• Constants (5.2)
• Arrays declarations (5.5) (if all their elements qualify)
• Object declarations (5.6) (if all their field values qualify)
101
Built-in Compiler Metadata An exhaustive list of all defined metadata can be obtained by
running haxe --help-metas from command line.
Related content
• See also the Compiler Metadata list (8.1).
Here, MyClass.foo can be accessed from the main-method because MyClass is annotated
with @:allow(Main). This would also work with @:allow(Main.main) and both versions
could alternatively be annotated to the field foo instead of the class MyClass:
1 class MyClass {
2 @:allow(Main.main)
3 static private var foo: Int;
4 }
5
6 class Main {
7 static public function main() {
8 MyClass.foo;
9 }
10 }
102
If a type cannot be modified to allow this kind of access, the accessing method may force
access:
1 class MyClass {
2 static private var foo: Int;
3 }
4
5 class Main {
6 @:access(MyClass.foo)
7 static public function main() {
8 MyClass.foo;
9 }
10 }
The @:access(MyClass.foo) annotation effectively subverts the visibility of the foo field
within the main-method.
• Additional syntax often adds complexity to the language parsing, and also adds (too)
many keywords.
• Additional syntax requires additional learning by the language user, whereas metadata
syntax is something that is already known.
If access is allowed to an interface (2.3.3), it extends to all classes implementing that interface:
1 class MyClass {
2 @:allow(I)
3 static private var foo: Int;
4 }
5
6 interface I { }
7
8 class Main implements I {
9 static public function main() {
10 MyClass.foo;
11 }
12 }
103
This is also true for access granted to parent classes, in which case it extends to all child
classes.
If a constructor is declared to be inline (4.4.2), the compiler may try to optimize it away in
certain situations. There are several requirements for this to work:
• The result of the constructor call must be directly assigned to a local variable.
• The expression of the constructor field must only contain assignments to its fields.
104
Part II
Compiler Reference
105
Chapter 7
Compiler Usage
Basic Usage The Haxe Compiler is typically invoked from command line with several argu-
ments which have to answer two questions:
To answer the first question, it is usually sufficient to provide a class path via the -cp path
argument, along with the main class to be compiled via the -main dot_path argument. The
Haxe Compiler then resolves the main class file and begins compilation.
The second question usually comes down to providing an argument specifying the desired
target. Each Haxe target has a dedicated command line switch, such as -js file_name for
JavaScript and -php directory for PHP. Depending on the nature of the target, the argument
value is either a file name (for -js, -swf and neko) or a directory path.
-cp path Adds a class path where .hx source files or packages (sub-directories) can be found.
-lib library_name Adds a Haxelib (Chapter 11) library. By default the most recent ver-
sion in the local Haxelib repository is used. To use specific version, -lib library_-
name:version can be used.
Output:
-php directory Generates PHP (12.4) source code in specified directory. Use -D php7 for
PHP7 source code.
106
-cpp directory Generates C++ (12.5) source code in specified directory and compiles it using
native C++ compiler.
-cs directory Generates C# (12.8) source code in specified directory.
-java directory Generates Java (12.7) source code in specified directory and compiles it us-
ing the Java Compiler.
-python file_name.py Generates Python (12.9) source code in the specified file.
-lua file_name.lua Generates Lua (12.9) source code in the specified file.
-hl file_name.hl Generates HashLink (12.11) byte code in specified file.
107
Target specific arguments
--php-front <filename> Select the name for the php front file.
--php-lib <filename> Select the name for the php lib folder.
--php-prefix <name> Prefix all classes with given name.
-swf-version <version> Change the SWF version.
-swf-header <header> Define SWF header (width:height:fps:color).
-swf-lib <file> Add the SWF library to the compiled SWF.
-swf-lib-extern <file> Use the SWF library for type checking.
--flash-strict More type strict flash API.
-java-lib <file> Add an external JAR or class directory library.
-net-lib <file>[@std] Add an external .NET DLL file.
-net-std <file> Add a root std .NET DLL search path.
-c-arg <arg> Pass option arg to the native Java/C# compiler.
Global compiler configuration macros: In order to include single modules, their paths can be
listed directly on command line or in hxml: haxe ... ModuleName pack.ModuleName.
For more specific includes or excludes, use these initialization macros (9.7):
The full documentation of these methods can be found in the haxe.macro.Compiler API
documentation.
108
Help
haxe -version Print the current Haxe compiler version.
haxe -help Display this list of options.
haxe --help-defines Print help for all compiler specific defines (7.2).
Related content
• Compilation tutorials in the Haxe Code Cookbook.
7.1 HXML
Compiler arguments (7) can be stored in a .hxml file and can be executed with haxe <file.hxml>.
In hxml it is possible to use newlines and comments which makes it easier to maintain Haxe
build configurations. It is possible to supply more arguments after the hxml file, e.g. haxe
build.hxml -debug.
Example:
This example has a configuration which compiles the class file website.HomePage.hx to
JavaScript into a file called bin/homepage.js, which is located in the src class path. And uses
full dead code elimination.
1 -cp src
2 -dce full
3 -js bin/homepage.js
4 -main website.HomePage
Multiple build compilations Hxml configurations allow multiple compilation using these ar-
guments:
Example:
This example has a configuration which compiles three different classes into their own JavaScript
files. Each build uses src as class path and uses full dead code elimination.
1 -cp src
2 -dce full
3
4 --each
5
6 -js bin/homepage.js
7 -main website.HomePage
8
9 --next
10
11 -js bin/gallery.js
109
12 -main website.GalleryPage
13
14 --next
15
16 -js bin/contact.js
17 -main website.ContactPage
Comments inside hxml Inside .hxml files use a hash (i.e. #) to comment out the rest of the line.
Calling build configurations inside HXML:
It is possible to create a configuration that looks like this:
1 build-server.hxml
2 --next
3 build-website.hxml
4 --next
5 build-game.hxml
110
Flag Description
absolute-path Print absolute file path in trace output
advanced-telemetry Allow the SWF to be measured with Monocle tool
analyzer Use static analyzer for optimization (experimental)
as3 Defined when outputing flash9 as3 source code
check-xml-proxy Check the used fields of the xml proxy
core-api Defined in the core api context
core-api-serialize Mark some generated core api classes with the Serializable a
cppia Generate experimental cpp instruction assembly
dce=<mode:std|full|no> Set the dead code elimination (8.2) mode (default std)
dce-debug Show dead code elimination (8.2) log
debug Activated when compiling with -debug
display Activated during completion
dll-export GenCPP experimental linking
dll-import GenCPP experimental linking
doc-gen Do not perform any removal/change in order to correctly ge
dump Dump the complete typed AST for internal debugging in a d
dump-dependencies Dump the classes dependencies in a dump subdirectory
dump-ignore-var-ids Remove variable IDs from non-pretty dumps (helps with dif
erase-generics Erase generic classes on C#
fdb Enable full flash debug infos for FDB interactive debugging
file-extension Output filename extension for cpp source code
flash-strict More strict typing for flash target
flash-use-stage Keep the SWF library initial stage
force-lib-check Force the compiler to check -net-lib and -java-lib added class
force-native-property Tag all properties with :nativeProperty metadata for 3.1
format-warning Print a warning for each formated string, for 2.x compatibili
gencommon-debug GenCommon internal
haxe-boot Given the name ’haxe’ to the flash boot class instead of a gen
haxe-ver The current Haxe version value
hxcpp-api-level Provided to allow compatibility between hxcpp versions
include-prefix prepend path to generated include files
interp The code is compiled to be run with --interp
java-ver=[version:5-7] Sets the Java version to be targeted
js-classic Don’t use a function wrapper and strict mode in JS output
js-es5 Generate JS for ES5-compliant runtimes
js-unflatten Generate nested objects for packages and types
keep-old-output Keep old source files in the output directory (for C#/Java)
loop-unroll-max-cost Maximum cost (number of expressions * iterations) before lo
macro Defined when code is compiled in the macro context (9)
macro-times Display per-macro timing when used with --times
net-ver=<version:20-45> Sets the .NET version to be targeted
net-target=<name> Sets the .NET target. Defaults to net. xbox, micro (Micro Fra
neko-source Output neko source instead of bytecode
neko-v1 Keep Neko 1.x compatibility
network-sandbox Use local network sandbox instead of local file access one
no-compilation Disable CPP final compilation
no-copt Disable completion optimization (for debug purposes)
no-debug Remove all debug macros from cpp output
no-deprecation-warnings Do not warn if fields annotated with @:deprecated are us
no-flash-override Change overrides on some basic classes into HX suffixed me
no-opt 111Disable optimizations
no-pattern-matching Disable pattern matching (6.4)
no-inline Disable inlining (4.4.2)
no-root GenCS internal
no-macro-cache Disable macro context caching
no-simplify Disable simplification filter
no-swf-compress Disable SWF output compression
no-traces Disable all trace calls
Chapter 8
Compiler Features
112
Global
Metadata Description
@:abi Function ABI/calling convention
@:abstract Sets the underlying class implementation as
@:access (Target path) Forces private access to package type or field
@:allow (Target path) Allows private access from package type or fi
@:analyzer Used to configure the static analyzer
@:annotation Annotation (@interface) definitions on -j
@:arrayAccess Allows Array access (2.8.3) on an abstract
@:autoBuild (Build macro call) Extends @:build metadata to all extending
@:bind Override Swf class declaration
@:bitmap (Bitmap file path) Embeds given bitmap data into the class (m
@:bridgeProperties Creates native property bridges for all Haxe
@:build (Build macro call) Builds a class or enum from a macro. See Typ
@:buildXml Specify xml data to be injected into Build.xm
@:callable Abstract forwards call to its underlying type
@:classCode Used to inject platform-native code into a cla
@:commutative Declares an abstract operator as commutativ
@:compilerGenerated Marks a field as generated by the compiler. S
@:coreApi Identifies this class as a core api class (forces
@:coreType Identifies an abstract as core type (2.8.6) so th
@:cppFileCode Code to be injected into generated cpp file
@:cppInclude File to be included in generated cpp file
@:cppNamespaceCode
@:dce Forces Dead Code Elimination (8.2) even wh
@:debug Forces debug information to be generated in
@:decl
@:delegate Automatically added by -net-lib on deleg
@:depend
@:deprecated Automatically added by -java-lib on clas
@:event Automatically added by -net-lib on event
@:enum Defines finite value sets to abstract definition
@:expose (?Name=Class path) Makes the class available on the window obje
@:extern Marks the field as extern so it is not generate
@:fakeEnum (Type name) Treat enum as collection of values of the spec
@:file(File path) Includes a given binary file into the target Sw
@:final Prevents a class from being extended
@:font (TTF path Range String) Embeds the given TrueType font into the clas
@:forward (List of field names) Forwards field access (2.8.5) to underlying ty
@:from Specifies that the field of the abstract is a cast
@:functionCode Injects native code into the beginning of the f
@:functionTailCode Injects native code into the end of the functio
@:generic Marks a class or class field as generic (3.3) so
@:genericBuild Builds instances of a type using the specified
@:getter (Class field name) Generates a native getter function on the giv
@:hack Allows extending classes marked as @:fina
@:headerClassCode Code to be injected into the generated class,
@:headerCode Code to be injected into the generated heade
@:headerNamespaceCode
@:hxGen Annotates that an extern class was generated
@:ifFeature (Feature name) Causes a field to be kept by DCE (8.2) if the g
@:include
@:internal 113 Generates the annotated field/class with int
@:isVar Forces a physical field to be generated for pr
@:javaCanonical (Output type package,Output type name) Used by the Java target to annotate the canon
@:jsRequire Generate javascript module require expressio
@:keep Causes a field or type to be kept by DCE (8.2
@:keepInit Causes a class to be kept by DCE (8.2) even i
@:keepSub Extends @:keep metadata to all implementi
@:macro (deprecated)
8.2 Dead Code Elimination
Dead Code Elimination or DCE is a compiler feature which removes unused code from the out-
put. After typing, the compiler evaluates the DCE entry-points (usually the main-method) and
recursively determines which fields and types are used. Used fields are marked accordingly and
unmarked fields are then removed from their classes.
DCE has three modes which are set when invoking the command line:
-dce std: Only classes in the Haxe Standard Library are affected by DCE. This is the default
setting on all targets.
-dce no: No DCE is performed.
-dce full: All classes are affected by DCE.
The DCE-algorithm works well with typed code, but may fail when dynamic (2.7) or reflection
(10.7) is involved. This may require explicit marking of fields or classes as being used by attribut-
ing the following metadata:
@:keep: If used on a class, the class along with all fields is unaffected by DCE. If used on a field,
that field is unaffected by DCE.
@:keepSub: If used on a class, it works like @:keep on the annotated class as well as all sub-
classes.
@:keepInit: Usually, a class which had all fields removed by DCE (or is empty to begin with)
is removed from the output. By using this metadata, empty classes are kept.
If a class needs to be marked with @:keep from the command line instead of editing its source
code, there is a compiler macro available for doing so: --macro keep(’type dot path’)
See the haxe.macro.Compiler.keep API for details of this macro. It will mark package, module or
sub-type to be kept by DCE and includes them for compilation.
The compiler automatically defines the flag dce with a value of either "std", "no" or
"full" depending on the active mode. This can be used in conditional compilation (6.1).
Trivia: DCE-rewrite
DCE was originally implemented in Haxe 2.07. This implementation considered a function to
be used when it was explicitly typed. The problem with that was that several features, most
importantly interfaces, would cause all class fields to be typed in order to verify type-safety.
This effectively subverted DCE completely, prompting the rewrite for Haxe 2.10.
114
substantial amount of work to replicate the required processing. This is why the Haxe Compiler
comes with a built-in completion mode for third-party software to use.
All completion is triggered using the --display file@position[@mode] compiler ar-
gument. The required arguments are:
file: The file to check for completion. This must be an absolute or relative path to a .hx file. It
does not respect any class paths or libraries.
position: The byte position (not character position) of where to check for completion in the given
file.
mode: The completion mode to use (see below).
Field access (8.3.2): Provides a list of fields that can be accessed on a given type.
Call argument (8.3.3): Reports the type of the function which is currently being called.
Type path (8.3.4): Lists sub-packages, sub-types and static fields.
Usage (8.3.5): Lists all occurrences of a given type, field or variable in all compiled files. (mode:
"usage")
Position (8.3.6): Reports the position of where a given type, field or variable is defined. (mode:
"position")
Top-level (8.3.7): Lists all identifiers which are available at a given position. (mode: "toplevel")
Due to Haxe being a very fast compiler, it is often sufficient to rely on the normal compiler
invocation for completion. For bigger projects Haxe provides a server mode (8.3.8) which ensures
that only those files are re-compiled that actually changed or had any of their dependencies
changes.
115
8.3.2 Field access completion
Field completion is triggered after a dot . character to list the fields that are available on the
given type. The compiler parses and types everything up to the point of completion and then
outputs the relevant information to stderr:
1 class Main {
2 public static function main() {
3 trace("Hello".|
4 }
5 }
If this file is saved to Main.hx, the completion can be invoked using the command haxe
--display Main.hx@0. The output looks similar to this (we omit several fields for brevity
and improve the formatting for readability):
1 <list>
2 <i n="length">
3 <t>Int</t>
4 <d>
5 The number of characters in ‘this‘ String.
6 </d>
7 </i>
8 <i n="charAt">
9 <t>index : Int -> String</t>
10 <d>
11 Returns the character at position ‘index‘ of ‘this‘ String.
12 If ‘index‘ is negative or exceeds ‘this.length‘, the empty String
13 "" is returned.
14 </d>
15 </i>
16 <i n="charCodeAt">
17 <t>index : Int -> Null<Int></t>
18 <d>
19 Returns the character code at position ‘index‘ of ‘this‘ String.
20 If ‘index‘ is negative or exceeds ‘this.length‘, null is returned.
21 To obtain the character code of a single character, "x".code can
22 be used instead to inline the character code at compile time.
23 Note that this only works on String literals of length 1.
24 </d>
25 </i>
26 </list>
The XML structure follows:
• The document node list encloses several nodes i, each representing a field.
• The n attribute contains the name of the field.
• The t node contains the type of the field.
116
When compiling with -D display-details, each field additionally has a k attribute which
can either be var or method. This allows distinguishing method fields from variable fields that
have a function type.
If this file is saved to Main.hx, the completion can be invoked using the command haxe
--display Main.hx@0. The output looks like this:
1 <type>
2 delimiter : String -> Array<String>
3 </type>
IDEs can parse this to recognize that the called function requires one argument named delimiter
of type String and returns an Array<String>.
package completion This lists all sub-packages of the haxe package as well as all modules in
that package:
1 import haxe.|
1 <list>
2 <i n="CallStack"><t></t><d></d></i>
3 <i n="Constraints"><t></t><d></d></i>
4 <i n="DynamicAccess"><t></t><d></d></i>
5 <i n="EnumFlags"><t></t><d></d></i>
6 <i n="EnumTools"><t></t><d></d></i>
7 <i n="Http"><t></t><d></d></i>
8 <i n="Int32"><t></t><d></d></i>
9 <i n="Int64"><t></t><d></d></i>
10 <i n="Json"><t></t><d></d></i>
11 <i n="Log"><t></t><d></d></i>
12 <i n="PosInfos"><t></t><d></d></i>
117
13 <i n="Resource"><t></t><d></d></i>
14 <i n="Serializer"><t></t><d></d></i>
15 <i n="Template"><t></t><d></d></i>
16 <i n="Timer"><t></t><d></d></i>
17 <i n="Ucs2"><t></t><d></d></i>
18 <i n="Unserializer"><t></t><d></d></i>
19 <i n="Utf8"><t></t><d></d></i>
20 <i n="crypto"><t></t><d></d></i>
21 <i n="ds"><t></t><d></d></i>
22 <i n="extern"><t></t><d></d></i>
23 <i n="format"><t></t><d></d></i>
24 <i n="io"><t></t><d></d></i>
25 <i n="macro"><t></t><d></d></i>
26 <i n="remoting"><t></t><d></d></i>
27 <i n="rtti"><t></t><d></d></i>
28 <i n="unit"><t></t><d></d></i>
29 <i n="web"><t></t><d></d></i>
30 <i n="xml"><t></t><d></d></i>
31 <i n="zip"><t></t><d></d></i>
32 </list>
import module completion This lists all sub-types (3.7.1) of the module haxe.Unserializer
as well as all its public static fields (because these can be imported too):
1 import haxe.Unserializer.|
1 <list>
2 <i n="DEFAULT_RESOLVER">
3 <t>haxe.TypeResolver</t>
4 <d>
5 This value can be set to use custom type resolvers.
6
7 A type resolver finds a Class or Enum instance from a given String
.
8 By default, the haxe Type Api is used.
9
10 A type resolver must provide two methods:
11
12 1. resolveClass(name:String):Class<Dynamic> is called to
13 determine a Class from a class name
14 2. resolveEnum(name:String):Enum<Dynamic> is called to
15 determine an Enum from an enum name
16
17 This value is applied when a new Unserializer instance is created.
18 Changing it afterwards has no effect on previously created
19 instances.
20 </d>
21 </i>
22 <i n="run">
23 <t>v : String -> Dynamic</t>
24 <d>
118
25 Unserializes ‘v‘ and returns the according value.
26
27 This is a convenience function for creating a new instance of
28 Unserializer with ‘v‘ as buffer and calling its unserialize()
29 method once.
30 </d>
31 </i>
32 <i n="TypeResolver"><t></t><d></d></i>
33 <i n="Unserializer"><t></t><d></d></i>
34 </list>
1 using haxe.Unserializer.|
other module completion This lists all sub-types (3.7.1) of the module haxe.Unserializer:
1 using haxe.Unserializer.|
1 class Main {
2 static public function main() {
3 var x:haxe.Unserializer.|
4 }
5 }
1 <list>
2 <i n="TypeResolver"><t></t><d></d></i>
3 <i n="Unserializer"><t></t><d></d></i>
4 </list>
Usage completion is enabled by using the "usage" mode argument (see Overview (Sec-
tion 8.3.1)). We demonstrate it here using a local variable. Note that it would work with fields
and types the same way:
1 class Main {
2 public static function main() {
3 var a = 1;
4 var b = a + 1;
5 trace(a);
6 a.|
7 }
8 }
If this file is saved to Main.hx, the completion can be invoked using the command haxe
--display Main.hx@0@usage. The output looks like this:
1 <list>
2 <pos>main.hx:4: characters 9-10</pos>
3 <pos>main.hx:5: characters 7-8</pos>
4 <pos>main.hx:6: characters 1-2</pos>
5 </list>
119
8.3.6 Position completion
Since Haxe 3.2.0
Position completion is enabled by using the "position" mode argument (see Overview
(Section 8.3.1)). We demonstrate it using a field. Note that it would work with local variables
and types the same way:
1 class Main {
2 static public function main() {
3 "foo".split.|
4 }
If this file is saved to Main.hx, the completion can be invoked using the command haxe
--display Main.hx@0@position. The output looks like this:
1 <list>
2 <pos>std/string.hx:124: characters 1-54</pos>
3 </list>
Top-level completion displays all identifiers which the Haxe Compiler knows about at a given
compilation position. This is the only completion method for which we need a real position
argument in order to demonstrate its effect:
1 class Main {
2 static public function main() {
3 var a = 1;
4 }
5 }
6
7 enum MyEnum {
8 MyConstructor1;
9 MyConstructor2(s:String);
10 }
If this file is saved to Main.hx, the completion can be invoked using the command haxe
--display Main.hx@63@toplevel. The output looks similar to this (we omit several entries
for brevity):
1 <il>
2 <i k="local" t="Int">a</i>
3 <i k="static" t="Void -> Unknown<0>">main</i>
120
4 <i k="enum" t="MyEnum">MyConstructor1</i>
5 <i k="enum" t="s : String -> MyEnum">MyConstructor2</i>
6 <i k="package">sys</i>
7 <i k="package">haxe</i>
8 <i k="type" p="Int">Int</i>
9 <i k="type" p="Float">Float</i>
10 <i k="type" p="MyEnum">MyEnum</i>
11 <i k="type" p="Main">Main</i>
12 </il>
The structure of the XML depends on the k attribute of each entry. In all cases the node value
of the i nodes contains the relevant name.
local, member, static, enum, global: The t attribute holds the type of the variable or field.
global, type: The p attribute holds the path of the module which contains the type or field.
How it works The compilation server will cache the following things:
parsed files the files will only get parsed again if they are modified or if there was a parse error
haxelib calls the previous results of haxelib calls will be reused (only for completion : they are
ignored when doing a compilation)
typed modules compilation modules will be cached after a successful compilation and can be
reused in later compilation/completions if none of their dependencies have been modified
You can get precise reading of the times spent by the compiler and how using the compilation
server affects them by adding --times to the command line.
Protocol As the following Haxe/Neko example shows, you can simply connect on the server
port and send all commands (one per line) ending with a 0 binary char. You can, then, read the
results.
Macros and other commands can log events which are not errors. From the command line,
we can see the difference between what is printed on stdout and what is print on stderr. This
is not the case in socket mode. In order to differentiate between the two, log messages (not errors)
121
are prefixed with a
x01 character and all newline-characters in the message are replaced by the same
x01 character.
Warnings and other messages can also be considered errors, but are not fatal ones. If a fatal
error occurred, it will send a single
x02 message-line.
Here’s some code that will treat connection to the server and handle the protocol details:
1 class Test {
2 static function main() {
3 var newline = "\textbackslash\ n";
4 var s = new neko.net.Socket();
5 s.connect(new neko.net.Host("127.0.0.1"),6000);
6 s.write("--cwd /my/project" + newline);
7 s.write("myproject.hxml" + newline);
8 s.write("\textbackslash\ 000");
9
10 var hasError = false;
11 for (line in s.read().split(newline))
12 {
13 switch (line.charCodeAt(0)) {
14 case 0x01:
15 neko.Lib.print(line.substr(1).split("\
textbackslash\ x01").join(newline));
16 case 0x02:
17 hasError = true;
18 default:
19 neko.io.File.stderr().writeString(line + newline);
20 }
21 }
22 if (hasError) neko.Sys.exit(1);
23 }
24 }
Effect on macros The compilation server can have some side effects on macro execution (9).
8.4 Resources
Haxe provides a simple resource embedding system that can be used for embedding files directly
into the compiled application.
While it may be not optimal to embed large assets such as images or music in the application
file, it comes in very handy for embedding smaller resources like configuration or XML data.
122
The string after the @ symbol is the resource identifier which is used in the code for retrieving
the resource. If it is omitted (together with the @ symbol) then the file name will become the
resource identifier.
123
8.5 Runtime Type Information
The Haxe compiler generates runtime type information (RTTI) for classes that are annotated or
extend classes that are annotated with the :rtti metadata. This information is stored as a XML
string in a static field __rtti and can be processed through haxe.rtti.XmlParser. The
resulting structure is described in RTTI structure (Section 8.5.1).
Since Haxe 3.2.0
The type haxe.rtti.Rtti has been introduced in order to simplify working with RTTI.
Retrieving this information is now very easy:
1 @:rtti
2 class Main {
3 var x:String;
4 static function main() {
5 var rtti = haxe.rtti.Rtti.getRtti(Main);
6 trace(rtti);
7 }
8 }
file: The full slash path of the .hx file containing the type. This might be null in case there is no
such file, e.g. if the type is defined through a macro (9).
params: An array of strings representing the names of the type parameters (3.2) the type has. As
of Haxe 3.2.0, this does not include the constraints (3.2.1).
doc: The documentation of the type. This information is only available if the compiler flag (6.1)
-D use_rtti_doc was in place. Otherwise, or if the type has no documentation, the
value is null.
isPrivate: Whether or not the type is private (3.7.1).
platforms: A list of strings representing the targets where the type is available.
meta: The meta data the type was annotated with.
fields: The list of member class fields (4), described in Class field information (Section 8.5.1).
124
statics: The list of static class fields, described in Class field information (Section 8.5.1).
tdynamic: The type which is dynamically implemented (2.7.2) by the class or null if no such
type exists.
doc: The documentation of the field. This information is only available if the compiler flag (6.1)
-D use_rtti_doc was in place. Otherwise, or if the field has no documentation, the
value is null.
get: The read access behavior (4.2) of the field.
125
Enum constructor information
name: The name of the constructor.
args: The list of arguments the constructor has or null if no arguments are available.
doc: The documentation of the constructor. This information is only available if the compiler
flag (6.1) -D use_rtti_doc was in place. Otherwise, or if the constructor has no docu-
mentation, the value is null.
platforms: A list of strings representing the targets where the constructor is available.
meta: The meta data the constructor was annotated with.
Haxe 3.3.0 introduces a new static analyzer for code optimizations. It is enabled by using the
-D analyzer-optimize compiler flag (7.2) and consists of multiple modules (8.6) which can
be configured globally with a compiler flag (7.2) as well as at type-level and field-level with a
compiler metadata (8.1):
Local configuration To enable an analyzer module for a given type or field @:analyzer(module)
is used. To disable a module @:analyzer(no_module) is used. In both cases “module” repre-
sents the name of the module to be disabled or enabled:
1 @:analyzer(module)
2 class C {
3 @:analyzer(module) function f() { } // Field-level enable
4 @:analyzer(no_module) function f() { } // Field-level disable
5 }
6 @:analyzer(no_module)
7 class D { } // Type-level disable
Modules The static analyzer currently comes with the following modules. Unless noted other-
wise they are enabled if -D analyzer is used.
126
local_dce : Detects and removes unused local variables.
fusion : Moves variable expressions to its usage in case of single-occurrence. By default, only
compiler-generated variables are handled. This can be changed by using the compiler flag
‘-D analyzer-user-var-fusion or the metadata @:analyzer(user_var_fusion).
purity_inference : Infers if fields are “pure”, i.e. do not have any side-effects. This can
improve the effect of the fusion module.
127
Chapter 9
Macros
Macros are without a doubt the most advanced feature in Haxe. They are often perceived as dark
magic that only a select few are capable of mastering, yet there is nothing magical (and certainly
nothing dark) about them.
• Expression
• Complex Type
• haxe.macro.Expr
parse
Source code Lexer / Parser Abstract Syntax Tree (AST) Macro processor
• Typed Expression
• Type transform
Output
• haxe.macro.Type
generate
A basic macro is a syntax-transformation. It receives zero or more expressions (5) and also
128
returns an expression. If a macro is called, it effectively inserts code at the place it was called
from. In that respect, it could be compared to a preprocessor like #define in C++, but a Haxe
macro is not a textual replacement tool.
We can identify different kinds of macros, which are run at specific compilation stages:
Initialization Macros: These are provided by command line using the --macro compiler pa-
rameter. They are executed after the compiler arguments were processed and the typer
context has been created, but before any typing was done (see Initialization macros (Sec-
tion 9.7)).
Build Macros: These are defined for classes, enums and abstracts through the @:build or @:autoBuild
metadata (6.9). They are executed per-type, after the type has been set up (including its re-
lation to other types, such as inheritance for classes) but before its fields are typed (see Type
Building (Section 9.5)).
Expression Macros: These are normal functions which are executed as soon as they are typed.
Related content
• See the macro API documentation for details about its tools, classes an methods.
• See the macro snippets and tutorials section in the Haxe Code Cookbook.
Haxe macros have access to different contextual information depending on the macro type. Other
than querying such information, the context also allows some modifications such as defining a
new type or registering certain callbacks. It is important to understand that not all information
is available for all macro kinds, as the following examples demonstrate:
• Initialization macros will find that the Context.getLocal*() methods return null.
There is no local type or method in the context of an initialization macro.
• Only build macros get a proper return value from Context.getBuildFields(). There
are no fields being built for the other macro kinds.
• Build macros have a local type (if incomplete), but no local method, so Context.getLocalMethod()
returns null.
129
Related content
• See the macro Context API documentation.
• See the macro snippets and tutorials section in the Haxe Code Cookbook.
9.2 Arguments
Most of the time, arguments to macros are expressions represented as an instance of enum
Expr. As such, they are parsed but not typed, meaning they can be anything conforming to
Haxe’s syntax rules. The macro can then inspect their structure, or (try to) get their type using
haxe.macro.Context.typeof().
It is important to understand that arguments to macros are not guaranteed to be evaluated,
so any intended side-effect is not guaranteed to occur. On the other hand, it is also important to
understand that an argument expression may be duplicated by a macro and used multiple times
in the returned expression:
1 import haxe.macro.Expr;
2
3 class Main {
4 static public function main() {
5 var x = 0;
6 var b = add(x++);
7 trace(x); // 2
8 }
9
10 macro static function add(e:Expr) {
11 return macro $e + $e;
12 }
13 }
The macro add is called with x++ as argument and thus returns x++ + x++ using expression
reification (9.3.1), causing x to be incremented twice.
9.2.1 ExprOf
Since Expr is compatible with any possible input, Haxe provides the type haxe.macro.ExprOf<T>.
For the most part, this type is identical to Expr, but it allows constraining the type of accepted
expressions. This is useful when combining macros with static extensions (6.3):
1 import haxe.macro.Expr;
2 using Main;
3
4 class Main {
5 static public function main() {
6 identity("foo");
7 identity(1);
8 "foo".identity();
9 // Int has no field identity
10 //1.identity();
11 }
12
13 macro static function identity(e:ExprOf<String>) {
130
14 return e;
15 }
16 }
The two direct calls to identity are accepted, even though the argument is declared as
ExprOf<String>. It might come as a surprise that the Int 1 is accepted, but it is a logical
consequence of what was explained about macro arguments (9.2): The argument expressions are
never typed, so it is not possible for the compiler to check their compatibility by unifying (3.5).
This is different for the next two lines which are using static extensions (note the using
Main): For these it is mandatory to type the left side ("foo" and 1) first in order to make sense
of the identity field access. This makes it possible to check the types against the argument
types, which causes 1.identity() to not consider Main.identity() as a suitable field.
131
9.3 Reification
The Haxe Compiler allows reification of expressions, types and classes to simplify working with
macros. The syntax for reification is macro expr, where expr is any valid Haxe expression.
${} and $e{}: Expr -> Expr This can be used to compose expressions. The expression within
the delimiting { } is executed, with its value being used in place.
$a{}: Array<Expr> -> Array<Expr> or Array<Expr> -> Expr If used in a place where
an Array<Expr> is expected (e.g. call arguments, block elements), $a{} treats its value as
that array. Otherwise it generates an array declaration.
$b{}: Array<Expr> -> Expr Generates a block expression from the given expression array.
$i{}: String -> Expr Generates an identifier from the given string.
$p{}: Array<String> -> Expr Generates a field expression from the given string array.
$v{}: Dynamic -> Expr Generates an expression depending on the type of its argument. This
is only guaranteed to work for basic types (2.1) and enum instances (2.4).
Additionally the metadata (6.9) @:pos(p) can be used to map the position of the annotated
expression to p instead of the place it is reified at.
This kind of reification only works in places where the internal structure expects an expres-
sion. This disallows object.${fieldName}, but object.$fieldName works. This is true for
all places where the internal structure expects a string:
132
TPath: macro : pack.Type
TFunction: macro : Arg1 -> Arg2 -> Return
TAnonymous: macro : { field: Type }
TParent: macro : (Type)
9.4 Tools
The Haxe Standard Library comes with a set of tool-classes to simplify working with macros.
These classes work best as static extensions (6.3) and can be brought into context either individ-
ually or as a whole through using haxe.macro.Tools. These classes are:
MacroStringTools: Offers useful operations on strings and string expressions in macro con-
text.
133
TypeTools: Allows printing Type instances in a human-readable way. Also offers several
useful operations on types, such as unifying (3.5) them or getting their corresponding
ComplexType.
Furthermore the haxe.macro.Printer class has public methods for printing various types
as a human-readable format. This can be helpful when debugging macros.
• They do not return expressions, but an array of class fields. Their return type must be set
explicitly to Array<haxe.macro.Expr.Field>.
• Their context (9.1) has no local method and no local variables.
• Their context does have build fields, available from haxe.macro.Context.getBuildFields().
• They are not called directly, but are argument to a @:build or @:autoBuild metadata
(6.9) on a class (2.3) or enum (2.4) declaration.
The following example demonstrates type building. Note that it is split up into two files
for a reason: If a module contains a macro function, it has to be typed into macro context as
well. This is often a problem for type-building macros because the type to be built could only
be loaded in its incomplete state, before the building macro has run. We recommend to always
define type-building macros in their own module.
1 import haxe.macro.Context;
2 import haxe.macro.Expr;
3
4 class TypeBuildingMacro {
5 macro static public function build(fieldName:String):Array<Field> {
6 var fields = Context.getBuildFields();
7 var newField = {
8 name: fieldName,
9 doc: null,
10 meta: [],
11 access: [AStatic, APublic],
12 kind: FVar(macro : String, macro "my default"),
13 pos: Context.currentPos()
14 };
15 fields.push(newField);
134
16 return fields;
17 }
18 }
1 @:build(TypeBuildingMacro.build("myFunc"))
2 class Main {
3 static public function main() {
4 trace(Main.myFunc); // my default
5 }
6 }
The build method of TypeBuildingMacro performs three steps:
This macro is argument to the @:build metadata on the Main class. As soon as this type is
required, the compiler does the following:
This allows adding and modifying class fields at will in a type-building macro. In our exam-
ple, the macro is called with a "myFunc" argument, making Main.myFunc a valid field access.
If a type-building macro should not modify anything, the macro can return null. This indi-
cates to the compiler that no changes are intended and is preferable to returning Context.getBuildFields(
135
9 case EFunction(_,f): f;
10 case _: throw "false";
11 }
12 var intArg = makeEnumField("B", FFun(fInt));
13 return [noArgs, intArg];
14 }
15
16 static function makeEnumField(name, kind) {
17 return {
18 name: name,
19 doc: null,
20 meta: [],
21 access: [],
22 kind: kind,
23 pos: Context.currentPos()
24 }
25 }
26 }
1 @:build(EnumBuildingMacro.build())
2 enum E { }
3
4 class Main {
5 static public function main() {
6 switch(E.A) {
7 case A:
8 case B(v):
9 }
10 }
11 }
Because enum E is annotated with a :build metadata, the called macro builds two construc-
tors A and B “into” it. The former is added with the kind being FVar(null, null), meaning
it is a constructor without argument. For the latter, we use reification (9.3.1) to obtain an instance
of haxe.macro.Expr.Function with a singular Int argument.
The main method proves the structure of our generated enum by matching (6.4) it. We can
see that the generated type is equivalent to this:
1 enum E {
2 A;
3 B(value:Int);
4 }
Related content
• See the Math API for all available functions.
• Math related snippets and tutorials in the Haxe Code Cookbook.
9.5.2 @:autoBuild
If a class has the :autoBuild metadata, the compiler generates :build metadata on all ex-
tending classes. If an interface has the :autoBuild metadata, the compiler generates :build
136
metadata on all implementing classes and all extending interfaces. Note that :autoBuild does
not imply :build on the class/interface itself.
1 import haxe.macro.Context;
2 import haxe.macro.Expr;
3
4 class AutoBuildingMacro {
5 macro static public
6 function fromInterface():Array<Field> {
7 trace("fromInterface: " + Context.getLocalType());
8 return null;
9 }
10
11 macro static public
12 function fromBaseClass():Array<Field> {
13 trace("fromBaseClass: " + Context.getLocalType());
14 return null;
15 }
16 }
1 @:autoBuild(AutoBuildingMacro.fromInterface())
2 interface I { }
3
4 interface I2 extends I { }
5
6 @:autoBuild(AutoBuildingMacro.fromBaseClass())
7 class Base { }
8
9 class Main extends Base implements I2 {
10 static public function main() { }
11 }
This outputs during compilation:
1 AutoBuildingMacro.hx:6:
2 fromInterface: TInst(I2,[])
3 AutoBuildingMacro.hx:6:
4 fromInterface: TInst(Main,[])
5 AutoBuildingMacro.hx:11:
6 fromBaseClass: TInst(Main,[])
It is important to keep in mind that the order of these macro executions is undefined, which
is detailed in Build Order (Section 9.6.3).
Related content
• Haxe snippets and tutorials about build macros in the Haxe Code Cookbook.
9.5.3 @:genericBuild
Since Haxe 3.1.0
137
Normal build-macros (9.5) are run per-type and are already very powerful. In some cases it is
useful to run a build macro per type usage instead, i.e. whenever it actually appears in the code.
Among other things this allows accessing the concrete type parameters in the macro.
@:genericBuild is used just like @:build by adding it to a type with the argument being
a macro call:
1 import haxe.macro.Expr;
2 import haxe.macro.Context;
3 import haxe.macro.Type;
4
5 class GenericBuildMacro1 {
6 static public function build() {
7 switch (Context.getLocalType()) {
8 case TInst(_, [t1]):
9 trace(t1);
10 case t:
11 Context.error("Class expected", Context.currentPos());
12 }
13 return null;
14 }
15 }
1 @:genericBuild(GenericBuildMacro1.build())
2 class MyType<T> { }
3
4 class Main {
5 static function main() {
6 var x:MyType<Int>;
7 var x:MyType<String>;
8 }
9 }
When running this example the compiler outputs TAbstract(Int,[]) and TInst(String,[]),
indicating that it is indeed aware of the concrete type parameters of MyType. The macro logic
could use this information to generate a custom type (using haxe.macro.Context.defineType)
or refer to an existing one. For brevity we return null here which asks the compiler to infer (3.6)
the type.
In Haxe 3.1 the return type of a @:genericBuild macro has to be a haxe.macro.Type.
Haxe 3.2 allows (and prefers) returning a haxe.macro.ComplexType instead, which is the
syntactic representation of a type. This is easier to work with in many cases because types can
simply be referenced by their paths.
Const type parameter Haxe allows passing constant expression (5.2) as a type parameter if the
type parameter name is Const. This can be utilized in the context of @:genericBuild macros
to pass information from the syntax directly to the macro:
1 import haxe.macro.Expr;
2 import haxe.macro.Context;
3 import haxe.macro.Type;
4
5 class GenericBuildMacro2 {
6 static public function build() {
7 switch (Context.getLocalType()) {
138
8 case TInst(_,[TInst(_.get() => { kind: KExpr(macro $v{(s:String)
}) },_)]):
9 trace(s);
10 case t:
11 Context.error("Class expected", Context.currentPos());
12 }
13 return null;
14 }
15 }
1 @:genericBuild(GenericBuildMacro2.build())
2 class MyType<Const> { }
3
4 class Main {
5 static function main() {
6 var x:MyType<"myfile.txt">;
7 }
8 }
Here the macro logic could load a file and use its contents to generate a custom type.
Related content
• Haxe snippets and tutorials about build macros in the Haxe Code Cookbook.
9.6 Limitations
9.6.1 Macro-in-Macro
9.6.2 Static extension
The concepts of static extensions (6.3) and macros are somewhat conflicting: While the former
requires a known type in order to determine used functions, macros execute before typing on
plain syntax. It is thus not surprising that combining these two features can lead to issues. Haxe
3.0 would try to convert the typed expression back to a syntax expression, which is not always
possible and may lose important information. We recommend that it is used with caution.
Since Haxe 3.1.0
The combination of static extensions and macros was reworked for the 3.1.0 release. The Haxe
Compiler does not even try to find the original expression for the macro argument and instead
passes a special @:this this expression. While the structure of this expression conveys no
information, the expression can still be typed correctly:
1 import haxe.macro.Context;
2 import haxe.macro.Expr;
3
4 using Main;
5 using haxe.macro.Tools;
6
7 class Main {
8 static public function main() {
9 #if !macro
139
10 var a = "foo";
11 a.test();
12 #end
13 }
14
15 macro static function test(e:ExprOf<String>) {
16 trace(e.toString()); // @:this this
17 // TInst(String,[])
18 trace(Context.typeof(e));
19 return e;
20 }
21 }
140
data to the class, which can be checked during the second macro execution.
141
Part III
Standard Library
142
Chapter 10
Standard Library
10.1 String
Type: String
A String is a sequence of characters.
Character code Use the .code property on a constant single-char string in order to compile its
ASCII character code:
1 "#".code // will compile as 35
Related content
• See the String API for details about its methods.
Arrays come with an API to cover most use-cases. Additionally they allow read and write
array access (5.8):
1 class Main {
2 static public function main() {
3 var a = [1, 2, 3];
4 trace(a[1]); // 2
5 a[1] = 1;
6 trace(a[1]); // 1
143
7 }
8 }
Since array access in Haxe is unbounded, i.e. it is guaranteed to not throw an exception, this
requires further discussion:
Arrays define an iterator (6.7) over their elements. This iteration is typically optimized by the
compiler to use a while loop (5.14) with array index:
1 class Main {
2 static public function main() {
3 var scores = [110, 170, 35];
4 var sum = 0;
5 for (score in scores) {
6 sum += score;
7 }
8 trace(sum); // 315
9 }
10 }
Haxe generates this optimized JavaScript output:
1 Main.main = function() {
2 var scores = [110,170,35];
3 var sum = 0;
4 var _g = 0;
5 while(_g < scores.length) {
6 var score = scores[_g];
7 ++_g;
8 sum += score;
9 }
10 console.log(sum);
11 };
Haxe does not allow arrays of mixed types unless the parameter type is forced to Dynamic
(2.7):
1 class Main {
2 static public function main() {
3 // Compile Error: Arrays of mixed types are only allowed if the
type is
4 // forced to Array<Dynamic>
5 //var myArray = [10, "Bob", false];
6
7 // Array<Dynamic> with mixed types
8 var myExplicitArray:Array<Dynamic> = [10, "Sally", true];
9 }
10 }
144
Trivia: Dynamic Arrays
In Haxe 2, mixed type array declarations were allowed. In Haxe 3, arrays can have mixed
types only if they are explicitly declared as Array<Dynamic>.
Related content
10.2.2 Vector
A Vector is an optimized fixed-length collection of elements. Much like Array (10.2.1), it has one
type parameter (3.2) and all elements of a vector must be of the specified type, it can be iterated
over using a for loop (5.13) and accessed using array access syntax (2.8.3). However, unlike Array
and List, vector length is specified on creation and cannot be changed later.
1 class Main {
2 static function main() {
3 var vec = new haxe.ds.Vector(10);
4
5 for (i in 0...vec.length) {
6 vec[i] = i;
7 }
8
9 trace(vec[0]); // 0
10 trace(vec[5]); // 5
11 trace(vec[9]); // 9
12 }
13 }
haxe.ds.Vector is implemented as an abstract type (2.8) over a native array implementa-
tion for given target and can be faster for fixed-size collections, because the memory for storing
its elements is pre-allocated.
Related content
• See the Vector API for details about the vector methods.
• Data structures tutorials and examples in the Haxe Code Cookbook.
10.2.3 List
A List is a collection for storing elements. On the surface, a list is similar to an Array (Sec-
tion 10.2.1). However, the underlying implementation is very different. This results in several
functional differences:
145
4. A list can freely modify/add/remove elements while iterating over them.
Related content
• See the List API for details about the list methods.
• Data structures tutorials and examples in the Haxe Code Cookbook.
10.2.4 GenericStack
A GenericStack, like Array and List is a container for storing elements. It has one type pa-
rameter (3.2) and all elements of the stack must be of the specified type. Here is a small example
program for initializing and working with a GenericStack.
1 import haxe.ds.GenericStack;
2
3 class Main {
4 static public function main() {
5 var myStack = new GenericStack<Int>();
6 for (ii in 0...5)
7 myStack.add(ii);
8 trace(myStack); //{4, 3, 2, 1, 0}
9 trace(myStack.pop()); //4
10 }
11 }
Trivia: FastList
In Haxe 2, the GenericStack class was known as FastList. Since its behavior more closely
resembled a typical stack, the name was changed for Haxe 3.
The Generic in GenericStack is literal. It is attributed with the :generic metadata. Depend-
ing on the target, this can lead to improved performance on static targets. See Generic (Sec-
tion 3.3) for more details.
Related content
• See the GenericStack API for details about its methods.
• Data structures tutorials and examples in the Haxe Code Cookbook.
146
10.2.5 Map
A Map is a container composed of key, value pairs. A Map is also commonly referred to as an as-
sociative array, dictionary, or symbol table. The following code gives a short example of working
with maps:
1 class Main {
2 static public function main() {
3 // Maps are initialized like arrays, but
4 // use the map literal syntax with the
5 // ’=>’ operator. Maps can have their
6 // key value types defined explicity
7 var map1:Map<Int, String> =
8 [1 => "one", 2=>"two"];
9
10 // Or they can infer their key value types
11 var map2 = [
12 "one"=>1,
13 "two"=>2,
14 "three"=>3
15 ];
16 $type(map2); // Map<String, Int>
17
18 // Keys must be unique
19 // Error: Duplicate Key
20 //var map3 = [1=>"dog", 1=>"cat"];
21
22 // Maps values can be accessed using array
23 // accessors "[]"
24 var map4 = ["M"=>"Monday", "T"=>"Tuesday"];
25 trace(map4["M"]); //Monday
26
27 // Maps iterate over their values by
28 // default
29 var valueSum;
30 for (value in map4) {
31 trace(value); // Monday \n Tuesday
32 }
33
34 // Can iterate over keys by using the
35 // keys() method
36 for (key in map4.keys()) {
37 trace(key); // M \n T
38 }
39
40 // Like arrays, a new Map can be made using
41 // comprehension
42 var map5 = [
43 for (key in map4.keys())
44 key => "FRIDAY!!"
45 ];
46 // {M => FRIDAY!!, T => FRIDAY!!}
147
47 trace(map5);
48 }
49 }
Under the hood, a Map is an abstract (2.8) type. At compile time, it gets converted to one of
several specialized types depending on the key type:
• String: haxe.ds.StringMap
• Int: haxe.ds.IntMap
• EnumValue: haxe.ds.EnumValueMap
• {}: haxe.ds.ObjectMap
The Map type does not exist at runtime and has been replaced with one of the above objects.
Map defines array access (2.8.3) using its key type.
Related content
• See the Map API for details of its methods.
• Data structures tutorials and examples in the Haxe Code Cookbook.
10.2.6 Option
An Option is an enum (2.4) in the Haxe Standard Library which is defined like so:
1 enum Option<T> {
2 Some(v:T);
3 None;
4 }
It can be used in various situations, such as communicating whether or not a method had a
valid return and if so, what value it returned:
1 import haxe.ds.Option;
2
3 class Main {
4 static public function main() {
5 var result = trySomething();
6 switch (result) {
7 case None:
8 trace("Got None");
9 case Some(s):
10 trace("Got a value: " +s);
11 }
12 }
13
14 static function trySomething():Option<String> {
15 if (Math.random() > 0.5) {
16 return None;
17 } else {
18 return Some("Success");
19 }
20 }
21 }
148
10.3 Regular Expressions
Haxe has built-in support for regular expressions1 . They can be used to verify the format of a
string, transform a string or extract some regular data from a given text.
Haxe has special syntax for creating regular expressions. We can create a regular expression
object by typing it between the ˜/ combination and a single / character:
1 var r = ˜/haxe/i;
Alternatively, we can create regular expression with regular syntax:
1 var r = new EReg("haxe", "i");
First argument is a string with regular expression pattern, second one is a string with flags
(see below).
We can use standard regular expression patterns such as:
• . any character
• * repeat zero-or-more
• + repeat one-or-more
• ? optional zero-or-one
• [A-Z0-9] character ranges
• [ˆ\r\n\t] character not-in-range
• (...) parenthesis to match groups of characters
• ˆ beginning of the string (beginning of a line in multiline matching mode)
• $ end of the string (end of a line in multiline matching mode)
• | ”OR” statement.
For example, the following regular expression matches valid email addresses:
1 ˜/[A-Z0-9._\%-]+@[A-Z0-9.-]+\.[A-Z][A-Z][A-Z]?/i;
Please notice that the i at the end of the regular expression is a flag that enables case-insensitive
matching.
The possible flags are the following:
• i case insensitive matching
• g global replace or split, see below
• m multiline matching, ˆ and $ represent the beginning and end of a line
• s the dot . will also match newlines (Neko, C++, PHP, Flash and Java targets only)
• u use UTF-8 matching (Neko and C++ targets only)
Related content
• See the EReg API for details about its methods.
• Haxe snippets and tutorials about regular expressions in the Haxe Code Cookbook.
1 http://en.wikipedia.org/wiki/Regular expression
149
10.3.1 Matching
Probably one of the most common uses for regular expressions is checking whether a string
matches the specific pattern. The match method of a regular expression object can be used to do
that:
1 class Main {
2 static function main() {
3 var r = ˜/world/;
4 var str = "hello world";
5 // true : ’world’ was found in the string
6 trace(r.match(str));
7 trace(r.match("hello !")); // false
8 }
9 }
10.3.2 Groups
Specific information can be extracted from a matched string by using groups. If match() returns
true, we can get groups using the matched(X) method, where X is the number of a group
defined by regular expression pattern:
1 class Main {
2 static function main() {
3 var str = "Nicolas is 26 years old";
4 var r =
5 ˜/([A-Za-z]+) is ([0-9]+) years old/;
6 r.match(str);
7 trace(r.matched(1)); // "Nicolas"
8 trace(r.matched(2)); // "26"
9 }
10 }
Note that group numbers start with 1 and r.matched(0) will always return the whole
matched substring.
The r.matchedPos() will return the position of this substring in the original string:
1 class Main {
2 static function main() {
3 var str = "abcdeeeeefghi";
4 var r = ˜/e+/;
5 r.match(str);
6 trace(r.matched(0)); // "eeeee"
7 // { pos : 4, len : 5 }
8 trace(r.matchedPos());
9 }
10 }
Additionally, r.matchedLeft() and r.matchedRight() can be used to get substrings to
the left and to the right of the matched substring:
1 class Main {
2 static function main() {
3 var r = ˜/b/;
150
4 r.match("abc");
5 trace(r.matchedLeft()); // a
6 trace(r.matched(0)); // b
7 trace(r.matchedRight()); // c
8 }
9 }
10.3.3 Replace
A regular expression can also be used to replace a part of the string:
1 class Main {
2 static function main() {
3 var str = "aaabcbcbcbz";
4 // g : replace all instances
5 var r = ˜/b[ˆc]/g;
6 // "aaabcbcbcxx"
7 trace(r.replace(str,"xx"));
8 }
9 }
10.3.4 Split
A regular expression can also be used to split a string into several substrings:
1 class Main {
2 static function main() {
3 var str = "XaaaYababZbbbW";
4 var r = ˜/[ab]+/g;
5 // ["X","Y","Z","W"]
6 trace(r.split(str));
7 }
8 }
10.3.5 Map
The map method of a regular expression object can be used to replace matched substrings using a
custom function. This function takes a regular expression object as its first argument so we may
use it to get additional information about the match being done and do conditional replacement.
For example:
151
1 class Main {
2 static function main() {
3 var r = ˜/(dog|fox)/g;
4 var s = "The quick brown fox jumped over the lazy dog.";
5 var s2 = r.map(s, function(r) {
6 var match = r.matched(0);
7 switch (match) {
8 case ’dog’: return ’fox’;
9 case ’fox’: return ’dog’;
10 default: throw ’Unknown animal: $match’;
11 };
12 });
13 trace(s2); // The quick brown dog jumped over the lazy fox.
14 }
15 }
• in JavaScript, the runtime is providing the implementation with the object RegExp.
• in Neko and C++, the PCRE library is used
10.4 Math
Haxe includes a floating point math library for some common mathematical operations. Most of
the functions operate on and return floats. However, an Int can be used where a Float is
expected, and Haxe also converts Int to Float during most numeric operations (see Numeric
Operators (Section 2.1.3) for more details).
Here are some example uses of the math library:
1 class Main {
2 static public function main() {
3 var x = 1/2;
4 var y = 20.2;
5 var z = -2;
6
7 trace(Math.abs(z)); //2
8 trace(Math.sin(x*Math.PI)); //1
9 trace(Math.ceil(y)); //21
10
11 // log is the natural logarithm
12 trace(Math.log(Math.exp(5))); //5
13
14 // Output for neko target, may vary
15 // depending on platform
16 trace(1/0); //inf
152
17 trace(-1/0); //-inf
18 trace(Math.sqrt(-1)); //nan
19 }
20 }
Related content
• See the Math API documentation for all available functions.
• Haxe snippets and tutorials about math in the Haxe Code Cookbook.
• NaN (Not a Number): returned when a mathematically incorrect operation is executed, e.g.
Math.sqrt(-1)
• POSITIVE INFINITY: e.g. divide a positive number by zero
• NEGATIVE INFINITY: e.g. divide a negative number by zero
• PI : 3.1415...
153
1 using MathStaticExtension;
2
3 class Main {
4 public static function main() {
5 var ang = 1/2*Math.PI;
6 trace(ang.toDegrees()); //90
7 }
8 }
10.5 Lambda
Definition: Lambda
Lambda is a functional language concept within Haxe that allows you to apply a function to
a list or iterators (6.7). The Lambda class is a collection of functional methods in order to use
functional-style programming with Haxe.
It is ideally used with using Lambda (see Static Extension (6.3)) and then acts as an extension
to Iterable types.
On static platforms, working with the Iterable structure might be slower than performing
the operations directly on known types, such as Array and List.
Lambda Functions The Lambda class allows us to operate on an entire Iterable at once.
This is often preferable to looping routines since it is less error prone and easier to read. For
convenience, the Array and List class contains some of the frequently used methods from the
Lambda class.
It is helpful to look at an example. The exists function is specified as:
1 static function exists<A>( it : Iterable<A>, f : A -> Bool ) : Bool
Most Lambda functions are called in similar ways. The first argument for all of the Lambda
functions is the Iterable on which to operate. Many also take a function as an argument.
154
Lambda.concat Merge two Iterables, returning a new List.
Lambda.filter Find the elements that satisfy a criteria, returning a new List.
Lambda.map, Lambda.mapi Apply a conversion to each element, returning a new List.
Lambda.fold Functional fold, which is also known as reduce, accumulate, compress or inject.
This example demonstrates the Lambda filter and map on a set of strings:
1 using Lambda;
2 class Main {
3 static function main() {
4 var words = [’car’, ’boat’, ’cat’, ’frog’];
5
6 var isThreeLetters = function(word) return word.length == 3;
7 var capitalize = function(word) return word.toUpperCase();
8
9 // Three letter words and capitalized.
10 trace(words.filter(isThreeLetters).map(capitalize)); // [CAR,
CAT]
11 }
12 }
This example demonstrates the Lambda count, has, foreach and fold function on a set of ints.
1 using Lambda;
2 class Main {
3 static function main() {
4 var numbers = [1, 3, 5, 6, 7, 8];
5
6 trace(numbers.count()); // 6
7 trace(numbers.has(4)); // false
8
9 // test if all numbers are greater/smaller than 20
10 trace(numbers.foreach(function(v) return v < 20)); // true
11 trace(numbers.foreach(function(v) return v > 20)); // false
12
13 // sum all the numbers
14 var sum = function(num, total) return total += num;
15 trace(numbers.fold(sum, 0)); // 30
16 }
17 }
Related content
• See the Lambda API documentation for all available functions.
10.6 Template
Haxe comes with a standard template system with an easy to use syntax which is interpreted by
a lightweight class called haxe.Template.
A template is a string or a file that is used to produce any kind of string output depending on
the input. Here is a small template example:
155
1 class Main {
2 static function main() {
3 var sample = "My name is <strong>::name::</strong>, <em>::age::</
em> years old";
4 var user = {name:"Mark", age:30};
5 var template = new haxe.Template(sample);
6 var output = template.execute(user);
7 trace(output);
8 }
9 }
The console will trace My name is Mark, 30 years old.
Expressions An expression can be put between the ::, the syntax allows the current possibili-
ties:
::name:: the variable name
::expr.field:: field access
::(expr):: the expression expr is evaluated
::(e1 op e2):: the operation op is applied to e1 and e2
::(135):: the integer 135. Float constants are not allowed
Conditions It is possible to test conditions using ::if flag1::. Optionally, the condition
may be followed by ::elseif flag2:: or ::else::. Close the condition with ::end::.
1 ::if isValid:: valid ::else:: invalid ::end::
Operators can be used but they don’t deal with operator precedence. Therefore it is required
to enclose each operation in parentheses (). Currently, the following operators are allowed: +,
-, *, /, >, <, >=, <=, ==, !=, && and ||.
For example ::((1 + 3) == (2 + 2)):: will display true.
1 ::if (points == 10):: Great! ::end::
To compare to a string, use double quotes " in the template.
1 ::if (name == "Mark"):: Hi Mark ::end::
Iterating Iterate on a structure by using ::foreach::. End the loop with ::end::.
1 <table>
2 <tr>
3 <th>Name</th>
4 <th>Age</th>
5 </tr>
6 ::foreach users::
7 <tr>
8 <td>::name::</td>
9 <td>::age::</td>
10 </tr>
11 ::end::
12 </table>
156
Sub-templates To include templates in other templates, pass the sub-template result string as
a parameter.
1 var users = [{name:"Mark", age:30}, {name:"John", age:45}];
2
3 var userTemplate = new haxe.Template("::foreach users:: ::name::(::age
::) ::end::");
4 var userOutput = userTemplate.execute({users: users});
5
6 var template = new haxe.Template("The users are ::users::");
7 var output = template.execute({users: userOutput});
8 trace(output);
The console will trace The users are Mark(30) John(45).
Template macros To call custom functions while parts of the template are being rendered, pro-
vide a macros object to the argument of Template.execute. The key will act as the template
variable name, the value refers to a callback function that should return a String. The first
argument of this macro function is always a resolve() method, followed by the given argu-
ments. The resolve function can be called to retrieve values from the template context. If macros
has no such field, the result is unspecified.
The following example passes itself as macro function context and executes display from
the template.
1 class Main {
2 static function main() {
3 new Main();
4 }
5
6 public function new() {
7 var user = {name:"Mark", distance:3500};
8 var sample = "The results: $$display(::user::,::time::)";
9 var template = new haxe.Template(sample);
10 var output = template.execute({user:user, time: 15}, this);
11 trace(output);
12 }
13
14 function display(resolve:String->Dynamic, user:User, time:Int) {
15 return user.name + " ran " + (user.distance/1000) + " kilometers
in " + time + " minutes";
16 }
17 }
18 typedef User = {name:String, distance:Int}
The console will trace The results: Mark ran 3.5 kilometers in 15 minutes.
Globals Use the Template.globals object to store values that should be applied across all haxe.Template
instances. This has lower priority than the context argument of Template.execute.
Using resources To separate the content from the code, consider using the resource embed-
ding system (8.4). Place the template-content in a new file called sample.mtt, add -resource
sample.mtt@my_sample to the compiler arguments and retrieve the content using haxe.Resource.getSt
157
1 class Main {
2 static function main() {
3 var sample = haxe.Resource.getString("my_sample");
4 var user = {name:"Mark", age:30};
5 var template = new haxe.Template(sample);
6 var output = template.execute(user);
7 trace(output);
8 }
9 }
When running the template system on the server side, you can simply use neko.Lib.print
or php.Lib.print instead of trace to display the HTML template to the user.
Related content
10.7 Reflection
Haxe supports runtime reflection of types and fields. Special care has to be taken here because
runtime representation generally varies between targets. In order to use reflection correctly it
is necessary to understand what kind of operations are supported and what is not. Given the
dynamic nature of reflection, this can not always be determined at compile-time.
The reflection API consists of two classes:
Reflect: A lightweight API which work best on anonymous structures (2.5), with limited support
for classes (2.3).
Type: A more robust API for working with classes and enums (2.4).
The available methods are detailed in the API for Reflect and Type.
Reflection can be a powerful tool, but it is important to understand why it can also cause
problems. As an example, several functions expect a String (10.1) argument and try to resolve it
to a type or field. This is vulnerable to typing errors:
1 class Main {
2 static function main() {
3 trace(Type.resolveClass("Mian")); // null
4 }
5 }
However, even if there are no typing errors it is easy to come across unexpected behavior:
1 class Main {
2 static function main() {
3 // null
4 trace(Type.resolveClass("haxe.Template"));
5 }
6 }
The problem here is that the compiler never actually “sees” the type haxe.Template, so it
does not compile it into the output. Furthermore, even if it were to see the type there could be
issues arising from dead code elimination (8.2) eliminating types or fields which are only used
via reflection.
158
Another set of problems comes from the fact that, by design, several reflection functions ex-
pect arguments of type Dynamic (2.7), meaning the compiler cannot check if the passed in argu-
ments are correct. The following example demonstrates a common mistake when working with
callMethod:
1 class Main {
2 static function main() {
3 // wrong
4 //Reflect.callMethod(Main, "f", []);
5 // right
6 Reflect.callMethod(Main,
7 Reflect.field(Main, "f"), []);
8 }
9
10 static function f() {
11 trace(’Called’);
12 }
13 }
The commented out call would be accepted by the compiler because it assigns the string "f"
to the function argument func which is specified to be Dynamic.
A good advice when working with reflection is to wrap it in a few functions within an appli-
cation or API which are called by otherwise type-safe code. An example could look like this:
1 typedef MyStructure = {
2 name: String,
3 score: Int
4 }
5
6 class Main {
7 static function main() {
8 var data = reflective();
9 // At this point data is nicely typed as MyStructure
10 }
11
12 static function reflective():MyStructure {
13 // Work with reflection here to get some values we want to return.
14 return {
15 name: "Reflection",
16 score: 0
17 }
18 }
19 }
While the method reflective could internally work with reflection (and Dynamic for that
matter) a lot, its return value is a typed structure which the callers can use in a type-safe manner.
10.8 Serialization
Many runtime values can be serialized and deserialized using the haxe.Serializer and haxe.Unserializer
classes. Both support two usages:
1. Create an instance and continuously call the serialize/unserialize method to handle
multiple values.
159
2. Call their static run method to serialize/deserialize a single value.
The following example demonstrates the first usage:
1 import haxe.Serializer;
2 import haxe.Unserializer;
3
4 class Main {
5 static function main() {
6 var serializer = new Serializer();
7 serializer.serialize("foo");
8 serializer.serialize(12);
9 var s = serializer.toString();
10 trace(s); // y3:fooi12
11
12 var unserializer = new Unserializer(s);
13 trace(unserializer.unserialize()); // foo
14 trace(unserializer.unserialize()); // 12
15 }
16 }
The result of the serialization (here stored in local variable s) is a String (10.1) and can
be passed around at will, even remotely. Its format is described in Serialization format (Sec-
tion 10.8.1).
Supported values
• null
• Bool, Int and Float (including infinities and NaN)
• String
• Date
• haxe.io.Bytes (encoded as base64)
• Array (10.2.1) and List (10.2.3)
• haxe.ds.StringMap, haxe.ds.IntMap and haxe.ds.ObjectMap
• anonymous structures (2.5)
• Haxe class instances (2.3) (not native ones)
• enum instances (2.4)
Serialization configuration Serialization can be configured in two ways. For both a static vari-
able can be set to influence all haxe.Serializer instances, and a member variable can be set to only
influence a specific instance:
USE_CACHE, useCache: If true, repeated structures or class/enum instances are serialized by
reference. This can avoid infinite loops for recursive data at the expense of longer serializa-
tion time. By default, object caching is disabled; strings however are always cached.
USE_ENUM_INDEX, useEnumIndex: If true, enum constructors are serialized by their index in-
stead of their name. This can make the resulting string shorter, but breaks if enum construc-
tors are inserted into the type before deserialization. This behavior is disabled by default.
160
Deserialization behavior If the serialization result is stored and later used for deserialization,
care has to be taken to maintain compatibility when working with class and enum instances. It
is then important to understand exactly how unserialization is implemented.
• The type has to be available in the runtime where the deserialization is made. If dead
code elimination (8.2) is active, a type which is used only through serialization might be
removed.
• Each Unserializer has a member variable resolver which is used to resolve classes
and enums by name. Upon creation of the Unserializer this is set to Unserializer.DEFAULT_-
RESOLVER. Both that and the instance member can be set to a custom resolver.
• Classes are resolved by name using resolver.resolveClass(name). The instance is
then created using Type.createEmptyInstance, which means that the class constructor
is not called. Finally, the instance fields are set according to the serialized value.
Custom (de)serialization If a class defines the member method hxSerialize, that method is
called by the serializer and allows custom serialization of the class. Likewise, if a class defines
the member method hxUnserialize it is called by the deserializer:
1 import haxe.Serializer;
2 import haxe.Unserializer;
3
4 class Main {
5
6 var x:Int;
7 var y:Int;
8
9 static function main() {
10 var s = Serializer.run(new Main(1, 2));
11 var c:Main = Unserializer.run(s);
12 trace(c.x); // 1
13 trace(c.y); // -1
14 }
15
16 function new(x, y) {
17 this.x = x;
18 this.y = y;
19 }
20
21 @:keep
22 function hxSerialize(s:Serializer) {
23 s.serialize(x);
24 }
25
26 @:keep
27 function hxUnserialize(u:Unserializer) {
28 x = u.unserialize();
29 y = -1;
161
30 }
31 }
In this example we decide that we want to ignore the value of member variable y and do not
serialize it. Instead we default it to -1 in hxUnserialize. Both methods are annotated with the
@:keep metadata to prevent dead code elimination (8.2) from removing them as they are never
properly referenced in the code.
See Serializer and Unserializer API documentation for details.
null: n
Int: z for zero, or i followed by the integer display (e.g. i456)
Float:
NaN: k
negative infinity: m
positive infinity: p
finite floats: d followed by the float display (e.g. d1.45e-8)
structure: o followed by the list of name-value pairs and terminated by g (e.g. oy1:xi2y1:kng
for {x:2, k:null})
List: l followed by the list of serialized items, followed by h (e.g. lnnh for a list of two null
values)
Array: a followed by the list of serialized items, followed by h. For multiple consecutive null
values, u followed by the number of null values is used (e.g. ai1i2u4i7ni9h for
[1,2,null,null,null,null,7,null,9])
Date: v followed by the date itself (e.g. v2010-01-01 12:45:10)
haxe.ds.StringMap: b followed by the name-value pairs, followed by h (e.g. by1:xi2y1:knh
for {"x" => 2, "k" => null})
haxe.ds.IntMap: q followed by the key-value pairs, followed by h. Each key is represented
as :<int> (e.g. q:4n:5i45:6i7h for {4 => null, 5 => 45, 6 => 7})
haxe.ds.ObjectMap: M followed by serialized value pairs representing the key and value,
followed by h
haxe.io.Bytes: s followed by the length of the base64 encoded bytes, then : and the byte
representation using the codes A-Za-z0-9% (e.g. s3:AAA for 2 bytes equal to 0, and
s10:SGVsbG8gIQ for haxe.io.Bytes.ofString("Hello !"))
162
exception: x followed by the exception value
class instance: c followed by the serialized class name, followed by the name-value pairs of the
fields, followed by g (e.g. cy5:Pointy1:xzy1:yzg for new Point(0, 0) (having two
integer fields x and y)
enum instance (by name): w followed by the serialized enum name, followed by the serialized
constructor name, followed by :, followed by the number of arguments, followed by the ar-
gument values (e.g. wy3:Fooy1:A:0 for Foo.A (with no arguments), wy3:Fooy1:B:2i4n
for Foo.B(4,null))
enum instance (by index): j followed by the serialized enum name, followed by :, followed
by the constructor index (starting from 0), followed by :, followed by the number of ar-
guments, followed by the argument values (e.g. jy3:Foo:0:0 for Foo.A (with no argu-
ments), jy3:Foo:1:2i4n for Foo.B(4,null))
cache references:
String: R followed by the corresponding index in the string cache (e.g. R456)
class, enum or structure r followed by the corresponding index in the object cache (e.g.
r42)
custom: C followed by the class name, followed by the custom serialized data, followed by g
10.9 Xml
Haxe provides built-in support for working with XML2 data via the haxe.Xml class.
Creating child elements Adding child elements to the root can be done using the addChild
method.
1 var child:Xml = Xml.createElement(’child’);
2 root.addChild(child);
3 trace(root); // <root><child/></root>
Adding attributes to an element can be done by using the set() method.
1 child.set(’name’, ’John’);
2 trace(root); // <root><child name="John"/></root>
2 http://en.wikipedia.org/wiki/XML
163
Accessing elements and values This code parses an XML string into an object structure Xml
and then accesses properties of the object.
1 var xmlString = ’<hello name="world!">Haxe is great!</hello>’;
2 var xml:Xml = Xml.parse(xmlString).firstElement();
3
4 trace(xml.nodeName); // hello
5 trace(xml.get(’name’)); // world!
6 trace(xml.firstChild().nodeValue); // Haxe is great!
The difference between firstChild and firstElement is that the second function will
return the first child with the type Xml.Element.
Iterate on Xml elements We can as well use other methods to iterate either over children or
elements.
1 for (child in xml) {
2 // iterate on all children.
3 }
4 for (elt in xml.elements()) {
5 // iterate on all elements.
6 }
7 for (user in xml.elementsNamed("user")) {
8 // iterate on all elements with a nodeName "user".
9 }
10 for (att in xml.attributes()) {
11 // iterator on all attributes.
12 }
See Xml API documentation for details about its methods.
10.10 Json
Haxe provides built-in support for (de-)serializing JSON3 data via the haxe.Json class.
3 http://en.wikipedia.org/wiki/JSON
164
Related content
• See the Haxe Json API documentation.
• Haxe snippets and tutorials about JSON in the Haxe Code Cookbook.
Note that the type of the object returned by haxe.Json.parse is Dynamic, so if the struc-
ture of our data is well-known, we may want to specify a type using anonymous structures (2.5).
This way we provide compile-time checks for accessing our data and most likely more optimal
code generation, because compiler knows about types in a structure:
1 typedef MyData = {
2 var name:String;
3 var tags:Array<String>;
4 }
5
6 class Main {
7 static function main() {
8 var s = ’{
9 "name": "Haxe",
10 "tags": ["awesome"]
11 }’;
12 var o:MyData = haxe.Json.parse(s);
13 trace(o.name); // Haxe (a string)
14 // awesome (a string in an array)
15 trace(o.tags[0]);
16 }
17 }
165
10.10.3 Implementation details
The haxe.Json API automatically uses native implementation on targets where it is available, i.e.
JavaScript, Flash and PHP and provides its own implementation for other targets.
Usage of Haxe own implementation can be forced with -D haxeJSON compiler argument.
This will also provide serialization of enums (2.4) by their index, maps (10.2.5) with string keys
and class instances.
Older browsers (Internet Explorer 7, for instance) may not have native JSON implementation.
In case it’s required to support them, we can include one of the JSON implementations available
on the internet in the HTML page. Alternatively, a -D old_browser compiler argument that
will make haxe.Json try to use native JSON and, in case it’s not available, fallback to its own
implementation.
10.11 Input/Output
10.12 Sys/sys
10.13 Remoting
Haxe remoting is a way to communicate between different platforms. With Haxe remoting, ap-
plications can transmit data transparently, send data and call methods between server and client
side.
Related content
• See the remoting package on the API documentation for more details on its classes.
Start a connection There are some target-specific constructors with different purposes that can
be used to set up a connection:
166
SocketConnection.create(sock:flash.XMLSocket) Allows remoting communi-
cations over an XMLSocket
LocalConnection.connect(name:String) Allows remoting communications over
a Flash LocalConnection
JavaScript: ExternalConnection.flashConnect(name:String, obj:String, ctx:Context)
Allows a connection to a given Flash Object. The Haxe Flash content must be loaded
and it must include the haxe.remoting.Connection class. This only works with
Flash 8 and higher.
Neko: HttpConnection.urlConnect(url:String) Will work like the asynchronous ver-
sion but in synchronous mode.
SocketConnection.create(...) Allows real-time communications with a Flash client
which is using an XMLSocket to connect to the server.
Remoting context Before communicating between platforms, a remoting context has to be de-
fined. This is a shared API that can be called on the connection at the client code.
This server code example creates and shares an API:
1 class Server {
2 function new() { }
3 function foo(x, y) { return x + y; }
4
5 static function main() {
6 var ctx = new haxe.remoting.Context();
7 ctx.addObject("Server", new Server());
8
9 if(haxe.remoting.HttpConnection.handleRequest(ctx))
10 {
11 return;
12 }
13
14 // handle normal request
15 trace("This is a remoting server !");
16 }
17 }
Using the connection Using a connection is pretty convenient. Once the connection is obtained,
use classic dot-access to evaluate a path and then use call() to call the method in the remoting
context and get the result. The asynchronous connection takes an additional function parameter
that will be called when the result is available.
This client code example connects to the server remoting context and calls a function foo()
on its API.
1 class Client {
2 static function main() {
3 var cnx = haxe.remoting.HttpAsyncConnection.urlConnect("http://
localhost/");
4 cnx.setErrorHandler( function(err) { trace(’Error: $err’); } );
5 cnx.Server.foo.call([1,2], function(data) { trace(’Result: $data’)
; });
6 }
167
7 }
To make this work for the Neko target, setup a Neko Web Server, point the url in the Client
to "http://localhost2000/remoting.n" and compile the Server using -main Server
-neko remoting.n.
Error handling
• When an error occurs in a asynchronous call, the error handler is called as seen in the
example above.
• When an error occurs in a synchronous call, an exception is raised on the caller-side as if
we were calling a local method.
Data serialization Haxe Remoting can send a lot of different kinds of data. See Serialization
(10.8).
Related content
• See the remoting package on the API documentation for more details on its classes.
Flash security specifics When Flash accesses a server from a different domain, set up a crossdomain.xml
file on the server, enabling the X-Haxe headers.
1 <cross-domain-policy>
2 <allow-access-from domain="*"/> <!-- or the appropriate domains
-->
3 <allow-http-request-headers-from domain="*" headers="X-Haxe*"/>
4 </cross-domain-policy>
Arguments types are not ensured There is no guarantee of any kind that the arguments types
will be respected when a method is called using remoting. That means even if the arguments of
function foo are typed to Int, the client will still be able to use strings while calling the method.
This can lead to security issues in some cases. When in doubt, check the argument type when
the function is called by using the Std.is method.
168
10.14 Unit testing
The Haxe Standard Library provides basic unit testing classes from the haxe.unit package.
Creating new test cases First, create a new class extending haxe.unit.TestCase and add own
test methods. Every test method name must start with ”test”.
1 class MyTestCase extends haxe.unit.TestCase {
2 public function testBasic() {
3 assertEquals("A", "A");
4 }
5 }
Running unit tests To run the test, an instance of haxe.unit.TestRunner has to be created. Add
the TestCase using the add method and call run to start the test.
1 class Main {
2 static function main() {
3 var r = new haxe.unit.TestRunner();
4 r.add(new MyTestCase());
5 // add other TestCases here
6
7 // finally, run the tests
8 r.run();
9 }
10 }
The result of the test looks like this:
1 Class: MyTestCase
2 .
3 OK 1 tests, 0 failed, 1 success
Test functions The haxe.unit.TestCase class comes with three test functions.
Setup and tear down To run code before or after the test, override the functions setup and
tearDown in the TestCase.
169
6 }
7
8 public function testSetup() {
9 assertEquals("foo", value);
10 }
11 }
Comparing Complex Objects With complex objects it can be difficult to generate expected val-
ues to compare to the actual ones. It can also be a problem that assertEquals doesn’t do a
deep comparison. One way around these issues is to use a string as the expected value and com-
pare it to the actual value converted to a string using Std.string. Below is a trivial example
using an array.
1 public function testArray() {
2 var actual = [1,2,3];
3 assertEquals("[1, 2, 3]", Std.string(actual));
4 }
Related content
• See the haxe.unit package on the API documentation for more details.
170
Part IV
Miscellaneous
171
Chapter 11
Haxelib
172
Chapter 12
Target Details
12.1 JavaScript
12.1.1 Getting started with Haxe/JavaScript
Haxe can be a powerful tool for developing JavaScript applications. Let’s look at our first sample.
This is a very simple example showing the toolchain.
Create a new folder and save this class as Main.hx.
1 import js.Browser;
2 class Main {
3 static function main() {
4 var button = Browser.document.createButtonElement();
5 button.textContent = "Click me!";
6 button.onclick = function(event) {
7 Browser.alert("Haxe is great");
8 }
9 Browser.document.body.appendChild(button);
10 }
11 }
To compile, either run the following from the command line:
1 haxe -js main-javascript.js -main Main -D js-flatten -dce full
Another possibility is to create and run (double-click) a file called compile.hxml. In this
example the hxml file should be in the same directory as the example class.
1 -js main-javascript.js
2 -main Main
3 -D js-flatten
4 -dce full
The output will be a main-javascript.js, which creates and adds a clickable button to the doc-
ument body.
Run the JavaScript To display the output in a browser, create an HTML-document called
index.html and open it.
1 <!DOCTYPE html>
2 <html>
173
3 <body>
4 <script src="main-javascript.js"></script>
5 </body>
6 </html>
More information
• Debugging in JavaScript (Section 13.4)
174
25 OR
26 Set the HTML contents of each element in the set of matched
elements.
27 **/
28 @:overload(function(htmlString:String):js.jquery.JQuery { })
29 @:overload(function(_function:Int -> String -> String):js.jquery.
JQuery { })
30 public function html():String;
31 }
Note that functions can be overloaded to accept different types of arguments and return val-
ues, using the @:overload metadata. Function overloading works only in externs.
Using this extern, we can use jQuery like this:
1 import js.jquery.*;
2 ..
3 new JQuery("#my-div").addClass("brand-success").html("haxe is great!")
;
4 ..
The package and class name of the extern class should be the same as defined in the external
library. If that is not the case, rewrite the path of a class using @:native.
1 package my.application.media;
2
3 @:native(’external.library.media.video’)
4 extern class Video {
5 ..
Some JavaScript libraries favor instantiating classes without using the new keyword. To
prevent the Haxe compiler outputting the new keyword when using a class, we can attach a
@:selfCall metadata to its constructor. For example, when we instantiate the jQuery extern
class above, new JQuery() will be outputted as $() instead of new $(). The @:selfCall
metadata can also be attached to a method. In this case, the method will be interpreted as a direct
call to the object, illustrated as follows:
1 extern class Functor {
2 public function new():Void;
3 @:selfCall function call():Void;
4 }
5
6 class Test {
7 static function main() {
8 var f = new Functor();
9 f.call(); // will be outputted as ‘f();‘
10 }
11 }
Beside externs, Typedefs (3.1) can be another great way to name (or alias) a JavaScript type.
The major difference between typedefs and externs is that, typedefs are duck-typed but externs
are not. Typedefs are suitable for common data structures, e.g. point ({x:Float, y:Float}).
Use of a point structure typedef for function arguments allows external JavaScript functions to
accept point class instances from Haxe or from another JavaScript library. It is also useful for
typing JSON objects.
The Haxe Standard Library comes with externs of jQuery and SWFObject. Their version
compatibility is summarized as follows:
175
Haxe version Library Externs location
3.3 jQuery 1.12.1 / 2.2.1 ¡code¿js.jquery.*¡/code¿
3.2- jQuery 1.6.4 ¡code¿js.JQuery¡/code¿
3.3 SWFObject 2.3 ¡code¿js.swfobject.*¡/code¿
3.2- SWFObject 1.5 ¡code¿js.SWFObject¡/code¿
There are many externs for other popular native libraries available on Haxelib library (11). To
view a list of them, check out the extern tag.
untyped __js__(expr, params) Injects raw JavaScript expressions (12.1.3). It’s allowed
to use {0}, {1}, {2} etc in the expression and use the rest arguments to feed Haxe fields. The
Haxe compiler will take care of the surrounding quotes if needed. The function can also return
values.
1 untyped __js__(’alert("Haxe is great!")’);
2 // output: alert("Haxe is great!");
3
4 var myMessage = "Haxe is great!";
5 untyped __js__(’alert({0})’, myMessage);
6 // output:
7 // var myMessage = "Haxe is great!";
8 // alert(myMessage);
9
10 var myVar:Bool = untyped __js__(’confirm({0})’, "Are you sure?");
11 // output: var myVar = confirm("Are you sure?");
12
13 var hexString:String = untyped __js__(’({0}).toString({1})’, 255, 16);
14 // output: var hexString = (255).toString(16);
176
untyped __typeof__(o) Same as typeof o in JavaScript.
1 var isNodeJS = untyped __typeof__(window) == null;
2 output: var isNodeJS = typeof(window) == null;
Expression injection In some cases it may be needed to inject raw JavaScript code into Haxe-
generated code. With the __js__ function we can inject pure JavaScript code fragments into the
output. This code is always untyped and can not be validated, so it accepts invalid code in the
output, which is error-prone. This could, for example, write a JavaScript comment in the output.
1 untyped __js__(’// haxe is great!’);
A more useful demonstration would be to call a function and pass arguments using the _-
_js__ function. This example illustrates how to call this function and how to pass parameters.
Note that the code interpolation will wrap the quotes around strings in the generated output.
1 // Haxe code:
2 var myVar = untyped __js__(’myObject.myJavaScriptFunction({0}, {1})’,
"Mark", 31);
This will generate the following JavaScript code:
1 // JavaScript Code
2 var myVar = myObject.myJavaScriptFunction("Mark", 31);
JavaScript metadata
Metadata Description
@:expose (?Name=Class path) Makes the class available on the window object or exports for node.js
@:jsRequire Generate javascript module require expression for given extern
@:selfCall Translates method calls into calling object directly
177
12.1.6 Exposing Haxe classes for JavaScript
It is possible to make Haxe classes or static fields available for usage in plain JavaScript. To
expose, add the @:expose metadata to the desired class or static fields.
This example exposes the Haxe class MyClass.
1 @:expose
2 class MyClass {
3 var name:String;
4 function new(name:String) {
5 this.name = name;
6 }
7 public function foo() {
8 return ’Greetings from $name!’;
9 }
10 }
Shallow expose When the code generated by Haxe is part of a larger JavaScript project and
wrapped in a large closure it is not always necessary to expose the Haxe types to global vari-
ables. Compiling the project using -D shallow-expose allows the types or static fields to be
available for the surrounding scope of the generated closure only.
When the code is compiled using -D shallow-expose, the generated output will look like
this:
1 var $hx_exports = $hx_exports || {};
2 (function () { "use strict";
3 var MyClass = $hx_exports.MyClass = function(name) {
4 this.name = name;
5 };
6 MyClass.prototype = {
178
7 foo: function() {
8 return "Greetings from " + this.name + "!";
9 }
10 };
11 })();
12 var MyClass = $hx_exports.MyClass;
In this pattern, a var statement is used to expose the module; it doesn’t write to the window
or exports object.
Modern JavaScript platforms, such as Node.js provide a way of loading objects from external
modules using the ”require” function. Haxe supports automatic generation of ”require” state-
ments for extern classes.
This feature can be enabled by specifying @:jsRequire metadata for the extern class. If
our extern class represents a whole module, we pass a single argument to the @:jsRequire
metadata specifying the name of the module to load:
1 @:jsRequire("fs")
2 extern class FS {
3 static function readFileSync(path:String, encoding:String):String;
4 }
In case our extern class represents an object within a module, second @:jsRequire argu-
ment specifies an object to load from a module:
1 @:jsRequire("http", "Server")
2 extern class HTTPServer {
3 function new();
4 }
12.2 Flash
12.2.1 Getting started with Haxe/Flash
Developing Flash applications is really easy with Haxe. Let’s look at our first code sample. This
is a basic example showing most of the toolchain.
Create a new folder and save this class as Main.hx.
1 import flash.Lib;
2 import flash.display.Shape;
3 class Main {
4 static function main() {
5 var stage = Lib.current.stage;
6
7 // create a center aligned rounded gray square
179
8 var shape = new Shape();
9 shape.graphics.beginFill(0x333333);
10 shape.graphics.drawRoundRect(0, 0, 100, 100, 10);
11 shape.x = (stage.stageWidth - 100) / 2;
12 shape.y = (stage.stageHeight - 100) / 2;
13
14 stage.addChild(shape);
15 }
16 }
To compile this, either run the following from the command line:
1 haxe -swf main-flash.swf -main Main -swf-version 15 -swf-header
960:640:60:f68712
Another possibility is to create and run (double-click) a file called compile.hxml. In this
example the hxml file should be in the same directory as the example class.
1 -swf main-flash.swf
2 -main Main
3 -swf-version 15
4 -swf-header 960:640:60:f68712
The output will be a main-flash.swf with size 960x640 pixels at 60 FPS with an orange back-
ground color and a gray square in the center.
Display the Flash Run the SWF standalone using the Standalone Debugger FlashPlayer.
To display the output in a browser using the Flash plugin, create an HTML-document called
index.html and open it.
1 <!DOCTYPE html>
2 <html>
3 <body>
4 <embed src="main-flash.swf" width="960" height="640">
5 </body>
6 </html>
More information
embed
(AS3-metadata) use Flash specific compiler metadata (12.2.4) like @:bitmap, @:font, @:sound
or @:file.
1 import flash.Lib;
2 import flash.display.BitmapData;
3 import flash.display.Bitmap;
180
4
5 class Main {
6 public static function main() {
7 var img = new Bitmap( new MyBitmapData(0, 0) );
8 Lib.current.addChild(img);
9 }
10 }
11
12 @:bitmap("relative/path/to/myfile.png")
13 class MyBitmapData extends BitmapData { }
Flash metadata
Metadata Description
@:bind Override Swf class declaration
@:bitmap (Bitmap file path) Embeds given bitmap data into the class (must extend flash.display
@:debug Forces debug information to be generated into the Swf even without -de
@:file(File path) Includes a given binary file into the target Swf and associates it with the
@:font (TTF path Range String) Embeds the given TrueType font into the class (must extend flash.tex
@:getter (Class field name) Generates a native getter function on the given field
@:noDebug Does not generate debug information into the Swf even if -debug is set
@:ns Internally used by the Swf generator to handle namespaces
@:setter (Class field name) Generates a native setter function on the given field
@:sound (File path) Includes a given .wav or .mp3 file into the target Swf and associates i
12.3 Neko
12.4 PHP
12.4.1 Getting started with Haxe/PHP
To get started with Haxe/PHP, create a new folder and save this class as Main.hx.
181
1 import php.Lib;
2
3 class Main {
4 static function main() {
5 Lib.println(’Haxe is great!’);
6 }
7 }
More information
• Haxe PHP API docs
• PHP.net Documentation
• PHP to Haxe tool
untyped __php__(expr) Injects raw PHP code expressions. It’s possible to pass fields from
Haxe source code using String Interpolation (6.5).
1 var value:String = "test";
2 untyped __php__("echo ’<pre>’; print_r($value); echo ’</pre>’;");
3 // output: echo ’<pre>’; print_r(’test’); echo ’</pre>’;
untyped __call__(function, arg, arg, arg...) Calls a PHP function with the de-
sired number of arguments and returns what the PHP function returns.
1 var value = untyped __call__("array", 1,2,3);
2 // output returns a NativeArray with values [1,2,3]
untyped __var__(global, paramName) Get the values from global vars. Note that the
dollar sign in the Haxe code is omitted.
1 var value : String = untyped __var__(’_SERVER’, ’REQUEST_METHOD’)
2 // output: $value = $_SERVER[’REQUEST_METHOD’]
182
untyped __physeq__(val1, val2) Strict equals test between the two values. Returns a
Bool.
1 var isFalse = untyped __physeq__(false, value);
2 // output: $isFalse = false === $value;
12.5 C++
12.5.1 Getting started with Haxe/C++
The c++ target uses various c++ compilers, which are assumed to be already installed on the
system, to create native executables or libraries. The compilation happens in two phases. Firstly,
the haxe compiler generates source, header and build files in an output directory. Secondly,
the ”hxcpp” Haxelib library (11) is invoked to run the system compilers and linkers required to
generate the ultimate result.
Prerequisites Before you can use the C++ target, you need in install
• hxcpp, e.g. haxelib install hxcpp.
• A system or cross-compiler
System Compilers System compilers are supported on the three primary operating systems -
Mac, Linux, and Windows. On Mac, it is recommended that you install the latest Xcode from
the Mac App Store. On Linux, it is recommended that you use the system package manager to
install the compilers and on Windows, Microsoft Visual Studio is recommended. On Windows,
you can also use gcc-based compilers. A minimal distribution is included in a Haxelib library
(11), and can be installed with haxelib install minimingw.
Cross Compilers Hxcpp can be used to compile for non-host architectures if you have a suitable
cross-compiler installed. The compilers are usually supplied in the form of a Software Develop-
ment Kits (SDK), or in the case of iOS devices, come with the system compiler (Xcode). Selecting
which compiler to use is achieved by defining particular variables in the hxcpp build environ-
ment. Note that the hxcpp build tool is only responsible for producing a native executable or a
native library (static or dynamic), not the complete bundling and packaging of assets and meta-
data that is typically required for mobile devices. Additional haxe libraries can be used for this
task.
Defining From the Command Line The easiest way to change the hxcpp build environment is
to pass the defines though the haxe command line using -D. Key-value pairs can also be passed
this way, e.g.:
haxe -main Main -cpp cpp -D android -D static_link -D PLATFORM=android-9
183
platform is set to a particular value. This platform information is passed on to the SDK so it can
generate the appropriate code.
Advanced users can add additional defines to the system at compile time using macros. These
definitions will also be passed on to the hxcpp build tool.
Defining From the System Environment Variables The hxcpp build tool will import all the
system environment variables, so you can configure the processes using the system like:
setenv HXCPP_VERBOSE
If you are running haxe though an IDE, some care must be taken with environment variables
since the variables may be read once from the environment in which the IDE was started, rather
than changing when the variables are changed on the system.
Defining From .hxcpp config.xml The hxcpp build tool parses several ”build files”. These files
are in a basic xml file format, and can be used to set, or conditionally set, configuration variables.
As part of the build process, the .hxcpp_config.xml file, known as the configuration file, will
be read (twice). This file is located in the user’s home directory (or user’s profile directory on
windows) and is the best place to configure variables that are specific to the system that rarely
change, such as the location of the cross-compiler SDKs. A placeholder file will be generated the
first time hxcpp is run by a user on the machine. You can see the exact location of this file in the
build log if you compile with the HXCPP_VERBOSE define.
The configuration file is read twice. The first time the "vars" section is read early in the
configuration process, and is used to set up and configure the location of the compilers. The
second time, the "exes" section is read near the end of the processes and allows modification of
the compiler or build process based on all the available information. See build.xml File Format
(Section 12.5.3) for details on the file format.
Defining Via @:buildXml Metadata Configuration data can be injected into the ”build.xml”
file that is created by haxe. This is done attaching metadata to a haxe class that is directly or
indirectly included in the build. For example:
1 @:buildXml("
2 <target id=’haxe’>
3 <lib name=’${haxelib:nme}/lib/${BINDIR}/libnme${LIBEXTRA}${LIBEXT
}’/>
4 </target>
5 <include name=’${haxelib:nme}/lib/NmeLink.xml’/>
6 ")
7 @:keep class StaticNme
8 {
9 ...
This metadata is best for adding libraries or include paths to the build. Some notes:
• The @:keep metadata is added to prevent dead-code-elimination from removing this class
• Quoting can be a bit tricky - here the double-quotes are used for haxe, and the single quotes
are added to the build.xml.
• Injecting a single ”include” command is a good way to manage the quoting issue.
• Knowledge of the build system is required to get this right. See build.xml File Format
(Section 12.5.3) for details on the file format.
184
• The build.xml file is read after the choice of compiler has been made, so it is generally not
suitable for configuring the compiler.
Running a build.xml File With Hxcpp When you compile a haxe program with hxcpp, the
haxe compiler will normally run the hxcpp build tool on the generated build.xml file automat-
ically. You can, however, prevent this by adding -D no-compilation to the haxe command
line.
You can run the hxcpp build tool on your own build files using
haxelib run hxcpp myfile.xml [-Ddefine] [-Dkey=value]
Note the lack of space between the ”-D” and the variable name.
A minimal build.xml file consists of an xml container and a dummy default target, like:
1 <xml>
2 <echo value="Hello!" />
3 <target id="default" />
4 </xml>
Conditions, Substitutions and Sections Most elements in the hxcpp xml file allow a common
syntax for dynamic configuration.
Xml elements can contain conditional ”if” and/or ”unless” values. These conditions are eval-
uated at parse time and the entire element will be skipped if the condition fails. For example
adding this lines:
1 <xml>
2 <echo value="Hello A" if="A" />
3 <echo value="Hello A && B" if="A B" />
4 <echo value="Hello A || B" if="A || B" />
5 <echo value="Hello !A" unless="A" />
6 <echo value="Hello !(A && B)" unless="A B" />
7 <echo value="Hello !(A || B)" unless="A || B" />
8 <echo value="Never Never" if="A" unless="A" />
9 <target id="default" />
10 </xml>
and running:
haxelib run hxcpp myfile.xml -DA
shows how the logic depends on whether or not A or B has been defined.
Sections can be used to group commands together based on a common condition. They can
also be used to include only part of another xml file, but this technique is currently only used
when parsing the .hxcpp config.xml file. For example:
185
1 <section if="C" >
2 <echo value="I See" />
3 <echo value="You" />
4 </section>
The xml attribute values can be substituted with variable values using dollars-brace syntax.
Using a colon allows a function-call to be substituted, e.g.:
1 <echo value="some var = ${SOME_VAR}" />
2 <echo value="${haxelib:hxcpp}/include" />
186
• HXCPP MSVC VER
• HXCPP NO COLOR
• HXCPP NO COLOUR
• HXCPP VERBOSE
• HXCPP CPP11
• HXCPP STRICT CASTS
• HXCPP VISIT ALLOCS
• HXCPP WINXP COMPAT
• NDKV
• NO AUTO MSVC
• PLATFORM
• QNX HOST
• QNX TARGET
• TOOLCHAIN VERSION
• USE GCC FILETYPES
187
• apple
• blackberry
• cygwin
• dll import
• emscripten
• gph
• hardfp
• haxe ver
• ios
• iphone
• iphoneos
• iphonesim
• linux
• linux host
• mac host
• macos
• mingw
• rpi
• simulator
• tizen
• toolchain
• webos
• windows
• windows host
• winrt
• xcompile
188
12.5.5 Using C++ Pointers
12.6 Cppia
12.7 Java
12.8 C#
12.9 Python
12.10 Lua
12.10.1 Getting started with Haxe/Lua
To get started with Haxe for Lua, it’s necessary to pick a Lua version and install dependencies.
All versions of Lua are supported, but may require different libraries. Lua 5.1, 5.2, 5.3, and LuaJIT
2.0 and 2.1 (beta) are supported.
Lua is a very lightweight language that ships with a much smaller feature set than Haxe. In
some cases (e.g. regex), it’s necessary to install supplementary libraries that are used to support
basic Haxe functionality.
In order to cover all dependencies, it is recommended to install and use LuaRocks. However,
if you do not utilize relevant behavior (e.g. regex) on a given platform, or if you are using an
embedded Lua client, then it is not necessary to install any packages for basic language function-
ality.
With LuaRocks, install the following packages:
1 luarocks install lrexlib-pcre
2 luarocks install environ
3 luarocks install luasocket
4 luarocks install luv
On Lua 5.1, install the bitops library:
1 luarocks install luabitop
On Lua 5.3, install the bit32 library instead:
1 luarocks install bit32
When developing for multiple Lua versions, it is recommended to use the Python package
hererocks.
With Lua installed, it is possible to write a simple command line application.
Create a new folder, and save this class as Main.hx.
1 class Main {
2 static function main() {
3 trace("hello world");
4 }
5 }
189
More information
• Lua Homepage
• LuaJIT Homepage
-D lua_ver Enable special features for a specific Lua version. Currently, this flag will enable
extern methods that are specific to certain versions (e.g. table.pack in Lua ¿ 5.2).
-D luajit Enable special features for LuaJIT. Currently this flag will enable the jit and ffi
module namespaces.
12.10.4 Multireturns
Lua allows for multiple values to be returned from a given function. Haxe does not support
this by default, but can allow extern definitions to reference multireturn values through the
@:multiReturn metadata.
1 class Main {
2 static function main() {
3 var strfind = NativeString.find("foobar", "bar");
4 trace(strfind.begin);
5 trace(strfind.end);
6 }
7 }
8
9 @:native("string")
10 extern class NativeString {
11 public static function find(str : String, target : String):
StringFind;
12 }
13
14 @:multiReturn extern class StringFind {
15 var begin : Int;
16 var end : Int;
17 }
This example has three parts:
• The extern class NativeString which is an extern for the base string library in Lua.
• The StringFind class which is marked as @:multiReturn that describes the return val-
ues.
• The Main class that invokes the string method as a simple example.
190
The multireturn behavior in Haxe is optimized based on usage. If fields are only accessed
directly, the Haxe compiler will allocate the multireturn to individual variables. But, if you pass
or assign the entire multireturn value, the compiler will wrap all values into a table object. This
operation ensures that multireturn variable handling only carries as much overhead as needed.
12.11 HashLink
191
Chapter 13
Debugging
Custom trace The trace can have a custom output by changing the Log.trace method where
all trace calls are redirected.
1 class Main {
2 static function main() {
3 haxe.Log.trace = function(v:Dynamic, ?infos:haxe.PosInfos) {
4 //custom trace function here
5 }
6 trace("hello","warning",123);
7 }
8 }
The v argument is the first parameter of the trace call. It can be a String or any other value.
The optional infos argument contains extra position parameter, see below.
The infos.customParams array contains all extra arguments that were given to the origi-
nal trace. If no extra parameters are passed, it will be null.
As illustration, the previous example will be compiled as if it was calling the following:
1 haxe.Log.trace("hello", {
2 fileName : "Test.hx",
3 lineNumber : 6,
4 className : "Test",
5 methodName : "main",
6 customParams : ["warning",123]
7 });
192
Removing traces You can simply remove all trace informations by compiling your project with
--no-traces argument. This will remove all trace calls as if they were not present in the pro-
gram.
193
Breakpoints In most browser developer tools breakpoints can be set to pause the code execu-
tion and start debugging. This mostly can be done by clicking near the line numbers. At each
breakpoint JavaScript will stop executing and let the current values be inspected. After examin-
ing the values, the execution of code can be resumed (typically with a play button).
In JavaScript a developer can use the debugger statement functionality to do the same from
code. In Haxe the same can be done with js.Lib.debug function; this inserts a debugger
statement that will make a breakpoint if a debugger is available. If no debugging functionality is
available, this statement has no effect.
• Chrome source-maps
• Firefox source-maps
• Safari source-maps
194
7 throw "Terrible error";
8 }
9 }
Building it with flags:
1 -main Main
2 -D php7
3 -php build
4 -debug
Running this build will trace the uncaught exception:
1 $ php build/index.php
2 PHP Fatal error: Uncaught php/_Boot/HxException: Terrible error in
build/lib/Main.php:25
3 Stack trace:
4 #0 build/lib/Main.php(16): Main::terribleError()
5 #1 build/index.php(13): Main::main()
6 #2 {main}
7 thrown in build/lib/Main.php on line 25
Install JStack using haxelib install jstack. JStack automatically adds -D source_-
map there is no need to add it manually. Now if JStack is installed, add it to the compilation:
1 -main Main
2 -D php7
3 -php build
4 -debug
5 -lib jstack
The output will have more informative stack trace for exceptions:
1 $ php build/index.php
2 Terrible error
3 Called from Main.terribleError (src/Main.hx line 7)
4 Called from Main.main (src/Main.hx line 3)
5 Called from build/index.php line 13
195