Haxe is a general-purpose language that provides platform support for C++, C#, Swf/ActionScript, JavaScript, Java, PHP, Python, Lua, HashLink, and Neko bytecode (the latter two being also written by the Haxe author). Note that this guide is for Haxe version 3. Some of the guide may be applicable to older versions, but it is recommended to use other references.
1/*
2 Welcome to Learn Haxe 3 in 15 minutes. http://www.haxe.org
3 This is an executable tutorial. You can compile and run it using the haxe
4 compiler, while in the same directory as LearnHaxe.hx:
5
6 $ haxe -main LearnHaxe3 --interp
7
8 Look for the slash-star marks surrounding these paragraphs. We are inside
9 a "Multiline comment". We can leave some notes here that will get ignored
10 by the compiler.
11
12 Multiline comments are also used to generate javadoc-style documentation for
13 haxedoc. They will be used for haxedoc if they immediately precede a class,
14 class function, or class variable.
15 */
16
17// Double slashes like this will give a single-line comment.
18
19/*
20 This is your first actual haxe code coming up, it's declaring an empty
21 package. A package isn't necessary, but it's useful if you want to create
22 a namespace for your code (e.g. org.yourapp.ClassName).
23
24 Omitting package declaration is the same as declaring an empty package.
25 */
26package; // empty package, no namespace.
27
28/*
29 Packages are directories that contain modules. Each module is a .hx file
30 that contains types defined in a package. Package names (e.g. org.yourapp)
31 must be lower case while module names are capitalized. A module contain one
32 or more types whose names are also capitalized.
33
34 E.g, the class "org.yourapp.Foo" should have the folder structure
35 org/module/Foo.hx, as accessible from the compiler's working directory or
36 class path.
37
38 If you import code from other files, it must be declared before the rest of
39 the code. Haxe provides a lot of common default classes to get you started:
40 */
41import haxe.ds.ArraySort;
42
43// you can import many classes/modules at once with "*"
44import haxe.ds.*;
45
46// you can import static fields
47import Lambda.array;
48
49// you can also use "*" to import all static fields
50import Math.*;
51
52// You can also import classes in a special way, enabling them to extend the
53// functionality of other classes like a "mixin". More on 'using' later.
54using StringTools;
55
56// Typedefs are like variables... for types. They must be declared before any
57// code. More on this later.
58typedef FooString = String;
59
60// Typedefs can also reference "structural" types, more on that later as well.
61typedef FooObject = { foo: String };
62
63// Here's the class definition. It's the main class for the file, since it has
64// the same name (LearnHaxe3).
65class LearnHaxe3 {
66 /*
67 If you want certain code to run automatically, you need to put it in
68 a static main function, and specify the class in the compiler arguments.
69 In this case, we've specified the "LearnHaxe3" class in the compiler
70 arguments above.
71 */
72 static function main() {
73 /*
74 Trace is the default method of printing haxe expressions to the
75 screen. Different targets will have different methods of
76 accomplishing this. E.g., java, c++, c#, etc. will print to std
77 out. JavaScript will print to console.log, and flash will print to
78 an embedded TextField. All traces come with a default newline.
79 Finally, It's possible to prevent traces from showing by using the
80 "--no-traces" argument on the compiler.
81 */
82 trace("Hello World, with trace()!");
83
84 // Trace can handle any type of value or object. It will try to print
85 // a representation of the expression as best it can. You can also
86 // concatenate strings with the "+" operator:
87 trace("Integer: " + 10 + " Float: " + 3.14 + " Boolean: " + true);
88
89 // In Haxe, it's required to separate expressions in the same block with
90 // semicolons. But, you can put two expressions on one line:
91 trace('two expressions..'); trace('one line');
92
93
94 //////////////////////////////////////////////////////////////////
95 // Types & Variables
96 //////////////////////////////////////////////////////////////////
97 trace("***Types & Variables***");
98
99 // You can save values and references to data structures using the
100 // "var" keyword:
101 var an_integer:Int = 1;
102 trace(an_integer + " is the value for an_integer");
103
104 /*
105 Haxe is statically typed, so "an_integer" is declared to have an
106 "Int" type, and the rest of the expression assigns the value "1" to
107 it. It's not necessary to declare the type in many cases. Here,
108 the haxe compiler is inferring that the type of another_integer
109 should be "Int".
110 */
111 var another_integer = 2;
112 trace(another_integer + " is the value for another_integer");
113
114 // The $type() method prints the type that the compiler assigns:
115 $type(another_integer);
116
117 // You can also represent integers with hexadecimal:
118 var hex_integer = 0xffffff;
119
120 /*
121 Haxe uses platform precision for Int and Float sizes. It also
122 uses the platform behavior for overflow.
123 (Other numeric types and behavior are possible using special
124 libraries.)
125
126 In addition to simple values like Integers, Floats, and Booleans,
127 Haxe provides standard library implementations for common data
128 structures like strings, arrays, lists, and maps:
129 */
130
131 // Strings can have double or single quotes.
132 var a_string = "some" + 'string';
133 trace(a_string + " is the value for a_string");
134
135 // Strings can be "interpolated" by inserting variables into specific
136 // positions. The string must be single quoted, and the variable must
137 // be preceded with "$". Expressions can be enclosed in ${...}.
138 var x = 1;
139 var an_interpolated_string = 'the value of x is $x';
140 var another_interpolated_string = 'the value of x + 1 is ${x + 1}';
141
142 // Strings are immutable, instance methods will return a copy of
143 // parts or all of the string. (See also the StringBuf class).
144 var a_sub_string = a_string.substr(0,4);
145 trace(a_sub_string + " is the value for a_sub_string");
146
147 // Regexes are also supported, but there's not enough space here to go
148 // into much detail.
149 var re = ~/foobar/;
150 trace(re.match('foo') + " is the value for (~/foobar/.match('foo')))");
151
152 // Arrays are zero-indexed, dynamic, and mutable. Missing values are
153 // defined as null.
154 var a = new Array<String>(); // an array that contains Strings
155 a[0] = 'foo';
156 trace(a.length + " is the value for a.length");
157 a[9] = 'bar';
158 trace(a.length + " is the value for a.length (after modification)");
159 trace(a[3] + " is the value for a[3]"); //null
160
161 // Arrays are *generic*, so you can indicate which values they contain
162 // with a type parameter:
163 var a2 = new Array<Int>(); // an array of Ints
164 var a3 = new Array<Array<String>>(); // an Array of Arrays (of Strings).
165
166 // Maps are simple key/value data structures. The key and the value
167 // can be of any type.
168 // Here, the keys are strings, and the values are Ints:
169 var m = new Map<String, Int>();
170 m.set('foo', 4);
171 // You can also use array notation:
172 m['bar'] = 5;
173 trace(m.exists('bar') + " is the value for m.exists('bar')");
174 trace(m.get('bar') + " is the value for m.get('bar')");
175 trace(m['bar'] + " is the value for m['bar']");
176
177 var m2 = ['foo' => 4, 'baz' => 6]; // Alternative map syntax
178 trace(m2 + " is the value for m2");
179
180 // Remember, you can use type inference. The Haxe compiler will
181 // decide the type of the variable the first time you pass an
182 // argument that sets a type parameter.
183 var m3 = new Map();
184 m3.set(6, 'baz'); // m3 is now a Map<Int,String>
185 trace(m3 + " is the value for m3");
186
187 // Haxe has some more common datastructures in the haxe.ds module, such
188 // as List, Stack, and BalancedTree.
189
190
191 //////////////////////////////////////////////////////////////////
192 // Operators
193 //////////////////////////////////////////////////////////////////
194 trace("***OPERATORS***");
195
196 // basic arithmetic
197 trace((4 + 3) + " is the value for (4 + 3)");
198 trace((5 - 1) + " is the value for (5 - 1)");
199 trace((2 * 4) + " is the value for (2 * 4)");
200 // Division always produces Floats.
201 trace((8 / 3) + " is the value for (8 / 3) (a Float)");
202 trace((12 % 4) + " is the value for (12 % 4)");
203
204 // basic comparison
205 trace((3 == 2) + " is the value for 3 == 2");
206 trace((3 != 2) + " is the value for 3 != 2");
207 trace((3 > 2) + " is the value for 3 > 2");
208 trace((3 < 2) + " is the value for 3 < 2");
209 trace((3 >= 2) + " is the value for 3 >= 2");
210 trace((3 <= 2) + " is the value for 3 <= 2");
211
212 // standard bitwise operators
213 /*
214 ~ Unary bitwise complement
215 << Signed left shift
216 >> Signed right shift
217 >>> Unsigned right shift
218 & Bitwise AND
219 ^ Bitwise exclusive OR
220 | Bitwise inclusive OR
221 */
222
223 var i = 0;
224 trace("Pre-/Post- Increments and Decrements");
225 trace(i++); // i = 1. Post-Increment
226 trace(++i); // i = 2. Pre-Increment
227 trace(i--); // i = 1. Post-Decrement
228 trace(--i); // i = 0. Pre-Decrement
229
230
231 //////////////////////////////////////////////////////////////////
232 // Control Structures
233 //////////////////////////////////////////////////////////////////
234 trace("***CONTROL STRUCTURES***");
235
236 // if statements
237 var j = 10;
238 if (j == 10) {
239 trace("this is printed");
240 } else if (j > 10) {
241 trace("not greater than 10, so not printed");
242 } else {
243 trace("also not printed.");
244 }
245
246 // there is also a "ternary" if:
247 (j == 10) ? trace("equals 10") : trace("not equals 10");
248
249 // Finally, there is another form of control structure that operates
250 // at compile time: conditional compilation.
251#if neko
252 trace('hello from neko');
253#elseif js
254 trace('hello from js');
255#else
256 trace('hello from another platform!');
257#end
258
259 // The compiled code will change depending on the platform target.
260 // Since we're compiling for neko (-x or -neko), we only get the neko
261 // greeting.
262
263
264 trace("Looping and Iteration");
265
266 // while loop
267 var k = 0;
268 while (k < 100) {
269 // trace(counter); // will print out numbers 0-99
270 k++;
271 }
272
273 // do-while loop
274 var l = 0;
275 do {
276 trace("do statement always runs at least once");
277 } while (l > 0);
278
279 // for loop
280 // There is no c-style for loop in Haxe, because they are prone
281 // to error, and not necessary. Instead, Haxe has a much simpler
282 // and safer version that uses Iterators (more on those later).
283 var m = [1, 2, 3];
284 for (val in m) {
285 trace(val + " is the value for val in the m array");
286 }
287
288 // Note that you can iterate on an index using a range
289 // (more on ranges later as well)
290 var n = ['foo', 'bar', 'baz'];
291 for (val in 0...n.length) {
292 trace(val + " is the value for val (an index for n)");
293 }
294
295
296 trace("Array Comprehensions");
297
298 // Array comprehensions give you the ability to iterate over arrays
299 // while also creating filters and modifications.
300 var filtered_n = [for (val in n) if (val != "foo") val];
301 trace(filtered_n + " is the value for filtered_n");
302
303 var modified_n = [for (val in n) val += '!'];
304 trace(modified_n + " is the value for modified_n");
305
306 var filtered_and_modified_n
307 = [for (val in n) if (val != "foo") val += "!"];
308 trace(filtered_and_modified_n
309 + " is the value for filtered_and_modified_n");
310
311
312 //////////////////////////////////////////////////////////////////
313 // Switch Statements (Value Type)
314 //////////////////////////////////////////////////////////////////
315 trace("***SWITCH STATEMENTS (VALUE TYPES)***");
316
317 /*
318 Switch statements in Haxe are very powerful. In addition to working
319 on basic values like strings and ints, they can also work on the
320 generalized algebraic data types in enums (more on enums later).
321 Here are some basic value examples for now:
322 */
323 var my_dog_name = "fido";
324 var favorite_thing = "";
325 switch (my_dog_name) {
326 case "fido" : favorite_thing = "bone";
327 case "rex" : favorite_thing = "shoe";
328 case "spot" : favorite_thing = "tennis ball";
329 default : favorite_thing = "some unknown treat";
330 // same as default:
331 // case _ : favorite_thing = "some unknown treat";
332 }
333 // The "_" case above is a "wildcard" value that will match anything.
334
335 trace("My dog's name is " + my_dog_name
336 + ", and his favorite thing is a: "
337 + favorite_thing);
338
339
340 //////////////////////////////////////////////////////////////////
341 // Expression Statements
342 //////////////////////////////////////////////////////////////////
343 trace("***EXPRESSION STATEMENTS***");
344
345 // Haxe control statements are very powerful because every statement
346 // is also an expression, consider:
347
348 // if statements
349 var k = if (true) 10 else 20;
350
351 trace("k equals ", k); // outputs 10
352
353 var other_favorite_thing = switch (my_dog_name) {
354 case "fido" : "teddy";
355 case "rex" : "stick";
356 case "spot" : "football";
357 default : "some unknown treat";
358 }
359
360 trace("My dog's name is " + my_dog_name
361 + ", and his other favorite thing is a: "
362 + other_favorite_thing);
363
364
365 //////////////////////////////////////////////////////////////////
366 // Converting Value Types
367 //////////////////////////////////////////////////////////////////
368 trace("***CONVERTING VALUE TYPES***");
369
370 // You can convert strings to ints fairly easily.
371
372 // string to integer
373 Std.parseInt("0"); // returns 0
374 Std.parseFloat("0.4"); // returns 0.4
375
376 // integer to string
377 Std.string(0); // returns "0"
378 // concatenation with strings will auto-convert to string.
379 0 + ""; // returns "0"
380 true + ""; // returns "true"
381 // See documentation for parsing in Std for more details.
382
383
384 //////////////////////////////////////////////////////////////////
385 // Dealing with Types
386 //////////////////////////////////////////////////////////////////
387
388 /*
389 As mentioned before, Haxe is a statically typed language. All in
390 all, static typing is a wonderful thing. It enables
391 precise autocompletions, and can be used to thoroughly check the
392 correctness of a program. Plus, the Haxe compiler is super fast.
393
394 *HOWEVER*, there are times when you just wish the compiler would
395 let something slide, and not throw a type error in a given case.
396
397 To do this, Haxe has two separate keywords. The first is the
398 "Dynamic" type:
399 */
400 var dyn: Dynamic = "any type of variable, such as this string";
401
402 /*
403 All that you know for certain with a Dynamic variable is that the
404 compiler will no longer worry about what type it is. It is like a
405 wildcard variable: You can pass it instead of any variable type,
406 and you can assign any variable type you want.
407
408 The other more extreme option is the "untyped" keyword:
409 */
410 untyped {
411 var x:Int = 'foo'; // This can't be right!
412 var y:String = 4; // Madness!
413 }
414
415 /*
416 The untyped keyword operates on entire *blocks* of code, skipping
417 any type checks that might be otherwise required. This keyword should
418 be used very sparingly, such as in limited conditionally-compiled
419 situations where type checking is a hindrance.
420
421 In general, skipping type checks is *not* recommended. Use the
422 enum, inheritance, or structural type models in order to help ensure
423 the correctness of your program. Only when you're certain that none
424 of the type models work should you resort to "Dynamic" or "untyped".
425 */
426
427
428 //////////////////////////////////////////////////////////////////
429 // Basic Object Oriented Programming
430 //////////////////////////////////////////////////////////////////
431 trace("***BASIC OBJECT ORIENTED PROGRAMMING***");
432
433 // Create an instance of FooClass. The classes for this are at the
434 // end of the file.
435 var foo_instance = new FooClass(3);
436
437 // read the public variable normally
438 trace(foo_instance.public_any
439 + " is the value for foo_instance.public_any");
440
441 // we can read this variable
442 trace(foo_instance.public_read
443 + " is the value for foo_instance.public_read");
444 // but not write it; this will throw an error if uncommented:
445 // foo_instance.public_read = 4;
446 // trace(foo_instance.public_write); // as will this.
447
448 // Calls the toString method:
449 trace(foo_instance + " is the value for foo_instance");
450 // same thing:
451 trace(foo_instance.toString()
452 + " is the value for foo_instance.toString()");
453
454 // The foo_instance has the "FooClass" type, while acceptBarInstance
455 // has the BarClass type. However, since FooClass extends BarClass, it
456 // is accepted.
457 BarClass.acceptBarInstance(foo_instance);
458
459 // The classes below have some more advanced examples, the "example()"
460 // method will just run them here.
461 SimpleEnumTest.example();
462 ComplexEnumTest.example();
463 TypedefsAndStructuralTypes.example();
464 UsingExample.example();
465 }
466}
467
468// This is the "child class" of the main LearnHaxe3 Class.
469class FooClass extends BarClass implements BarInterface {
470 public var public_any:Int; // public variables are accessible anywhere
471 public var public_read (default, null): Int; // enable only public read
472 public var public_write (null, default): Int; // or only public write
473 // Use this style to enable getters/setters:
474 public var property (get, set): Int;
475
476 // private variables are not available outside the class.
477 // see @:allow for ways around this.
478 var _private:Int; // variables are private if they are not marked public
479
480 // a public constructor
481 public function new(arg:Int) {
482 // call the constructor of the parent object, since we extended BarClass:
483 super();
484
485 this.public_any = 0;
486 this._private = arg;
487 }
488
489 // getter for _private
490 function get_property() : Int {
491 return _private;
492 }
493
494 // setter for _private
495 function set_property(val:Int) : Int {
496 _private = val;
497 return val;
498 }
499
500 // Special function that is called whenever an instance is cast to a string.
501 public function toString() {
502 return _private + " with toString() method!";
503 }
504
505 // this class needs to have this function defined, since it implements
506 // the BarInterface interface.
507 public function baseFunction(x: Int) : String {
508 // convert the int to string automatically
509 return x + " was passed into baseFunction!";
510 }
511}
512
513// A simple class to extend.
514class BarClass {
515 var base_variable:Int;
516 public function new() {
517 base_variable = 4;
518 }
519 public static function acceptBarInstance(b:BarClass) {}
520}
521
522// A simple interface to implement
523interface BarInterface {
524 public function baseFunction(x:Int):String;
525}
526
527
528//////////////////////////////////////////////////////////////////
529// Enums and Switch Statements
530//////////////////////////////////////////////////////////////////
531
532// Enums in Haxe are very powerful. In their simplest form, enums
533// are a type with a limited number of states:
534enum SimpleEnum {
535 Foo;
536 Bar;
537 Baz;
538}
539
540// Here's a class that uses it:
541class SimpleEnumTest {
542 public static function example() {
543 // You can specify the "full" name,
544 var e_explicit:SimpleEnum = SimpleEnum.Foo;
545 var e = Foo; // but inference will work as well.
546 switch (e) {
547 case Foo: trace("e was Foo");
548 case Bar: trace("e was Bar");
549 case Baz: trace("e was Baz"); // comment this line to throw an error.
550 }
551
552 /*
553 This doesn't seem so different from simple value switches on strings.
554 However, if we don't include *all* of the states, the compiler will
555 complain. You can try it by commenting out a line above.
556
557 You can also specify a default for enum switches as well:
558 */
559 switch (e) {
560 case Foo: trace("e was Foo again");
561 default : trace("default works here too");
562 }
563 }
564}
565
566// Enums go much further than simple states, we can also enumerate
567// *constructors*, but we'll need a more complex enum example.
568enum ComplexEnum {
569 IntEnum(i:Int);
570 MultiEnum(i:Int, j:String, k:Float);
571 SimpleEnumEnum(s:SimpleEnum);
572 ComplexEnumEnum(c:ComplexEnum);
573}
574// Note: The enum above can include *other* enums as well, including itself!
575// Note: This is what's called *Algebraic data type* in some other languages.
576
577class ComplexEnumTest {
578 public static function example() {
579 var e1:ComplexEnum = IntEnum(4); // specifying the enum parameter
580 // Now we can switch on the enum, as well as extract any parameters
581 // it might have had.
582 switch (e1) {
583 case IntEnum(x) : trace('$x was the parameter passed to e1');
584 default: trace("Shouldn't be printed");
585 }
586
587 // another parameter here that is itself an enum... an enum enum?
588 var e2 = SimpleEnumEnum(Foo);
589 switch (e2){
590 case SimpleEnumEnum(s): trace('$s was the parameter passed to e2');
591 default: trace("Shouldn't be printed");
592 }
593
594 // enums all the way down
595 var e3 = ComplexEnumEnum(ComplexEnumEnum(MultiEnum(4, 'hi', 4.3)));
596 switch (e3) {
597 // You can look for certain nested enums by specifying them
598 // explicitly:
599 case ComplexEnumEnum(ComplexEnumEnum(MultiEnum(i,j,k))) : {
600 trace('$i, $j, and $k were passed into this nested monster');
601 }
602 default: trace("Shouldn't be printed");
603 }
604 // Check out "generalized algebraic data types" (GADT) for more details
605 // on why these are so great.
606 }
607}
608
609class TypedefsAndStructuralTypes {
610 public static function example() {
611 // Here we're going to use typedef types, instead of base types.
612 // At the top we've declared the type "FooString" to mean a "String" type.
613 var t1:FooString = "some string";
614
615 // We can use typedefs for "structural types" as well. These types are
616 // defined by their field structure, not by class inheritance. Here's
617 // an anonymous object with a String field named "foo":
618 var anon_obj = { foo: 'hi' };
619
620 /*
621 The anon_obj variable doesn't have a type declared, and is an
622 anonymous object according to the compiler. However, remember back at
623 the top where we declared the FooObj typedef? Since anon_obj matches
624 that structure, we can use it anywhere that a "FooObject" type is
625 expected.
626 */
627 var f = function(fo:FooObject) {
628 trace('$fo was passed in to this function');
629 }
630 f(anon_obj); // call the FooObject signature function with anon_obj.
631
632 /*
633 Note that typedefs can have optional fields as well, marked with "?"
634
635 typedef OptionalFooObj = {
636 ?optionalString: String,
637 requiredInt: Int
638 }
639
640 Typedefs work well with conditional compilation. For instance,
641 we could have included this at the top of the file:
642
643#if( js )
644 typedef Surface = js.html.CanvasRenderingContext2D;
645#elseif( nme )
646 typedef Surface = nme.display.Graphics;
647#elseif( !flash9 )
648 typedef Surface = flash8.MovieClip;
649#elseif( java )
650 typedef Surface = java.awt.geom.GeneralPath;
651#end
652
653 That would give us a single "Surface" type to work with across
654 all of those platforms.
655 */
656 }
657}
658
659class UsingExample {
660 public static function example() {
661 /*
662 The "using" import keyword is a special type of class import that
663 alters the behavior of any static methods in the class.
664
665 In this file, we've applied "using" to "StringTools", which contains
666 a number of static methods for dealing with String types.
667 */
668 trace(StringTools.endsWith("foobar", "bar") + " should be true!");
669
670 /*
671 With a "using" import, the first argument type is extended with the
672 method. What does that mean? Well, since "endsWith" has a first
673 argument type of "String", that means all String types now have the
674 "endsWith" method:
675 */
676 trace("foobar".endsWith("bar") + " should be true!");
677
678 /*
679 This technique enables a good deal of expression for certain types,
680 while limiting the scope of modifications to a single file.
681
682 Note that the String instance is *not* modified in the run time.
683 The newly attached method is not really part of the attached
684 instance, and the compiler still generates code equivalent to a
685 static method.
686 */
687 }
688}
We’re still only scratching the surface here of what Haxe can do. For a formal overview of all Haxe features, see the manual and the API docs. For a comprehensive directory of available third-party Haxe libraries, see Haxelib.
For more advanced topics, consider checking out:
Finally, please join us on the Haxe forum, on IRC #haxe on freenode, or on the Haxe Gitter chat.