mirror of
https://github.com/adambard/learnxinyminutes-docs.git
synced 2025-08-01 04:20:39 +02:00
Merge pull request #275 from jdonaldson/master
More updates to the haxe guide
This commit is contained in:
@@ -16,40 +16,62 @@ references.
|
||||
Welcome to Learn Haxe 3 in 15 minutes. http://www.haxe.org
|
||||
This is an executable tutorial. You can compile and run it using the haxe
|
||||
compiler, while in the same directory as LearnHaxe.hx:
|
||||
haxe -main LearnHaxe3 -x out
|
||||
$> haxe -main LearnHaxe3 -x out
|
||||
|
||||
Look for the slash-star marks surrounding these paragraphs. We are inside
|
||||
a "Multiline comment". We can leave some notes here that will get ignored
|
||||
by the compiler.
|
||||
|
||||
Multiline comments are also used to generate javadoc-style documentation for
|
||||
haxedoc. They will be used for haxedoc if they immediately precede a class,
|
||||
class function, or class variable.
|
||||
|
||||
*/
|
||||
|
||||
// Let's start with comments... this is a single line comment
|
||||
// Double slashes like this will give a single-line comment
|
||||
|
||||
|
||||
/*
|
||||
And this is multiline. Multiline comments are also used to generate
|
||||
javadoc-style documentation for haxedoc. They will be used if they precede
|
||||
a class, class function, or class variable.
|
||||
*/
|
||||
|
||||
/*
|
||||
A package declaration isn't necessary, but it's useful if you want to
|
||||
organize your code into modules later on. Also worth mentioning, all
|
||||
expressions in Haxe must end in a semicolon:
|
||||
This is your first actual haxe code coming up, it's declaring an empty
|
||||
package. A package isn't necessary, but it's useful if you want to create a
|
||||
namespace for your code (e.g. org.module.ClassName).
|
||||
*/
|
||||
package; // empty package, no namespace.
|
||||
|
||||
/*
|
||||
Packages define modules for your code. Each module (e.g. org.module) must
|
||||
be lower case, and should exist as a folder structure containing the class.
|
||||
Class (and type) names must be capitalized. E.g, the class "org.module.Foo"
|
||||
should have the folder structure org/module/Foo.hx, as accessible from the
|
||||
compiler's working directory or class path.
|
||||
|
||||
// if you import code from other files, it must be declared before the rest of
|
||||
// the code.
|
||||
If you import code from other files, it must be declared before the rest of
|
||||
the code. Haxe provides a lot of common default classes to get you started:
|
||||
*/
|
||||
import haxe.ds.ArraySort;
|
||||
|
||||
// you can import many classes/modules at once with "*"
|
||||
import haxe.ds.*;
|
||||
|
||||
// you can also import classes in a special way, enabling them to extend the
|
||||
// functionality of other classes. More on this later.
|
||||
/*
|
||||
You can also import classes in a special way, enabling them to extend the
|
||||
functionality of other classes like a "mixin". More on 'using' later.
|
||||
*/
|
||||
using StringTools;
|
||||
|
||||
// Haxe files typically define classes, although they can also define other
|
||||
// types of code... more on that later.
|
||||
/*
|
||||
Typedefs are like variables... for types. They must be declared before any
|
||||
code. More on this later.
|
||||
*/
|
||||
typedef FooString = String;
|
||||
|
||||
// Typedefs can also reference "structural" types, more on that later as well.
|
||||
typedef FooObject = { foo: String };
|
||||
|
||||
/*
|
||||
Here's the class definition. It's the main class for the file, since it has
|
||||
the same name (LearnHaxe3).
|
||||
*/
|
||||
class LearnHaxe3{
|
||||
/*
|
||||
If you want certain code to run automatically, you need to put it in
|
||||
@@ -58,6 +80,7 @@ class LearnHaxe3{
|
||||
arguments above.
|
||||
*/
|
||||
static function main(){
|
||||
|
||||
/*
|
||||
Trace is the default method of printing haxe expressions to the
|
||||
screen. Different targets will have different methods of
|
||||
@@ -67,8 +90,6 @@ class LearnHaxe3{
|
||||
Finally, It's possible to prevent traces from showing by using the
|
||||
"--no-traces" argument on the compiler.
|
||||
*/
|
||||
|
||||
|
||||
trace("Hello World, with trace()!");
|
||||
|
||||
/*
|
||||
@@ -76,16 +97,11 @@ class LearnHaxe3{
|
||||
a representation of the expression as best it can. You can also
|
||||
concatenate strings with the "+" operator:
|
||||
*/
|
||||
trace(
|
||||
" Integer: " + 10 +
|
||||
" Float: " + 3.14 +
|
||||
" Boolean: " + true
|
||||
);
|
||||
|
||||
trace( " Integer: " + 10 + " Float: " + 3.14 + " Boolean: " + true);
|
||||
|
||||
/*
|
||||
Remember what I said about expressions needing semicolons? You
|
||||
can put more than one expression on a line if you want.
|
||||
In Haxe, it's required to separate expressions in the same block with
|
||||
semicolons. But, you can put two expressions on one line:
|
||||
*/
|
||||
trace('two expressions..'); trace('one line');
|
||||
|
||||
@@ -99,7 +115,6 @@ class LearnHaxe3{
|
||||
You can save values and references to data structures using the
|
||||
"var" keyword:
|
||||
*/
|
||||
|
||||
var an_integer:Int = 1;
|
||||
trace(an_integer + " is the value for an_integer");
|
||||
|
||||
@@ -111,7 +126,6 @@ class LearnHaxe3{
|
||||
the haxe compiler is inferring that the type of another_integer
|
||||
should be "Int".
|
||||
*/
|
||||
|
||||
var another_integer = 2;
|
||||
trace(another_integer + " is the value for another_integer");
|
||||
|
||||
@@ -137,8 +151,14 @@ class LearnHaxe3{
|
||||
var a_string = "some" + 'string'; // strings can have double or single quotes
|
||||
trace(a_string + " is the value for a_string");
|
||||
|
||||
/*
|
||||
Strings can be "interpolated" by inserting variables into specific
|
||||
positions. The string must be single quoted, and the variable must
|
||||
be preceded with "$". Expressions can be enclosed in ${...}.
|
||||
*/
|
||||
var x = 1;
|
||||
var an_interpolated_string = 'the value of x is $x';
|
||||
var another_interpolated_string = 'the value of x + 1 is ${x + 1}';
|
||||
|
||||
/*
|
||||
Strings are immutable, instance methods will return a copy of
|
||||
@@ -148,6 +168,12 @@ class LearnHaxe3{
|
||||
var a_sub_string = a_string.substr(0,4);
|
||||
trace(a_sub_string + " is the value for a_sub_string");
|
||||
|
||||
/*
|
||||
Regexes are also supported, but there's not enough space to go into
|
||||
much detail.
|
||||
*/
|
||||
trace((~/foobar/.match('foo')) + " is the value for (~/foobar/.match('foo')))");
|
||||
|
||||
/*
|
||||
Arrays are zero-indexed, dynamic, and mutable. Missing values are
|
||||
defined as null.
|
||||
@@ -191,7 +217,7 @@ class LearnHaxe3{
|
||||
trace(m3 + " is the value for m3");
|
||||
|
||||
/*
|
||||
Haxe has many more common datastructures in the haxe.ds module, such as
|
||||
Haxe has some more common datastructures in the haxe.ds module, such as
|
||||
List, Stack, and BalancedTree
|
||||
*/
|
||||
|
||||
@@ -199,7 +225,6 @@ class LearnHaxe3{
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Operators
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
trace("***OPERATORS***");
|
||||
|
||||
// basic arithmetic
|
||||
@@ -218,7 +243,7 @@ class LearnHaxe3{
|
||||
trace((3 >= 2) + " is the value for 3 >= 2");
|
||||
trace((3 <= 2) + " is the value for 3 <= 2");
|
||||
|
||||
//bitwise operators
|
||||
// standard bitwise operators
|
||||
/*
|
||||
~ Unary bitwise complement
|
||||
<< Signed left shift
|
||||
@@ -252,6 +277,27 @@ class LearnHaxe3{
|
||||
trace("also not printed.");
|
||||
}
|
||||
|
||||
// there is also a "ternary" if:
|
||||
(j == 10) ? trace("equals 10") : trace("not equals 10");
|
||||
|
||||
/*
|
||||
Finally, there is another form of control structures that operates
|
||||
at compile time: conditional compilation.
|
||||
*/
|
||||
#if neko
|
||||
trace('hello from neko');
|
||||
#elseif js
|
||||
trace('hello from js');
|
||||
#else
|
||||
trace('hello from another platform!');
|
||||
#end
|
||||
/*
|
||||
The compiled code will change depending on the platform target.
|
||||
Since we're compiling for neko (-x or -neko), we only get the neko
|
||||
greeting.
|
||||
*/
|
||||
|
||||
|
||||
trace("Looping and Iteration");
|
||||
|
||||
// while loop
|
||||
@@ -310,13 +356,14 @@ class LearnHaxe3{
|
||||
generalized algebraic data types in enums (more on enums later).
|
||||
Here's some basic value examples for now:
|
||||
*/
|
||||
var my_dog_name = 'fido';
|
||||
var favorite_thing = '';
|
||||
var my_dog_name = "fido";
|
||||
var favorite_thing = "";
|
||||
switch(my_dog_name){
|
||||
case "fido" : favorite_thing = 'bone';
|
||||
case "rex" : favorite_thing = 'shoe';
|
||||
case "spot" : favorite_thing = 'tennis ball';
|
||||
case _ : favorite_thing = 'some unknown treat';
|
||||
case "fido" : favorite_thing = "bone";
|
||||
case "rex" : favorite_thing = "shoe";
|
||||
case "spot" : favorite_thing = "tennis ball";
|
||||
default : favorite_thing = "some unknown treat";
|
||||
// case _ : "some unknown treat"; // same as default
|
||||
}
|
||||
// The "_" case above is a "wildcard" value
|
||||
// that will match anything.
|
||||
@@ -345,10 +392,10 @@ class LearnHaxe3{
|
||||
trace("K equals ", k); // outputs 10
|
||||
|
||||
var other_favorite_thing = switch(my_dog_name) {
|
||||
case "fido" : 'teddy';
|
||||
case "rex" : 'stick';
|
||||
case "spot" : 'football';
|
||||
case _ : 'some unknown treat';
|
||||
case "fido" : "teddy";
|
||||
case "rex" : "stick";
|
||||
case "spot" : "football";
|
||||
default : "some unknown treat";
|
||||
}
|
||||
|
||||
trace("My dog's name is" + my_dog_name
|
||||
@@ -358,6 +405,7 @@ class LearnHaxe3{
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Converting Value Types
|
||||
//////////////////////////////////////////////////////////////////
|
||||
trace("***CONVERTING VALUE TYPES***");
|
||||
|
||||
// You can convert strings to ints fairly easily.
|
||||
|
||||
@@ -372,33 +420,93 @@ class LearnHaxe3{
|
||||
true + ""; // returns "true";
|
||||
// See documentation for parsing in Std for more details.
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Dealing with Types
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
|
||||
As mentioned before, Haxe is a statically typed language. All in
|
||||
all, static typing is a wonderful thing. It enables
|
||||
precise autocompletions, and can be used to thoroughly check the
|
||||
correctness of a program. Plus, the Haxe compiler is super fast.
|
||||
|
||||
*HOWEVER*, there are times when you just wish the compiler would let
|
||||
something slide, and not throw a type error in a given case.
|
||||
|
||||
To do this, Haxe has two separate keywords. The first is the
|
||||
"Dynamic" type:
|
||||
*/
|
||||
var dyn: Dynamic = "any type of variable, such as this string";
|
||||
|
||||
/*
|
||||
All that you know for certain with a Dynamic variable is that the
|
||||
compiler will no longer worry about what type it is. It is like a
|
||||
wildcard variable: You can pass it instead of any variable type,
|
||||
and you can assign any variable type you want.
|
||||
|
||||
The other more extreme option is the "untyped" keyword:
|
||||
*/
|
||||
|
||||
untyped {
|
||||
var x:Int = 'foo'; // this can't be right!
|
||||
var y:String = 4; // madness!
|
||||
}
|
||||
|
||||
/*
|
||||
The untyped keyword operates on entire *blocks* of code, skipping
|
||||
any type checks that might be otherwise required. This keyword should
|
||||
be used very sparingly, such as in limited conditionally-compiled
|
||||
situations where type checking is a hinderance.
|
||||
|
||||
In general, skipping type checks is *not* recommended. Use the
|
||||
enum, inheritance, or structural type models in order to help ensure
|
||||
the correctness of your program. Only when you're certain that none
|
||||
of the type models work should you resort to "Dynamic" or "untyped".
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Basic Object Oriented Programming
|
||||
//////////////////////////////////////////////////////////////////
|
||||
trace("***BASIC OBJECT ORIENTED PROGRAMMING***");
|
||||
|
||||
|
||||
// create an instance of FooClass. The classes for this are at the
|
||||
// end of the file.
|
||||
var instance = new FooClass(3);
|
||||
/*
|
||||
Create an instance of FooClass. The classes for this are at the
|
||||
end of the file.
|
||||
*/
|
||||
var foo_instance = new FooClass(3);
|
||||
|
||||
// read the public variable normally
|
||||
trace(instance.public_any + " is the value for instance.public_any");
|
||||
trace(foo_instance.public_any + " is the value for foo_instance.public_any");
|
||||
|
||||
// we can read this variable
|
||||
trace(instance.public_read + " is the value for instance.public_read");
|
||||
trace(foo_instance.public_read + " is the value for foo_instance.public_read");
|
||||
// but not write it
|
||||
// instance.public_write = 4; // this will throw an error if uncommented:
|
||||
// trace(instance.public_write); // as will this.
|
||||
// foo_instance.public_write = 4; // this will throw an error if uncommented:
|
||||
// trace(foo_instance.public_write); // as will this.
|
||||
|
||||
trace(instance + " is the value for instance"); // calls the toString method
|
||||
trace(instance.toString() + " is the value for instance.toString()"); // same thing
|
||||
trace(foo_instance + " is the value for foo_instance"); // calls the toString method
|
||||
trace(foo_instance.toString() + " is the value for foo_instance.toString()"); // same thing
|
||||
|
||||
|
||||
// instance has the "FooClass" type, while acceptBaseFoo has the
|
||||
// BaseFooClass type. However, since FooClass extends BaseFooClass,
|
||||
// it is accepted.
|
||||
BaseFooClass.acceptBaseFoo(instance);
|
||||
/*
|
||||
The foo_instance has the "FooClass" type, while acceptBarInstance
|
||||
has the BarClass type. However, since FooClass extends BarClass, it
|
||||
is accepted.
|
||||
*/
|
||||
BarClass.acceptBarInstance(foo_instance);
|
||||
|
||||
/*
|
||||
The classes below have some more advanced examples, the "example()"
|
||||
method will just run them here.
|
||||
*/
|
||||
SimpleEnumTest.example();
|
||||
ComplexEnumTest.example();
|
||||
TypedefsAndStructuralTypes.example();
|
||||
UsingExample.example();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -406,7 +514,7 @@ class LearnHaxe3{
|
||||
/*
|
||||
This is the "child class" of the main LearnHaxe3 Class
|
||||
*/
|
||||
class FooClass extends BaseFooClass implements BaseFooInterface{
|
||||
class FooClass extends BarClass implements BarInterface{
|
||||
public var public_any:Int; // public variables are accessible anywhere
|
||||
public var public_read (default,null): Int; // use this style to only enable public read
|
||||
public var public_write (null, default): Int; // or public write
|
||||
@@ -418,7 +526,7 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
|
||||
|
||||
// a public constructor
|
||||
public function new(arg:Int){
|
||||
super(); // call the constructor of the parent object, since we extended BaseFooClass
|
||||
super(); // call the constructor of the parent object, since we extended BarClass
|
||||
|
||||
this.public_any= 0;
|
||||
this._private = arg;
|
||||
@@ -442,7 +550,7 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
|
||||
}
|
||||
|
||||
// this class needs to have this function defined, since it implements
|
||||
// the BaseFooInterface interface.
|
||||
// the BarInterface interface.
|
||||
public function baseFunction(x: Int) : String{
|
||||
// convert the int to string automatically
|
||||
return x + " was passed into baseFunction!";
|
||||
@@ -452,21 +560,217 @@ class FooClass extends BaseFooClass implements BaseFooInterface{
|
||||
/*
|
||||
A simple class to extend
|
||||
*/
|
||||
class BaseFooClass {
|
||||
class BarClass {
|
||||
var base_variable:Int;
|
||||
public function new(){
|
||||
base_variable = 4;
|
||||
}
|
||||
public static function acceptBaseFoo(b:BaseFooClass){
|
||||
public static function acceptBarInstance(b:BarClass){
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
A simple interface to implement
|
||||
*/
|
||||
interface BaseFooInterface{
|
||||
interface BarInterface{
|
||||
public function baseFunction(x:Int):String;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// Enums and Switch Statements
|
||||
//////////////////////////////////////////////////////////////////
|
||||
|
||||
/*
|
||||
Enums in Haxe are very powerful. In their simplest form, enums
|
||||
are a type with a limited number of states:
|
||||
*/
|
||||
|
||||
enum SimpleEnum {
|
||||
Foo;
|
||||
Bar;
|
||||
Baz;
|
||||
}
|
||||
|
||||
// Here's a class that uses it:
|
||||
|
||||
class SimpleEnumTest{
|
||||
public static function example(){
|
||||
var e_explicit:SimpleEnum = SimpleEnum.Foo; // you can specify the "full" name
|
||||
var e = Foo; // but inference will work as well.
|
||||
switch(e){
|
||||
case Foo: trace("e was Foo");
|
||||
case Bar: trace("e was Bar");
|
||||
case Baz: trace("e was Baz"); // comment this line to throw an error.
|
||||
}
|
||||
|
||||
/*
|
||||
This doesn't seem so different from simple value switches on strings.
|
||||
However, if we don't include *all* of the states, the compiler will
|
||||
complain. You can try it by commenting out a line above.
|
||||
|
||||
You can also specify a default for enum switches as well:
|
||||
*/
|
||||
switch(e){
|
||||
case Foo: trace("e was Foo again");
|
||||
default : trace("default works here too");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Enums go much further than simple states, we can also enumerate
|
||||
*constructors*, but we'll need a more complex enum example
|
||||
*/
|
||||
enum ComplexEnum{
|
||||
IntEnum(i:Int);
|
||||
MultiEnum(i:Int, j:String, k:Float);
|
||||
SimpleEnumEnum(s:SimpleEnum);
|
||||
ComplexEnumEnum(c:ComplexEnum);
|
||||
}
|
||||
// Note: The enum above can include *other* enums as well, including itself!
|
||||
|
||||
class ComplexEnumTest{
|
||||
public static function example(){
|
||||
var e1:ComplexEnum = IntEnum(4); // specifying the enum parameter
|
||||
/*
|
||||
Now we can switch on the enum, as well as extract any parameters
|
||||
it might of had.
|
||||
*/
|
||||
switch(e1){
|
||||
case IntEnum(x) : trace('$x was the parameter passed to e1');
|
||||
default: trace("Shouldn't be printed");
|
||||
}
|
||||
|
||||
// another parameter here that is itself an enum... an enum enum?
|
||||
var e2 = SimpleEnumEnum(Foo);
|
||||
switch(e2){
|
||||
case SimpleEnumEnum(s): trace('$s was the parameter passed to e2');
|
||||
default: trace("Shouldn't be printed");
|
||||
}
|
||||
|
||||
// enums all the way down
|
||||
var e3 = ComplexEnumEnum(ComplexEnumEnum(MultiEnum(4, 'hi', 4.3)));
|
||||
switch(e3){
|
||||
// You can look for certain nested enums by specifying them explicitly:
|
||||
case ComplexEnumEnum(ComplexEnumEnum(MultiEnum(i,j,k))) : {
|
||||
trace('$i, $j, and $k were passed into this nested monster');
|
||||
}
|
||||
default: trace("Shouldn't be printed");
|
||||
}
|
||||
/*
|
||||
Check out "generalized algebraic data types" (GADT) for more details
|
||||
on why these are so great.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
class TypedefsAndStructuralTypes {
|
||||
public static function example(){
|
||||
/*
|
||||
Here we're going to use typedef types, instead of base types.
|
||||
At the top we've declared the type "FooString" to mean a "String" type.
|
||||
*/
|
||||
var t1:FooString = "some string";
|
||||
|
||||
/*
|
||||
We can use typedefs for "structural types" as well. These types are
|
||||
defined by their field structure, not by class inheritance. Here's
|
||||
an anonymous object with a String field named "foo":
|
||||
*/
|
||||
|
||||
var anon_obj = { foo: 'hi' };
|
||||
|
||||
/*
|
||||
The anon_obj variable doesn't have a type declared, and is an
|
||||
anonymous object according to the compiler. However, remember back at
|
||||
the top where we declared the FooObj typedef? Since anon_obj matches
|
||||
that structure, we can use it anywhere that a "FooObject" type is
|
||||
expected.
|
||||
*/
|
||||
|
||||
var f = function(fo:FooObject){
|
||||
trace('$fo was passed in to this function');
|
||||
}
|
||||
f(anon_obj); // call the FooObject signature function with anon_obj.
|
||||
|
||||
/*
|
||||
Note that typedefs can have optional fields as well, marked with "?"
|
||||
|
||||
typedef OptionalFooObj = {
|
||||
?optionalString: String,
|
||||
requiredInt: Int
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
Typedefs work well with conditional compilation. For instance,
|
||||
we could have included this at the top of the file:
|
||||
|
||||
#if( js )
|
||||
typedef Surface = js.html.CanvasRenderingContext2D;
|
||||
#elseif( nme )
|
||||
typedef Surface = nme.display.Graphics;
|
||||
#elseif( !flash9 )
|
||||
typedef Surface = flash8.MovieClip;
|
||||
#elseif( java )
|
||||
typedef Surface = java.awt.geom.GeneralPath;
|
||||
#end
|
||||
|
||||
That would give us a single "Surface" type to work with across
|
||||
all of those platforms.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
class UsingExample {
|
||||
public static function example() {
|
||||
|
||||
/*
|
||||
The "using" import keyword is a special type of class import that
|
||||
alters the behavior of any static methods in the class.
|
||||
|
||||
In this file, we've applied "using" to "StringTools", which contains
|
||||
a number of static methods for dealing with String types.
|
||||
*/
|
||||
trace(StringTools.endsWith("foobar", "bar") + " should be true!");
|
||||
|
||||
/*
|
||||
With a "using" import, the first argument type is extended with the
|
||||
method. What does that mean? Well, since "endsWith" has a first
|
||||
argument type of "String", that means all String types now have the
|
||||
"endsWith" method:
|
||||
*/
|
||||
trace("foobar".endsWith("bar") + " should be true!");
|
||||
|
||||
/*
|
||||
This technique enables a good deal of expression for certain types,
|
||||
while limiting the scope of modifications to a single file.
|
||||
|
||||
Note that the String instance is *not* modified in the run time.
|
||||
The newly attached method is not really part of the attached
|
||||
instance, and the compiler still generates code equivalent to a
|
||||
static method.
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
We're still only scratching the surface here of what Haxe can do. For a formal
|
||||
overiew of all Haxe features, checkout the [online
|
||||
manual](http://haxe.org/manual), the [online api](http://api.haxe.org/), and
|
||||
"haxelib", the [haxe library repo] (http://lib.haxe.org/).
|
||||
|
||||
For more advanced topics, consider checking out:
|
||||
|
||||
* [Abstract types](http://haxe.org/manual/abstracts)
|
||||
* [Macros](http://haxe.org/manual/macros), and [Compiler Macros](http://haxe.org/manual/macros_compiler)
|
||||
* [Tips and Tricks](http://haxe.org/manual/tips_and_tricks)
|
||||
|
||||
|
||||
Finally, please join us on [the mailing list](https://groups.google.com/forum/#!forum/haxelang), on IRC [#haxe on
|
||||
freenode](http://webchat.freenode.net/), or on
|
||||
[Google+](https://plus.google.com/communities/103302587329918132234).
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user